二分查找:
二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。值得注意的是折半查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列。
讓我們回憶一下平時我們如何在一本詞典裏查找一個單詞呢?
查找單詞過程的原理與二分查找的思路幾乎是相同的。
我們在字典(單詞按照“字典序”進行排序的)中查找單詞,先假設字典頁數一共200頁。第一步都是翻到字典的中間頁100頁,然後再判斷是該單詞是否出現在該頁,又或者出現在字典的前面還是後面。若是前面則翻到字典的50頁,若是後面則翻到字典的150頁,查找範圍從200頁——100頁——50頁…
查找的範圍不斷縮小,這也就是二分查找效率的體現!
實現二分法查找基本思路
(1)首先,從數組的中間元素開始搜索,如果該元素正好是符合條件的目標元素,則搜索過程結束,否則執行下一步。
(2)如果目標元素大於/小於中間元素,則在數組大於/小於中間元素的那一半區域查找,然後重複步驟(1)的操作。
(3)如果某一步數組爲空,則表示找不到目標元素。
二分法查找的時間複雜度O(logn)。
參考博客
代碼實現:在長度爲 len 的 arr 數組中二分查找元素 ans 。
int l=0, r=len-1
while(l<r){
int mid = (r+l)/2;
if(arr[mid]>ans){
r=mid-1;
}
else if(arr[mid]<ans){
l=mid+1;
}
else if(arr[mid]==ans){ //找到
ans=mid;
break;
}
}
例題一:
輸入 n(n≤10 6) 個不超過 109的單調不減的(就是後面的數字不小於前面的數字)非負整數 a1 ,a2 ,…, an,然後進行m(m≤10 5 ) 次詢問。對於每次詢問,給出一個整數q(q≤10 9),要求輸出這個數字在序列中的編號,如果沒有找到的話輸出 -1 。
輸入格式
第一行 2 個整數 n 和 m,表示數字個數和詢問次數。
第二行 n 個整數,表示這些待查詢的數字。
第三行 m 個整數,表示詢問這些數字的編號,從 1 開始編號。
輸出格式
m 個整數表示答案。
輸入輸出樣例
輸入
11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
輸出
1 2 -1
該題爲模板題,注意要處理找不到的情況即可。
#include<bits/stdc++.h>
using namespace std;
int data[1000000+5], q[100000+5];
int main()
{
int n, m;
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1; i<=n; i++){
cin>>data[i];
}
for(int i=1; i<=m; i++){
cin>>q[i];
}
for(int i=1; i<=m; i++){
int l=1, r=n;
int num=q[i];
while(l<r){
int mid = (l+r)/2;
if(data[mid]<num){
l = mid+1;
}
else{
r = mid;
}
}
if(data[r]==num){
cout<<r<<" ";
}
else{
cout<<-1<<" ";
}
}
return 0;
}
例題二
米爾科的伐木機工作過程如下:米爾科設置一個高度參數H(米),伐木機升起一個巨大的鋸片到高度H,並鋸掉所有的樹比H高的部分(當然,樹木不高於H米的部分保持不變)。米爾科就行到樹木被鋸下的部分。
例如,如果一行樹的高度分別爲20,15,10和17,米爾科把鋸片升到15米的高度,切割後樹木剩下的高度將是15,15,10和15,而米爾科將從第1棵樹得到5米,從第4棵樹得到2米,共得到7米木材。
米爾科非常關注生態保護,所以他不會砍掉過多的木材。這正是他爲什麼儘可能高地設定伐木機鋸片的原因。幫助米爾科找到伐木機鋸片的最大的整數高度H,使得他能得到木材至少爲M米。換句話說,如果再升高1米,則他將得不到M米木材。
輸入格式
第1行:2個整數N和M,N表示樹木的數量(1<=N<=1000000),M表示需要的木材總長度(1<=M<=2000000000)
第2行:N個整數表示每棵樹的高度,值均不超過1000000000。所有木材長度之和大於M,因此必有解。
輸出格式
第1行:1個整數,表示砍樹的最高高度。
輸入輸出樣例
輸入
5 20
4 42 40 26 46
輸出
36
思路:
①、求出每棵從低到高的樹的前綴和,方便計算可得木材
eg:sum[N]:第N棵樹的高度前綴和
前綴和概念
②、初始化:H_l = 0,H_r = 樹的最高高度。
③、利用 H_l、H_r 二分確定一個高度 H_mid。
④、初始化:s_l = 0,s_r = 樹的數量。
⑤、利用 s_l、s_r 二分確定一個高度小於等於H的樹 s_mid。
⑥、可計算出在H_mid高度下得到的木材 m
m = sum[N] - sum[r_s] - (N-r_s) * H
⑦、判斷M與m的大小關係。
若M<m,則 H_l = H_mid,重複③~⑥
若M>m,則 H_r = H_mid - 1,重複③~⑥
若M=m,則退出循環,輸出答案。
⑧、若m始終不能與M相等,則最終 H_l 會與 H_r 相交,輸出 H_r 即可。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll data[1000000+7], sum[1000000+7];
int main()
{
ll N, M;
ios::sync_with_stdio(false);
cin>>N>>M;
for(ll i=1; i<=N; i++){
cin>>data[i];
}
memset(sum, 0, sizeof(sum));
sort(data+1, data+N+1);
for(ll i=1; i<=N; i++){
sum[i] = sum[i-1]+data[i];
}
//選擇要砍的樹的最高高度
int l_h=0, r_h=data[N];
while(l_h<r_h)
{
int H=(l_h+r_h+1)/2; //"+1"防止陷入循環
//找出小於或等於最高高度的樹
int l_s=0, r_s=N;
while(l_s<r_s)
{
int S=(l_s+r_s+1)/2;//"+1"防止陷入循環
if(data[S]>H){
r_s=S-1;
}
else
l_s=S;
}
if(sum[N]-sum[r_s]-(N-r_s)*H<M){
r_h=H-1;
}
else {
l_h=H;
}
}
cout<<r_h;
return 0;
}
以後如果遇到較爲經典的二分查找類型的題會陸續添上……
希望將自己的學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白