C語言基礎(chǔ)之二分查找知識(shí)最全匯總
一、前言
在自學(xué)二分查找的過程中我想到了一些變化問題,有的自己就慢慢理解了,有的在網(wǎng)上找到了答案,奈何沒有找到想要的總結(jié)歸納。我就斗膽自己寫了一篇,號(hào)稱史上最全。希望和我一樣的伙伴可以少走一點(diǎn)彎路。
二分查找憑借其低時(shí)間復(fù)雜度O(log(n))成為了各個(gè)蒟蒻的入門知識(shí),但是其衍生出的各種題目相較原題目而言就沒有那么容易求解,以下借用c語言實(shí)現(xiàn)二分查找算法及其衍生。二分查找僅適用于事先已經(jīng)排好序的順序表。其基本思路就是每次取中間數(shù),如果中間數(shù)大于所求數(shù)就向上查找,反之向下。
二、原始二分查找
1.在有序數(shù)組中尋找一個(gè)數(shù)所在下標(biāo)。
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;
}
對(duì)于這種基本的排序問題,我們通過判斷mid變量與key的大小對(duì)有序數(shù)組進(jìn)行二分。
當(dāng)查找過程中,會(huì)出現(xiàn)key值下標(biāo)坐落在left或right上時(shí)的情況,
以下用left舉例
當(dāng)left = key時(shí),一個(gè)顯然的結(jié)論,此時(shí)arr[mid]無法等于key。其所作工作為減小right,進(jìn)一步縮進(jìn)范圍,直到right = left或right = left+1,此時(shí)范圍縮小到最小,mid = left。輸出mid的值即為所求下標(biāo)。
我們當(dāng)然可以在left和right = key時(shí)直接輸出key的下標(biāo),但是這樣會(huì)造成多次比較。
三、二分查找的變化之?dāng)?shù)組元素重復(fù)
對(duì)于二分查找而言,會(huì)出現(xiàn)數(shù)組元素重復(fù)的情況,以下問題的求解建立在數(shù)組元素重復(fù)的情況下:
1.返回匹配數(shù)key的最小下標(biāo)
#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點(diǎn),不考慮mid = key的情況
else
right = mid - 1;
}
if ((left < lenght)&&(arr[left] == key))
printf("%d\n",left);
return 0;
}
當(dāng)arr[mid] == key 時(shí),此時(shí)直接輸出,則無法達(dá)到最小坐標(biāo)。所以我們需要移動(dòng)left或right。對(duì)于移動(dòng)left而言,顯然移動(dòng)后left將大于mid,更不可能找到最小坐標(biāo)。所以需要移動(dòng)right,讓left逐漸逼近key的最小下標(biāo)。
當(dāng)arr[mid] > key時(shí),此時(shí)需要移動(dòng)right,所以此時(shí)不會(huì)出現(xiàn)left = key的情況
當(dāng)arr[mid] < key時(shí),此時(shí)需要移動(dòng)left,前面已經(jīng)知道m(xù)id<key,當(dāng)left = mid + 1時(shí),(其實(shí)類似于單調(diào)函數(shù)的求值)arr[mid] < arr[left] <=key
與原題對(duì)比,我們可以發(fā)現(xiàn),只有arr[mid] == key 時(shí)產(chǎn)生了變化,這里有一個(gè)顯然的結(jié)論,對(duì)于arr[mid] == key ,當(dāng)求最小下標(biāo)時(shí),我們需要下標(biāo)left<mid,所以只能移動(dòng)right,當(dāng)求最大下標(biāo)時(shí),我們需要right>mid,所以只能移動(dòng)left。
2.返回匹配數(shù)的最大下標(biāo)
#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.返回第一個(gè)大于匹配數(shù)的下標(biāo)
#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;
}
這個(gè)問題,比較簡單,把之前的if條件注釋掉就好了。=_=因?yàn)槲覀冊(cè)谘h(huán)過程中如果沒有找到,left會(huì)自己停在第一個(gè)大于key的坐標(biāo)上。
4.查找某個(gè)數(shù)的出現(xiàn)次數(shù)
這個(gè)問題就比較進(jìn)階,一開始在一篇介紹二分查找的文章里看到求出現(xiàn)次數(shù),怎么想也聯(lián)系不到二分查找。最后在網(wǎng)上找到了別人寫的代碼,其實(shí)就是兩次二分查找,但是試了好幾次也沒有寫對(duì),看來我的蒟蒻路還有很長。
四、輪轉(zhuǎn)循環(huán)
何為輪轉(zhuǎn)(循環(huán))有序數(shù)組,就比如說{7,8,9,0,1,2,3,4,5,6}就是一個(gè)輪轉(zhuǎn)數(shù)組,其最小值不在[0]點(diǎn)。對(duì)于這樣的數(shù)組是否可以使用二分查找,?
對(duì)于這種問題,我們需要考慮的是他的順序起點(diǎn),以{7,8,9,0,1,2,3,4,5,6}為例,他的順序起點(diǎn)為a[3] = 0;
(其實(shí)把它理解成為一個(gè)分段函數(shù)最容易,但是csdn不好寫數(shù)學(xué)表達(dá)式,不好畫圖,有興趣的蒟蒻可以私信我)
#include <stdio.h>
int find(int arr[],int left,int right,int key) {
//傳參a[]是所求數(shù)組,left,right為所求左右下標(biāo),key是所求元素
while (left <= right) {
int mid = (left + right) / 2;
if(arr[mid] > key){
if(arr[mid] < arr[right])//當(dāng)此式成立時(shí)表明元素順序起點(diǎn)在mid左邊。
right = mid - 1;
else {//元素順序起點(diǎn)在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左邊是遞增的(順序起點(diǎn)在[0]或右邊)
left = mid + 1;
else{//順序起點(diǎn)在左邊且不在[0]上
if (key > arr[right])//在mid左邊
right = mid - 1;
else
left = left + 1;
}
}
else//當(dāng)相等時(shí)直接返回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的項(xiàng)目練習(xí)上,有在楊氏矩陣中對(duì)一個(gè)數(shù)進(jìn)行查找的案例。題中所給出的解題思路是利用楊氏矩陣每行遞增,每列遞增的性質(zhì)每次排除某行或某列。而在我的實(shí)踐過程中,其可以采用二分查找的思路,對(duì)大規(guī)模的矩陣進(jìn)行更加優(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) {//在列上進(jìn)行折半查找
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;
}//在行上進(jìn)行折半查找
else
return 1;
}
}while ((x != a) && (y != b));
return 0;
}
六、用二叉樹來描述二分查找
二分查找過程可用二叉樹來描述:把當(dāng)前查找區(qū)間的中間位置上的結(jié)點(diǎn)作為根,左子表和右子表中的結(jié)點(diǎn)分別作為根的左子樹和右子樹。
由此得到的二叉樹,稱為描述二分查找的判定樹(Decision Tree)或比較樹(Comparison Tree)。以下圖為例

