查找算法总结
只要打開電腦,就會涉及查找技術。如硬盤中文件搜索,瀏覽器中的搜索“關鍵字key”等,無論是數據庫,還是普通的ERP系統,查找功能數據處理的一個基本,同時也是一個非常重要的功能。所有這些需要被查找數據的集合,統稱為“查找表”。數據查找并不復雜,但是如何實現數據又快又好地查找呢?前人在實踐中積累的一些方法,值得我們好好學些一下。我們假定查找的數據唯一存在,數組中沒有重復的數據存在。
我們如何在查找表里面找到我們想要的那個數據。此時數據本身沒有特征,所以我們需要的那個數據可能出現在數組的各個位置,可能在數據的開頭位置,也可能在數據的結束位置。這種性質要求我們必須對數據進行遍歷之后才能獲取到對應的數據。
(1) 普通的順序查找(無序查找)
順序查找(Sequential Search)又叫線型查找,是基本的查找技術。其基本過程是:從查找表中的第一個(或者最后一個)記錄開始,逐個進行給定值與關鍵字的比較,若相等則查找成功;如果直到最后一個(或者第一個)記錄,關鍵字和給定值不等,則查找不成功.
[cpp] view plain copy int find(int array[], int length, int value) { if(NULL == array || 0 == length) return -1; for(int index = 0; index < length; index++){ if(value == array[index]) return index; } return -1; }由于每次循環時都要對index是否越界,即是否小于等于length做判斷,因此我們可以通過設置哨兵進行改進。
int find(int array[], int length, int value) { if(NULL == array || 0 == length) return -1; int i = length;array[0] = value; //注意此時數組存儲在array[1]到array[length]中while(array[i]!= key)//循環從尾部開始i--;return i; //返回0則說明查找失敗 }此時代碼從尾部開始查找,由于array[0] = value,也就是說如果在array[i]有key則返回i值,查找成功,否則一定在最終的array[0]處等于value,此時返回的是0,即說明array[1]-array[length]中沒有關鍵字value,查找失敗。
分析:對于順序查找而言,最好的情況是要查找的值在第一個位置就找到了,算法時間復雜度為O(1),最壞的情況是遍歷所有元素,需要n次,時間復雜度為O(n),由于關鍵字出現在任何一個位置上的概率是相等的,因此平均查找次數為(n+1)/2,所以時間復雜度是O(n)。當n很大時,查找效率極為低下,由于這個算法簡單,對靜態查找表的記錄沒有任何要求,在一些小型數據的查找時,可以使用。
(2)有序表查找
如果數據排列地非常整齊,那結果會是什么樣的呢?就像在生活中,如果平時 不注意收拾整齊,那么找東西的時候非常麻煩,效率很低;但是一旦東西放的位置固定下來,所有東西都歸類放好,那么結果就不一樣了,我們就會形成思維定勢,這樣查找東西的效率就會非常高。下面我們介紹幾種方法:
二分法查找: int binary_searcht(int array[], int length, int value) { if(NULL == array || 0 == length) return -1; int start = 0; int end = length -1; while(start <= end){ int middle = start + ((end - start) >> 1); if(value == array[middle]) return middle; else if(value > array[middle]){ start = middle + 1; }else{ end = middle -1; } } return -1; }分析::查找最好情況是1次,最壞情況是需要log(n+1)/log(2),即時間復雜度為O(logn),理論證明見《算法導論》.
其思想與折半查找較為相似,根據要查找的關鍵字value與查找表中最大最小記錄的關鍵字比較后的查找方法,由于是在有序表中查找,例如是在升序的表中查找,對于一個較小的值我們自然想到在查找表前面部分進行查找,因此我們將上述middle語句更改為:
middle = start+(end - start)*(value-array[start])/(array[end] - array[start]);即可,這里不再贅述。
相對于折半查找,一般將待比較的value值與第mid=(low+high)/2位置的元素比較,比較結果分三種情況
1)相等,mid位置的元素即為所求
2)> ,low=mid+1;
3) < ,high=mid-1;
斐波那契查找與折半查找很相似,他是根據斐波那契序列的特點對有序表進行分割的。他要求開始表中記錄的個數為某個斐波那契數小1,即
n=F(m)-1;開始將k值與第F(k-1)位置的記錄進行比較(mid=low+F(k-1)-1),比較結果也分為三種:
1)相等,mid位置的元素即為所求
2)> ,low=mid+1,k-=2;
(說明:low=mid+1說明待查找的元素在[mid+1,hign]范圍內,k-=2 說明范圍[mid+1,high]內的元素個數為n-(F(k-1)) = Fk-1-F(k-1) =Fk-F(k-1)-1=F(k-2)-1個,所以可以遞歸的應用斐波那契查找)
3)< ,high=mid-1,k-=1;
說明:low=mid+1說明待查找的元素在[low,mid-1]范圍內,k-=1 說明范圍[low,mid-1]內的元素個數為F(k-1)-1 個,所以可以遞歸 的應用斐波那契查找
斐波那契查找的核心是:
1)當key=a[mid]時,查找成功;
2)當key
分析:關于斐波那契查找, 如果要查找的記錄在右側,則左側的數據都不用再判斷了,不斷反復進行下去,對處于當眾的大部分數據,其工作效率要高一些。所以盡管斐波那契查找的時間復雜度也為O(logn),但就平均性能來說,斐波那契查找要優于折半查找。可惜如果是最壞的情況,比如這里key=1,那么始終都處于左側在查找,則查找效率低于折半查找。
還有關鍵一點,折半查找是進行加法與除法運算的(mid=(low+high)/2),插值查找則進行更復雜的四則運算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low]))),而斐波那契查找只進行最簡單的加減法運算(mid = low + F[k-1] - 1),在海量數據的查找過程中,這種細微的差別可能會影響最終的效率。
(3)二叉搜索樹
二叉搜索樹或者是一棵空樹或者有下列性質:
1.若左子樹不為空,則左子樹上的所有結點的值均小于根節點的值
2.若右子樹不為空,則右子樹上的所有結點的值均大于根節點的值
3.它的左右子樹也分別為二叉搜索樹
分析:若果二叉樹是比較平衡的,即其深度與完全二叉樹相同,那么查找的時間復雜度為O(logn),近似于折半查找,最壞的情況下是斜樹,查找時間復雜度是O(n),對于二叉搜索樹的其它諸如插入(insert),刪除(delete)操作,在其它博客中有,可自行查找。
另外還有AVL樹,多路查找樹(B樹),以及散列表查找(哈希表)另有單獨博客講解。
總結
- 上一篇: 九大排序算法总结
- 下一篇: 第一个出现一次的字符