2種二分查詢及2種優化方式

2021-07-05 03:09:33 字數 3986 閱讀 2414

題目1:經典演算法—二分查詢

折半查詢的基本思想:減小查詢序列的長度,分而治之的進行關鍵字的查詢。該序列必須是有序的

查詢過程是:在有序表中,取中間的記錄作為比較關鍵字,若給定值與中間記錄的關鍵字相等,則查詢成功;若給定的值小於中間記錄的關鍵字,則在中間記錄的左半區間繼續查詢;若給定值大於中間記錄的關鍵字,則在中間記錄的右半區間繼續查詢;不斷重複這個過程,直到查詢成功。否則查詢失敗

實現:通常設定3個指標:low、high、mid。假設序列(陣列)是從小到大排序。如果陣列是從大到小排序,只需要改一些**即可,思想不變。

code:

int binsearch(int* array, int n, int key)

else

if (key > array[mid])

else

}return -1;

}

實際上,二分查詢的過程可以繪製成一棵二叉樹,每次二分查詢的過程就相當於把原來的樹劃分為兩棵子樹,所以每次二分之後下次就只需要查詢其中一半的資料就可以了。那麼二分查詢演算法的時間複雜度是多少呢?在最好的情況下,只需要查詢一次就可以了,因為這時候中間記錄的關鍵字與要查詢的key是相等,自然一次就夠了。在最壞的情況下是從根節點查詢到最下面的葉子結點,這個過程需要的時間複雜度是o(logn)

需要注意的是,雖然二分查詢演算法的效率很高(這也是二分查詢演算法被廣泛應用的原因),但是仍然是有使用條件的:有序。就是說在需要頻繁進行插入或者刪除操作的資料記錄中使用二分查詢演算法不太划算,因為要維持資料的有序還需要額外的排序開銷

題目2:

公升序陣列a經過迴圈右移後,二分查詢給定元素x。

如a=,迴圈移動後a=。

思路:

(1)類似正常的二分查詢,都是不斷移動左右邊界,不過判斷條件更加複雜一點。

(2)每次計算中間那個元素mid,和左邊界的元素left比較,總能確定有一邊的區間是公升序的;

(3)然後對公升序那邊進行分析,若這個區間不可能包含x,則不再考慮這個區間;若可能包含x,則將查詢範圍限制在這個區間。即每次都可以排除一半區間。

int binsearch(int* arr, int n, int key) 

else

//右邊公升序

}return -1;

}

優化一:插值查詢演算法可以發現二分查詢每次都是選取中間的那個記錄關鍵字作為劃分依據的,那為什麼不可以是其他位置的關鍵字呢?在有些情況下,使用二分查詢演算法並不是最合適的。舉個例子:在1-1000中,一共有1000個關鍵字,如果要查詢關鍵字10,按照二分查詢演算法,需要從500開始劃分,這樣的話效率就比較低了,所以有人提出了插值查詢演算法。說白了就是改變劃分的比例,比如三分或者四分。

插值查詢演算法對二分查詢演算法的改進主要體現在mid的計算上,其計算公式如下:

而原來的二分查詢公式是這樣的:

所以我們發現主要變化的地方是1/2這個係數。其思想可以總結如下:插值查詢是根據要查詢的關鍵字的key與查詢表中最大最小記錄的關鍵字比較之後的查詢演算法,其核心是上述計算mid的計算公式。由於大體框架與二分查詢演算法是一致的,所以時間複雜度仍然是o(logn)。

優化二:斐波那契查詢演算法

從前面的分析中可以看到,無論劃分的關鍵字太大或者太小都不合適,所以又有人提出了斐波那契查詢演算法,其利用了**分割比原理來實現的。

乙個數列如果滿足f(n)=f(n-1)+f(n-2),則稱這個數列為斐波那契數列。在斐波那契查詢演算法中計算mid的公式如下:

斐波那契查詢的前提是待查詢的查詢表必須順序儲存並且有序。波那契查詢與折半查詢很相似,他是根據斐波那契序列的特點對有序表進行分割的

要求開始表中記錄的個數為某個斐波那契數小1,及n=fk-1;

開始將key值與第f(k-1)位置的記錄進行比較(即mid=low+f(k-1)-1),比較結果也分為三種

1、key == arr[mid],mid位置的元素即為所求

2、key > arr[mid],low=mid+1,k-=2

low=mid+1:說明待查詢的元素在[mid+1,high]範圍內

k-=2:說明範圍[mid+1,high]內的元素個數為n-(f(k-1))= fk-1-f(k-1)=fk-f(k-1)-1=f(k-2)-1個,所以可以遞迴的應用斐波那契查詢

3、key < arr[mid],high=mid-1,k-=1

low=mid+1:說明待查詢的元素在[low,mid-1]範圍內

k-=1:說明範圍[low,mid-1]內的元素個數為f(k-1)-1 個,所以可以遞迴 的應用斐波那契查詢

code:

const

int max_size=20;//斐波那契陣列的長度

//構造乙個斐波那契陣列

void fibonacci(int* f)

//斐波那契查詢

//arr為要查詢的陣列,n為要查詢的陣列長度,key為要查詢的關鍵字

int fibonaccisearch(int* arr, int n, int key)

else

if(key > temp[mid])

else

}

delete temp;

return -1;

}

斐波那契查詢的核心是:

1、當key = arr[mid]時,查詢成功;

2、當key < arr[mid]時,新的查詢範圍是第low個到第mid-1個,此時範圍個數為f[k-1] - 1個,即陣列左邊的長度,所以要在[low, f[k - 1] - 1]範圍內查詢;

3、當key > arr[mid]時,新的查詢範圍是第mid+1個到第high個,此時範圍個數為f[k-2] - 1個,即陣列右邊的長度,所以要在[f[k - 2] - 1]範圍內查詢。

關鍵點1:

關於斐波那契查詢, 如果要查詢的記錄在右側,則左側的資料都不用再判斷了,不斷反覆進行下去,對處於當眾的大部分資料,其工作效率要高一些。所以儘管斐波那契查詢的時間複雜度也為o(logn),但就平均效能來說,斐波那契查詢要優於折半查詢。可惜如果是最壞的情況,比如這裡key=1,那麼始終都處於左側在查詢,則查詢效率低於折半查詢

關鍵點2:

1、折半查詢是進行加法與除法運算的(mid=(low+high)/2)

2、插值查詢則進行更複雜的四則運算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low])))

3、而斐波那契查詢只進行最簡單的加減法運算(mid = low + f[k-1] - 1),在海量資料的查詢過程中,這種細微的差別可能會影響最終的效率。

二分查詢 2

v1中使用while迴圈的方式,我們也可以不使用迴圈,使用遞迴的方式來實現 public static intbinarysearch v2 int array,int value public static intmysearch int array,int start,int end,int v...

2 二分查詢(上)

二分模板有兩個,分別適用於不同的情況。演算法思路 假設目標值在閉區間 l,r 中,每次將區間長度縮小一半,當l r時,我們就找到了目標值。版本一 當我們將區間 l,r 劃分成 l,mid 和 mid 1,r 時,其更新操作時l mid 1或者 r mid 計算mid時不需要加1。版本二 當我們將區間...

2 二分查詢(下)

假設按照公升序排序的陣列在預先未知的某個點上進行了旋轉。例如,陣列 0,1,2,4,5,6,7 可能變為 4,5,6,7,0,1,2 搜尋乙個給定的目標值,如果陣列中存在這個目標值,則返回它的索引,否則返回 1 你可以假設陣列中不存在重複的元素。你的演算法時間複雜度必須是 o log n 級別。輸入...