七、二分查找的概率問題
我們需要引入一個(gè)概念叫做平均查找長度ASL,為確定記錄在查找表中的位置,需和給定值進(jìn)行比較的關(guān)鍵字個(gè)數(shù)的期望值稱為查找算法在查找成功時(shí)的平均查找長度()。二分查找的ASL = log2(n+1)-1。
到此這篇關(guān)于C語言基礎(chǔ)之二分查找知識(shí)最全匯總的文章就介紹到這了,更多相關(guān)C語言二分查找匯總內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Matlab實(shí)現(xiàn)水波倒影特效的制作
這篇文章主要介紹了如何利用Matlab制作出水波倒影的特效,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下2022-03-03
C++有限狀態(tài)機(jī)實(shí)現(xiàn)詳解
這篇文章主要為大家詳細(xì)介紹了C++有限狀態(tài)機(jī)的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
嵌入式項(xiàng)目使用C語言結(jié)構(gòu)體位段特性實(shí)現(xiàn)斷言宏校驗(yàn)數(shù)據(jù)范圍有效性的方法
今天小編就為大家分享一篇關(guān)于嵌入式項(xiàng)目使用C語言結(jié)構(gòu)體位段特性實(shí)現(xiàn)斷言宏校驗(yàn)數(shù)據(jù)范圍有效性的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12
C語言學(xué)生成績管理系統(tǒng)小設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語言學(xué)生成績管理系統(tǒng)小設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
C++如何通過ostringstream實(shí)現(xiàn)任意類型轉(zhuǎn)string
再使用整型轉(zhuǎn)string的時(shí)候感覺有點(diǎn)棘手,因?yàn)閕toa不是標(biāo)準(zhǔn)C里面的,而且即便是有itoa,其他類型轉(zhuǎn)string不是很方便。后來去網(wǎng)上找了一下,發(fā)現(xiàn)有一個(gè)好方法2013-09-09

