書名:《圖說演算法-使用C#》
作者:吳燦銘、胡昭民
所讀版本:博碩文化
搜尋方法
基於資料量大小:
資料量大:外部搜尋
資料量小:內部搜尋
基於被搜尋的數據是否異動:
否:靜態搜尋
是:動態搜尋
搜尋算法
線性搜尋法
從頭到尾搜索全部數據
優點:搜索前不對資料作任何處理
缺點:搜尋速度較慢
時間複雜度:
如果數據沒有重覆,找到數據可中止搜索,最差情況為O(n)
平均情況,數據出現機率相等,需進行(n+1)/2次比較
數據量很大是不適合用線性搜尋
線性搜尋
public static bool SequentialSearch(int[] arr, int target)
{
foreach (int item in arr)
{
if(item == target) { return true; }
}
return false;
}
二分搜尋法
搜尋對象需要事先排序好,而且數據量必須能直接在記憶體中執行。
適用於靜態數據
將數據分割成兩等份,比較目標值和中間值的大小:
目標值>中間值:目標值在右邊
目標值<中間值:目標值在左邊
目標值==中間值:找到目標值
時間複雜度:每次搜尋都會比上次少一半的範圍 -> O(log n)
二分查找
public static int BinarySearch(int[] arr, int target)
{
int left = 0;
int right = arr.Length - 1;
int mid = 0;
while(left <= right)
{
mid = (right - left) / 2 + left;
if(arr[mid] == target) { return mid; }
else if(arr[mid] < target)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return -1;
}
內插搜尋法
依照數據位置的分布,利用公式預測數據的所在位置,再以二分法的方式漸漸逼近。
內插法假設數據平均分布在陣列中,而每一筆數據的差距相當接近/有一定比例的距離
內插法公式:mid = left + ((target – arr[left]) / (arr[right] – arr[left])) * (right – left)
時間複雜度:平均而言優於O(log n)
須先排序數據
內插搜尋
public static int InterpolationSearch(int[] arr, int target)
{
int left = 0;
int right = arr.Length - 1;
int interpolate = 0;
while(left <= right && interpolate != -1)
{
interpolate = (int)((float)(target - arr[left]) * (right - left) / (arr[right] - arr[left]));
int mid = left + interpolate;
if(mid > right + 1 || mid < -1) { return -1; }
else if(interpolate > arr[left] && interpolate > arr[right]) { return -1; }
if(arr[mid] == target)
{
return mid;
}
else if (arr[mid] < target)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return -1;
}
費氏搜尋法
以費氏級數分割數據,比二分法好的是只用到加減法而不需用到乘除法
費氏搜尋樹
左右子樹均為費氏樹
根節點(k)的費氏級數基於數據數量(n)決定:F(k + 1) >= n+1
子節點與父節點的差值絕對值為費氏數
當k >= 2
費氏樹的樹根為Fib(k)
左子樹為(k – 1)階費氏樹(樹根為Fib(k – 1))
右子樹為數(k – 2)階費氏樹(樹根為Fib(k) + Fib(k – 2))
若n + 1 != 費氏數的值,則找出存在一個m 使用 Fib(k + 1) – m = n + 1,m = Fib(k + 1) – (n + 1),再依費氏樹的建立原則完成費氏樹的建立,最後將樹的各節點減去m,把值<1的節點去掉。
target取值範圍
target < Fib(k) => target在1 到 Fib(k) – 1之間
target == Fib(k) => 找到數據
target > Fib(k) => target在 Fib(k) + 1 到 Fib(k+1) – 1之間
時間複雜度:
平均情況:O(log2N)
最壞情況比二分查找慢
算法複雜,需額外產生費氏樹
費氏搜尋
public static int Fib_Search(int[] arr, int target)
{
//F(根節點 + 1) = 數組元素數量 + 1
int idx = 2;
while (Fib(idx) <= arr.Length) { idx++; }
idx--;
int rootNode = Fib(idx); //定義根節點
int diff1 = Fib(idx - 1); //上一個費氏數
int diff2 = rootNode - diff1; //上二個費氏數
rootNode--;
while (true)
{
if (target == arr[rootNode]) { return rootNode; }
else
{
if (idx == 2) { return arr.Length; }
if (target < arr[rootNode])
{
rootNode = rootNode - diff2;
int temp = diff1;
diff1 = diff2;
diff2 = temp - diff2;
idx = idx - 1;
}
else
{
if (idx == 3) { return arr.Length; }
rootNode = rootNode + diff2;
diff1 = diff1 - diff2;
diff2 = diff2 - diff1;
idx = idx - 2;
}
}
}
}
static int Fib(int n)
{
if (n == 1 || n == 0) { return n; }
else { return Fib(n - 1) + Fib(n - 2); }
}