一、查找算法
常見的查找算法大概有順序查找、二分查找、二叉排序樹查找、哈希表法(散列表)、分塊查找等,
下面簡單了解一下其他幾種查找算法。
1.順序查找
也就是暴力方法,按順序比較每個元素,直到找到關鍵字為止。
條件:無序或有序數據,時間復雜度:O(n)
2.二叉排序樹查找
二叉排序樹的性質:
1. 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
2. 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
3. 它的左、右子樹也分別為二叉排序樹。
在二叉查找樹b中查找x的過程為:
1. 若b是空樹,則搜索失敗,否則:
2. 若x等於b的根節點的數據域之值,則查找成功;否則:
3. 若x小於b的根節點的數據域之值,則搜索左子樹;否則:
4. 查找右子樹。
時間復雜度:O(\log_2(n))
3.哈希表查找
創建哈希表(散列表)
哈希查找的操作步驟:⑴用給定的哈希函數構造哈希表⑵根據選擇的沖突處理方法解決地址沖突⑶在哈希表的基礎上執行哈希查找。
建立哈希表操作步驟: ① 取數據元素的關鍵字key,計算其哈希 函數值。若該地址對應的存儲 空間還沒有被占用,則將該元素存入;否則執行step2解決沖突。 ② 根據選擇的沖突處理方法,計算關鍵字 key的下一個存儲地址。若下一個存儲地 址仍被占用,則繼續執行step2,直到找 到能用的存儲地址為止。
時間復雜度:幾乎是O(1),取決於產生沖突的多少。
4.分塊查找
將n個數據元素"按塊有序"划分為m塊(m ≤ n)。
每一塊中的結點不必有序,但塊與塊之間必須"按塊有序";
即第1塊中任一元素的關鍵字都必須小於第2塊中任一元素的關鍵字;
而第2塊中任一元素又都必須小於第3塊中的任一元素,……。
然后使用二分查找及順序查找。
二、二分查找
一般是操作有序數組,查找過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜素過程結束;
如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。如果在某一步驟數組為空,則代表找不到。
這種搜索算法每一次比較都使搜索范圍縮小一半。時間復雜度:O(logn)。
三、二分查找的應用
二分查找一般是用在有序序列的查找,很多時候查找需要結合排序操作來進行。
但是需要注意,二分查找並不一定非要在有序序列中才能得到應用,
只要在二分之后可以淘汰掉一半數據的場景,都可以應用二分搜索。
四、遞歸和非遞歸實現
public static int binSearch(int[] arr,int des){ if(arr==null || arr.length<1){ return -1; } int left=0; int right=arr.length-1;//注意防止數組下標越界 /** * 這里的判斷條件必須包含等於, * 考慮{1,2,3},查找1,如果不判斷等於,就丟失了比較導致查找錯誤 */ while(left<=right){ int mid=left+(right-left)/2; if(des==arr[mid]){ return mid; }else if(des<arr[mid]){//舍棄右側 /** * 注意,此處的mid已經參與過比較了並失敗了, * 所以重新二分不要包含進來,下同 */ right=mid-1; }else{//舍棄左側 left=mid+1; } } return -1;//查找失敗 返回-1 } /** * @Title: bSearchRecursion * 遞歸參數有不同,left和right明顯在每次遞歸時都是變的 */ public static int binSearchRecursion(int[] arr,int left,int right,int des){ if(arr==null || arr.length<1){ return -1; } if(left<=right){ int mid=left+(right-left)/2; if(des==arr[mid]){ return mid; }else if(des<arr[mid]){//舍棄右側 return binSearchRecursion(arr,left,mid-1,des); }else{//舍棄左側 return binSearchRecursion(arr,mid+1,right,des); } } return -1; }
五、二分查找和斐波那契查找
折半查找,一般將待比較的key值與第mid=(low+high)/2位置的元素比較,
同時為了防止溢出,使用更高效的移位,也可以記做 mid=low+((low+high)>>1)。
比較結果分三種情況
1)相等,mid位置的元素即為所求
2)> ,low=mid+1;
3)< ,high=mid-1;
斐波那契查找要求元素表中記錄的個數為某個斐波那契數減1,即n=Fk-1;
斐波那契數列:0、1、1、2、3、5、8、13、21、……
如果設F(n)為該數列的第n項(n∈N)。那么這句話可以寫成如下形式:
F(0) = 0,F(1)=1,F(n)=F(n-1)+F(n-2) (n≥2),
這是一個線性遞推數列,斐波那契數列的算法實現經常在數據結構教材的遞歸一節中出現。
對於二分查找,分割是從mid=(low+high)/2開始;而對於斐波那契查找,分割是從mid = low + F[k-1] - 1開始的; 通過上面知道了,數組a現在的元素個數為F[k]-1個,即數組長為F[k]-1,mid把數組分成了左右兩部分, 左邊的長度為:F[k-1] - 1, 那么右邊的長度就為(數組長-左邊的長度-1), 即:(F[k]-1) - (F[k-1] - 1) = F[k] - F[k-1] - 1 = F[k-2] - 1。
斐波那契查找的核心是:
1)當key=a[mid]時,查找成功;
2)當key<a[mid]時,新的查找范圍是第low個到第mid-1個,此時范圍個數為F[k-1] - 1個,即數組左邊的長度,所以要在[low, F[k - 1] - 1]范圍內查找;
3)當key>a[mid]時,新的查找范圍是第mid+1個到第high個,此時范圍個數為F[k-2] - 1個,即數組右邊的長度,所以要在[F[k - 2] - 1]范圍內查找。
與二分查找相比,斐波那契查找它只涉及加法和減法運算,而不用除法(用“>>1”要好點)。因為除法比加減法要慢,在海量數據的查找過程中,這種細微的差別可能會影響最終的效率。
參考《大話數據結構》