C語言基礎(chǔ)之二分查找知識最全匯總
一、前言
在自學(xué)二分查找的過程中我想到了一些變化問題,有的自己就慢慢理解了,有的在網(wǎng)上找到了答案,奈何沒有找到想要的總結(jié)歸納。我就斗膽自己寫了一篇,號稱史上最全。希望和我一樣的伙伴可以少走一點彎路。
二分查找憑借其低時間復(fù)雜度O(log(n))成為了各個蒟蒻的入門知識,但是其衍生出的各種題目相較原題目而言就沒有那么容易求解,以下借用c語言實現(xiàn)二分查找算法及其衍生。二分查找僅適用于事先已經(jīng)排好序的順序表。其基本思路就是每次取中間數(shù),如果中間數(shù)大于所求數(shù)就向上查找,反之向下。
二、原始二分查找
1.在有序數(shù)組中尋找一個數(shù)所在下標。
int main(){ int arr[] = {1,2,3,4,5,6,7,8,9}; int left,right,fin; fin = 3; left = 0;right = sizeof(arr)/sizeof(arr[0])-1; while(left <= right) { int mid; mid = (left+right) / 2; if (arr[mid] == fin) { printf("找到了:%d\n", mid); return 0; } if (mid < fin) left = mid+1; else if (mid > fin) right = mid-1; } printf("沒找到"); return 0; }
對于這種基本的排序問題,我們通過判斷mid變量與key的大小對有序數(shù)組進行二分。
當查找過程中,會出現(xiàn)key值下標坐落在left或right上時的情況,
以下用left舉例
當left = key時,一個顯然的結(jié)論,此時arr[mid]無法等于key。其所作工作為減小right,進一步縮進范圍,直到right = left或right = left+1,此時范圍縮小到最小,mid = left。輸出mid的值即為所求下標。
我們當然可以在left和right = key時直接輸出key的下標,但是這樣會造成多次比較。
三、二分查找的變化之數(shù)組元素重復(fù)
對于二分查找而言,會出現(xiàn)數(shù)組元素重復(fù)的情況,以下問題的求解建立在數(shù)組元素重復(fù)的情況下:
1.返回匹配數(shù)key的最小下標
#include <stdio.h> int main() { int arr[] = {1,3,3,3,3,4,5,6,7}; int left,right,lenght,key; key = 3; left = 0; lenght = sizeof(arr)/sizeof(arr[0]); right = lenght -1; while(left <= right){ //等于的條件不能忽略 int mid = (left+right)/2; if (arr[mid] < key) left = mid + 1; //目的是用left來尋找key點,不考慮mid = key的情況 else right = mid - 1; } if ((left < lenght)&&(arr[left] == key)) printf("%d\n",left); return 0; }
當arr[mid] == key 時,此時直接輸出,則無法達到最小坐標。所以我們需要移動left或right。對于移動left而言,顯然移動后left將大于mid,更不可能找到最小坐標。所以需要移動right,讓left逐漸逼近key的最小下標。
當arr[mid] > key時,此時需要移動right,所以此時不會出現(xiàn)left = key的情況
當arr[mid] < key時,此時需要移動left,前面已經(jīng)知道m(xù)id<key,當left = mid + 1時,(其實類似于單調(diào)函數(shù)的求值)arr[mid] < arr[left] <=key
與原題對比,我們可以發(fā)現(xiàn),只有arr[mid] == key 時產(chǎn)生了變化,這里有一個顯然的結(jié)論,對于arr[mid] == key ,當求最小下標時,我們需要下標left<mid,所以只能移動right,當求最大下標時,我們需要right>mid,所以只能移動left。
2.返回匹配數(shù)的最大下標
#include <stdio.h> int main() { int arr[] = {1,3,3,3,3,4,5,6,7}; int left,right,lenght,key; key = 3; left = 0; lenght = sizeof(arr)/sizeof(arr[0]); right = lenght -1; while(left <= right){ int mid = (left+right)/2; if (arr[mid] <= key) left = mid + 1; else right = mid - 1; } if ((right >= 0)&&(arr[right] == key)) printf("%d\n",right); return 0; }
3.返回第一個大于匹配數(shù)的下標
#include <stdio.h> int main() { int arr[] = {1,1,1,1,1,4,5,6,7}; int left,right,lenght,key; key = 3; left = 0; lenght = sizeof(arr)/sizeof(arr[0]); right = lenght -1; while(left <= right){ int mid = (left+right)/2; if (arr[mid] < key) left = mid + 1; else right = mid - 1; } printf("%d\n",arr[left]); return 0; }
這個問題,比較簡單,把之前的if條件注釋掉就好了。=_=因為我們在循環(huán)過程中如果沒有找到,left會自己停在第一個大于key的坐標上。
4.查找某個數(shù)的出現(xiàn)次數(shù)
這個問題就比較進階,一開始在一篇介紹二分查找的文章里看到求出現(xiàn)次數(shù),怎么想也聯(lián)系不到二分查找。最后在網(wǎng)上找到了別人寫的代碼,其實就是兩次二分查找,但是試了好幾次也沒有寫對,看來我的蒟蒻路還有很長。
四、輪轉(zhuǎn)循環(huán)
何為輪轉(zhuǎn)(循環(huán))有序數(shù)組,就比如說{7,8,9,0,1,2,3,4,5,6}就是一個輪轉(zhuǎn)數(shù)組,其最小值不在[0]點。對于這樣的數(shù)組是否可以使用二分查找,?
對于這種問題,我們需要考慮的是他的順序起點,以{7,8,9,0,1,2,3,4,5,6}為例,他的順序起點為a[3] = 0;
(其實把它理解成為一個分段函數(shù)最容易,但是csdn不好寫數(shù)學(xué)表達式,不好畫圖,有興趣的蒟蒻可以私信我)
#include <stdio.h> int find(int arr[],int left,int right,int key) { //傳參a[]是所求數(shù)組,left,right為所求左右下標,key是所求元素 while (left <= right) { int mid = (left + right) / 2; if(arr[mid] > key){ if(arr[mid] < arr[right])//當此式成立時表明元素順序起點在mid左邊。 right = mid - 1; else {//元素順序起點在mid右邊,但是key有可能在mid左邊有可能在mid右邊 if (key > arr[left])//在左邊 right = mid - 1; else left = mid + 1; } } else if(arr[mid] < key){ if(arr[mid] > arr[left])//在mid左邊是遞增的(順序起點在[0]或右邊) left = mid + 1; else{//順序起點在左邊且不在[0]上 if (key > arr[right])//在mid左邊 right = mid - 1; else left = left + 1; } } else//當相等時直接返回mid return mid; } } int main() { int left,right,key,mid; int arr[] = {7,8,9,0,1,2,3,4,5,6}; left = 0; right =( sizeof(arr)/sizeof(arr[0]) ) - 1; key = 4; mid = find(arr,left,right,key); printf("峽谷吳彥祖 %d",mid); return 0; }
五、楊氏矩陣
在劍指offer的項目練習上,有在楊氏矩陣中對一個數(shù)進行查找的案例。題中所給出的解題思路是利用楊氏矩陣每行遞增,每列遞增的性質(zhì)每次排除某行或某列。而在我的實踐過程中,其可以采用二分查找的思路,對大規(guī)模的矩陣進行更加優(yōu)化的查找。以下是此方法的代碼:
int yang_matrix_2(int arr[X][Y],int num) { int x = 0; int y = Y; int a = x; int b = y; do{ a = x;b = y; if (arr[x][y] > num) {//在列上進行折半查找 int left = 0; int right = Y-1; while (left <= right){ int mid = (((left ^ right) >> 1)+(left & right)); if (arr[x][mid] > num) right = mid - 1; else { if (arr[x][mid] < num) left = mid + 1; else return 1;} } y = left; x--; } else{ if(arr[x][y] < num){ int left = 0; int right = X-1; while (left <= right){ int mid = (((left ^ right) >> 1)+(left & right)); if (arr[mid][y] > num) right = mid - 1; else {if (arr[mid][y] < num) left = mid + 1; else return 1;} } x = right; ++y; }//在行上進行折半查找 else return 1; } }while ((x != a) && (y != b)); return 0; }
六、用二叉樹來描述二分查找
二分查找過程可用二叉樹來描述:把當前查找區(qū)間的中間位置上的結(jié)點作為根,左子表和右子表中的結(jié)點分別作為根的左子樹和右子樹。
由此得到的二叉樹,稱為描述二分查找的判定樹(Decision Tree)或比較樹(Comparison Tree)。以下圖為例
七、二分查找的概率問題
我們需要引入一個概念叫做平均查找長度ASL,為確定記錄在查找表中的位置,需和給定值進行比較的關(guān)鍵字個數(shù)的期望值稱為查找算法在查找成功時的平均查找長度()。二分查找的ASL = log2(n+1)-1。
到此這篇關(guān)于C語言基礎(chǔ)之二分查找知識最全匯總的文章就介紹到這了,更多相關(guān)C語言二分查找匯總內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
嵌入式項目使用C語言結(jié)構(gòu)體位段特性實現(xiàn)斷言宏校驗數(shù)據(jù)范圍有效性的方法
今天小編就為大家分享一篇關(guān)于嵌入式項目使用C語言結(jié)構(gòu)體位段特性實現(xiàn)斷言宏校驗數(shù)據(jù)范圍有效性的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12C語言學(xué)生成績管理系統(tǒng)小設(shè)計
這篇文章主要為大家詳細介紹了C語言學(xué)生成績管理系統(tǒng)小設(shè)計,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01C++如何通過ostringstream實現(xiàn)任意類型轉(zhuǎn)string
再使用整型轉(zhuǎn)string的時候感覺有點棘手,因為itoa不是標準C里面的,而且即便是有itoa,其他類型轉(zhuǎn)string不是很方便。后來去網(wǎng)上找了一下,發(fā)現(xiàn)有一個好方法2013-09-09