查找定義
:根據給定的某個值,在查找表中肯定一個其關鍵字等於給定值的數據元素(或記錄)。
查找表分類:靜態查找表和動態查找表。
動態查找表:在查找過程當中同時插入查找表中不存在的數據元素,或者從查找表中刪除已經存在的某個數據元素。
1. 順序查找
/* 順序查找,a爲數組,n爲要查找的數組元素個數,key爲要查找的關鍵字*/
int Sequential_Search(int *a, int n, int key)
{
int i = 0;
for(; i < n; i++)
{
if(a[i] == key)
return i;
}
return 0;
}
/*有哨兵順序查找-優化最簡單的順序查找*/
/*優化部分:a[0]存放要查找的關鍵字key,減小了數組越界的比較,若是查找表長度很大,仍是比最簡單的順序查找快不少的。ps:a[0] = key的目的就是讓上述的簡單順序查找的兩次判斷修正爲一次a[i]與a[0]是否相等的一次判斷。*/
int Sequential_Search2(int *a, int n, int key)
{
int i;
a[0] = key;
for(i = n; a[i] != a[0]; i--);
return i;
}
平均查找長度
(Average Search Length,ASL)
需和指定key進行比較的關鍵字的個數的指望值,成爲查找算法在查找成功時的平均查找長度。
對於含有n個數據元素的查找表,查找成功的平均查找長度爲:ASL = Pi*Ci的和。
Pi:查找表中第i個數據元素的機率。
Ci:找到第i個數據元素時已經比較過的次數。
順序查找 查找
成功
時的平均查找長度爲:
(假設每一個數據元素的機率相等) ASL = 1/n(1+2+3+…+n) = (n+1)/2 ;
當查找
不成功
時,須要n+1次比較,時間複雜度爲O(n);
2. 有序表查找
2.1 折半查找
前提:線性表中的記錄必須是關鍵字有序(一般從小到大),線性表必須採用
順序存儲。
基本思想:取中間記錄做爲比較對象,若給定值與中間記錄的關鍵字相等,則查找成功;若給定值小於中間記錄的關鍵字,則在中間記錄左半區繼續查找;不然,在右半區查找。不斷重複,知道查找成功或者查找失敗爲止。
/*折半查找,非遞歸*/
int Binary_Search(int *a, int n, int key)
{
int low, high;
int mid;
low = 1;
high = n;
while(low < high) //能夠有等號 low < = high ,不影響判斷。
{
mid = (low + high) / 2; //能夠修正爲 mid = (low + high) >> 1;
if(a[mid] == key)
return mid;
if(a[mid] > key)
high = mid - 1;
if(a[mid] < key)
low = mid + 1; //對於if else語句,能夠考慮條件表達式,減小代碼的行數。
}
return 0;
}
/*折半查找,遞歸實現*/
int Binary_Search2(int *a, int low, int high, int key)
{
int mid = (low + high) / 2;
if(a[mid] == key)
return mid;
if(a[mid] > key)
return Binary_Search2(a, low, mid-1, key); //有沒有return均可以。
else
return Binary_Search2(a, mid+1, high, key); //有沒有return均可以。
}
/*
ASL: 「具備n個節點的徹底二叉樹的深刻爲[log2n]+1,儘管折半查找斷定二叉樹不是徹底二叉樹,但一樣相同的推導能夠得出,
最壞狀況是查找到關鍵字或查找失敗的次數爲[log2n]+1」,最好狀況是1,因此折半查找的時間複雜度爲o(logN)>o(n).
注:折半查找的前提條件是須要有序表順序存儲,對於靜態查找表,一次排序後再也不變化,這樣的算法已經比較好了。但對於須要
頻繁執行插入或刪除操做的數據集來講,維護有序的排序會帶來不小的工做量,那就不建議使用。——《大話數據結構》
*/
2.2 插值查找
首先考慮一個新問題,爲何必定要是折半,而不是折四分之一或者折更多呢?
打個比方,在英文字典裏面查「apple」,你下意識翻開字典是翻前面的書頁仍是後面的書頁呢?若是再讓你查「zoo」,你又怎麼查?很顯然,這裏你絕對不會是從中間開始查起,而是有必定目的的往前或日後翻。
一樣的,好比要在取值範圍1 ~ 10000 之間 100 個元素從小到大均勻分佈的數組中查找5, 咱們天然會考慮從數組下標較小的開始查找。
通過以上分析,折半查找這種查找方式,仍是有改進空間的,並不必定是折半的!
mid = (low+high)/ 2, 即 mid = low + 1/2 * (high - low);
改進爲下面的計算機方案(不知道具體過程):mid = low + (key - a[low]) / (a[high] - a[low]) * (high - low),也就是將上述的比例參數1/2改進了,根據關鍵字在整個有序表中所處的位置,讓mid值的變化更靠近關鍵字key,這樣也就間接地減小了比較次數。
分析:從時間複雜度上來看,它也是o(n),可是對於表長較大,而關鍵字分佈又比較均勻的查找表來講,插值查找算法的平均性能比折半查找要好的多。反之,數組中若是分佈很是不均勻,那麼差值查找未必是很合適的選擇。
2.3 斐波那契查找
/*斐波那契 查找*/
int Fbonacci_Search(int *a, int n, int key)
{
int low,high,mid,i,k;
int F[] = {0,1,1,2,3,5,8,13,21,34}; //經典的斐波那契數列已經早就定義好,也能夠遞歸本身求解。
low = 1;
high = n;
k = 0;
while(n > F[k] - 1) //計算 n 位於斐波那契數列的位置
k++;
for(i=n; i<F[k] - 1; i++) //將不滿的數值補全
a[i] = a[n];
while(low <= high)
{
mid = low + F[k-1] - 1; //利用斐波那契數列來找尋下一個要比較的關鍵字的位置
if(key < a[mid])
{
high = mid - 1;
k--;
}
else
{
if(key > a[mid])
{
low = mid + 1;
k = k -2;
}
else
{
if(mid <= n)
return mid;
else
return n;
}
}
}
}
總結: