有序表查找要求咱們的數據是有序的,是排序好的,咱們只須要進行查找便可
二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。可是,折半查找要求線性表必須採用順序存儲結構,並且表中元素按關鍵字有序排列。
首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查找關鍵字比較,若是二者相等,則查找成功;
不然利用中間位置記錄將表分紅前、後兩個子表,若是中間位置記錄的關鍵字大於查找關鍵字,則進一步查找前一子表,不然進一步查找後一子表。
重複以上過程,直到找到知足條件的記錄,使查找成功,或直到子表不存在爲止,此時查找不成功。
int Binary_Search(int *a, int n, int key) { int low, high, mid; low = 0; high = n - 1; while (low<=high) { mid = (low + high) / 2; if (a[mid] < key) low = mid + 1; else if (a[mid]>key) high = mid - 1; else return mid; } return -1; } int main() { int a[10] = { 1, 6, 12, 21, 30, 31, 32, 42, 49, 52 }; int index; index=Binary_Search(a, 10, 49); if (index != -1) printf("find key in %d\n", index); else printf("not find key\n"); system("pause"); return 0; }
其時間複雜度是O(logn),不過因爲折半查找的前提是須要有序表順序存儲,對於靜態查找表,一次排序後再也不變化,這樣的算法是比較好的。可是對於須要頻繁插入或刪除操做的數據集來講,維護有序的排序會帶來不小的工做量,不建議使用。
對於前面的折半查找,爲啥必定要折通常,而不是1/4或者其餘? 好比:咱們查字典Apple,咱們會先從中間查找,仍是有必定目的的向前找。
或者在0-1000個數之間有200個元素從小到大均勻分佈排序,咱們如果須要查找到15,咱們會去中間查找500,仍是直接去前面開始查找。 以上都說明咱們上面的折半查找能夠再進行改進
折半查找這種查找方式,不是自適應的(也就是說是傻瓜式的),其前面的查找係數(比例參數)始終是1/2
也就是將上述的比例參數1/2改進爲自適應的,根據關鍵字在整個有序表中所處的位置,讓mid值的變化更靠近關鍵字key,這樣也就間接地減小了比較次數。
基於二分查找算法,將查找點的選擇改進爲自適應選擇,能夠提升查找效率。固然,插值查找也屬於有序查找。
int Insert_Search(int *a, int n, int key) { int low, high, mid; low = 0; high = n - 1; while (low <= high) { mid = low + (key - a[low]) / (a[high] - a[low])*(high - low); if (a[mid] < key) low = mid + 1; else if (a[mid]>key) high = mid - 1; else return mid; } return -1; }
實現方法和折半同樣,只是修改了mid
而插值查找則比較靈活,並非簡單的從中間進行的,它是根據咱們須要查詢的值的漸進進行搜索的. 插值查找的不一樣點在於每一次並非從中間切分,而是根據離所求值的距離進行搜索的.
對於表長較大,而關鍵字分佈又比較均勻的查找表來講,插值查找算法的平均性能比折半查找要好的多。反之,數組中若是分佈很是不均勻,那麼插值查找未必是很合適的選擇。
時間複雜度:平均狀況O(loglog(n)),最壞O(log(n))
除了插值查找以外,咱們再介紹一種有序查找,能夠對摺半進行優化,那就是斐波那契查找,利用了黃金分割原理來實現的
斐波那契查找與折半查找很類似,他是根據斐波那契序列的特色對有序表進行分割的。他要求開始表中記錄的個數爲某個斐波那契數小1,即n=F(k)-1;
越向後,每兩個數之間相除越接近黃金比例
例如上面的查找數組大小爲n=10,F[6]<n<F[7],因此得出其位置爲k=7
推文:斐波那契查找原理詳解與實現html
是爲了格式上的統一,以方便遞歸或者循環程序的編寫。
表中的數據是F(k)-1個,使用mid值進行分割又用掉一個,那麼剩下F(k)-2個。
正好分給兩個子序列,每一個子序列的個數分別是F(k-1)-1與F(k-2)-1個,格式上與以前是統一的。
否則的話,每一個子序列的元素個數有多是F(k-1),F(k-1)-1,F(k-2),F(k-2)-1個,寫程序會很是麻煩。
void Fibonacci(int **Fb, int n) { int f1, f2,ft; int count=3; f1 = 1; f2 = 1; while (f2<n) { ft = f1 + f2; f1 = f2; f2 = ft; count++; } (*Fb) = (int *)malloc(count*sizeof(int)); (*Fb)[0] = 0; (*Fb)[1] = 1; for (f1 = 2; f1 <= count - 1; f1++) (*Fb)[f1] = (*Fb)[f1 - 1] + (*Fb)[f1 - 2]; }
int Fibonacci_Search(int *a, int n, int key) { int low, high, mid, i, j,k; int *Fb,*temp; //根據實際狀況來初始化 low = 1; high = n; Fibonacci(&Fb, n); k = 0; while (n > Fb[k]) //計算n位於斐波那契數列位置 k++; temp = (int *)malloc((Fb[k] - 1 + 1)*sizeof(int));//後面加1是由於還有個下標0空間 memcpy(temp, a, n*sizeof(n + 1)); for (i = n + 1; i <= Fb[k] - 1; i++) //咱們要查找的是數組1到F[k]-1,而實際數組長要包括一個下標0空間 temp[i] = a[n]; while (low<=high) { mid = low + Fb[k - 1] - 1; if (key == temp[mid]) { //分狀況,是前半段,正常輸出,後半段判斷是否是在咱們補充的數組裏面,這時咱們返回原數組最後一個下標 if (mid <= n) return mid; else return n; } else if (key>temp[mid]) { low = mid + 1; k = k - 2; } else { high = mid - 1; k = k - 1; } } return 0; //0是未找到,0下標使無心義的 }
int main() { int a[11] = { 0,1,16,24,35,47,59,62,73,88,99}; int index; index = Fibonacci_Search(a, 10, 73); if (index != -1) printf("find key in %d\n", index); else printf("not find key\n"); system("pause"); return 0; }
其時間複雜度也是O(logn),就平均性能來講,斐波那契查找因爲折半查找。可是如果最壞狀況,好比key=1,始終處於左側長半區查找,則效率低於折半查找。