思想及實現
這是學演算法的入門演算法,先舉個最簡單的例子:
猜數字1:我心中默念乙個數字,範圍在1~100,你每次可以猜乙個數,我會回答你大了,小了,直到猜中為止。
答:比如假設要猜的數字是38,整個過程如下:
猜測範圍(1,100),猜50,大了。所以比50大的都不用考慮了,包括50。
猜測範圍(1,49),猜25,小了。所以比25小的也不用考慮了,以此推論。
猜測範圍(26,49)猜37,小了。
猜測範圍(38,49)猜44,大了。
猜測範圍(38,43)猜40,大了。
猜測範圍(38,39)猜38,猜中了。
我們可以把這個過程抽象成**:
int
bin_search()
return-1
;//沒找到
}
幾個重點:
二分查詢必須應用於有序資料,預設為從小打大。樸素二分查詢的資料不能重複,但可以不連續。
看到這裡也許有人會說,如果我的obj都提前知道了還有什麼意義呢?但是有時候,我們需要在一堆資料裡面找到第乙個滿足要求的資料,比如這道題:
實戰
原題:
吃瓜問題:有m堆瓜和n個群眾,需要盡量滿足每位群眾吃瓜需求,但是為了避免浪費,分配的瓜不能多也不能少,如果沒能找到就不分配。
輸入: 輸入共 3 行。
第一行兩個整數 m,n。
第二行 m 個整數分別表示 m堆瓜的數量。(保證各不相同)
第三行 n 個整數分別表示 n個群眾想吃的數量。(保證各不相同)
輸出: 對於每個 yi 輸出一行乙個整數為對應數量的瓜的編號,若沒有對應數量的瓜,則輸出 0。
輸入樣例:
5 31 3 26 7 15
26 99 3
輸出樣例:30
2
需要注意的是,每堆瓜我們不僅需要儲存瓜的數量,也需要儲存他的編號,因此可以用結構體來儲存。
排序的時候也對整個結構體進行排序。
這道題可以分為3個步驟:
接收資料,排序,二分查詢。直接上**:
#include
#include
#include
#include
using
namespace std;
typedef
struct node
;int m, n;
node wm[
100005];
//比較函式
bool
cmp(
const node &a,
const node &b)
//注意我們要比較瓜的數量,返回瓜的編號
intbin_search
(int obj,
int l,
int r)
return0;
}int
main()
sort
(wm +
1, wm + m +
1, cmp)
;int obj,l =
1, r = m;
for(
int i =
0; i < n; i++
)return0;
}
思路及實現
還是上面得吃瓜問題,如果此時,我要求每個人分配的西瓜數量要大於等於他所需要的數量,那應該如何是好?
其實此類問題可以抽象為以下問題:
在000000111111中尋找第乙個1。 0表示不滿足條件的值,1表示滿足條件的值。比如說我要吃20個西瓜,那你給我21個或者22個都是滿足條件,但是為了節約,應該找出最小的。也就是21的編號。如果找不出來就返回0.
直接上題目吧:
原題:
吃瓜問題公升級版:有m堆瓜和n個群眾,需要盡量滿足每位群眾吃瓜需求,但是為了避免浪費,分配的應該是剛好大於等於需要的瓜的第一堆,如果所有數量都不能滿足就不分配。
輸入: 輸入共 3 行。
第一行兩個整數 m,n。
第二行 m 個整數分別表示 m堆瓜的數量。(保證各不相同)
第三行 n 個整數分別表示 n個群眾想吃的數量。(保證各不相同)
輸出 對於每個 yi 輸出一行乙個整數為大於等於需要數量的第一堆瓜的編號,若所有瓜堆的數量均小於需要數量,則輸出 0。
輸入樣例:
5 51 3 26 7 15
27 10 3 4 2
輸出樣例:05
242
有了上題的基礎應該很好理解這題,需要注意的是,此題中,要求找不到返回0,因此我們可以在n中增加一堆瓜,數值設為超級大,序號設為0。這樣其它條件都不滿足,就一定會找到這堆瓜,返回的編號自然也就是0了。可以減少**量。直接上**:
#include
#include
#include
#include
using namespace std;
typedef struct node
;int m, n;
node wm[100005]
;bool cmp(const node &a, const node &b)
int binleft_search(int obj, int l, int r)
//此時l=r
return wm[l].num;
}int main(
) wm[0].num=0; //增加一堆超多的瓜
wm[0].val=2100000000;
sort(wm , wm + m + 1, cmp);
int obj,l = 0, r = m;
for(int i = 0; i < n; i++)
return 0;
}
小試牛刀:
右邊界問題:11110000問題。見下方總結
關於左右邊界的總結:
00001111問題(找左側邊界):
若二分找到的是0,說明此時要找的答案在右側,所以左側增大,l = mid + 1
若二分找到的是1,說明此時找到的答案在左側或者自身,所以右側減少,但是需要保護mid,r = mid.
1111000(找右側問題):
若二分找到的是0,說明此時要找的答案在左側,所以右側減少,r = mid - 1
若二分找到的是1,說明此時找到的答案在右側或者自身,所以左側增大,但是需要保護midl = mid.
還但有一點不同:mid=(l+r+1)/2; 為什麼要+1呢,這是為了mid的位置向右偏,否則可能會導致死迴圈:
若 l 指標指向 1 ,r 指向 0 時,若按照原來的公式mid = (l+r) /2,mid還是l,沒有變化,因此會死迴圈。若+1,則此時mid 指向0,便可以收縮r。
記憶口訣:
樸素二分:
迴圈條件:左右可相等: l<=r
劃分條件:取其正中間:(l+r)/ 2
左側收縮:若小則公升左: <,l=mid+1
右側收縮:若大則降右: >,r=mid-1
終止條件:相等則退出: =,return mid
while
(l<=n)
右側二分:00001111問題
迴圈條件:左右不相等 l != r
劃分條件:取其正中間:(l+r)/ 2
先左收縮:若0則公升左: <,l=mid+1
其他情況:右側需保護。 r=mid
while
(l!=r)
左側二分:111110000問題
迴圈條件:左右不相等 l != r
劃分條件:加一再除二:(l+r+1)/ 2
先右收縮:若0則降右: >,r=mid-1
其他情況:左側需保護。 l=mid
while
(l!=r)
題目1 二分 二分查詢
時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述nettle最近在玩 艦 因此nettle收集了很多很多的船 這裡我們假設nettle氪了很多金,開了無數個船位 去除掉重複的船之後,還剩下n 1 n 1,000,000 種不同的船。每一艘船有乙個稀有值,任意兩艘船的稀有...
題目1 二分 二分查詢
link 時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述nettle最近在玩 艦 因此nettle收集了很多很多的船 這裡我們假設nettle氪了很多金,開了無數個船位 去除掉重複的船之後,還剩下n 1 n 1,000,000 種不同的船。每一艘船有乙個稀有值,任意兩...
二分查詢題目總結
一位大佬曾經說過 二分查詢思路很簡單,細節是魔鬼 做了一些題後感慨真的是這樣 二分查詢法 實質就是將乙個有序的資料集不斷地對半分割,直至找到目標值 其中兩個最關鍵的也是在面對不同題目會稍有不同的就是怎麼退出迴圈,怎麼縮小查詢空間。怎麼突破這兩點就是關鍵 在乙個有序資料集裡查詢乙個乙個目標數可以有一下...