演算法 - 查詢
在演算法中用的比較多的查詢演算法是二分查詢,在這裡梳理一下整數二分和浮點數二分的思路。
將來複習資料結構的話也會在這裡補充。
二分查詢
(一)是什麼
二分查詢(折半查詢:Binary Search)的查詢過程:先確定待查記錄所在的範圍(區間),然後逐步縮小範圍直到找到或找不到該記錄為止。
注:
就算是找不到這個記錄最終也是可以停下的,只不過停下的位置對應的值並不是想要的。
(二)優勢
時間複雜度低:O(logn)
推導:
資料大小為n,每次查詢後資料都會減半,最壞情況為直到區間數為1個時才停止,即:n * (1/2)的x次方 = 1
解得x = logn
(三)兩個板子演算法
首先必須清楚一件事,二分演算法解決的本質是:尋找某一性質的邊界,給出的兩個板子就是找到性質1的右邊界(箭頭1)和性質2的左邊界(箭頭2)。
1.確定性質1的邊界——找箭頭1
mid = l + r + 1 >> 1
if(check(mid)): //判斷是否滿足該性質
Ture
mid在箭頭1的左邊,那要找到箭頭的話需要改變區間:l = mid,使[l, r] -> [mid, r] (包含mid,因為mid也滿足性質。下同理)。
False
mid在箭頭1的右邊,需要:r = mid - 1, 使[l, r] -> [l, mid -1]
2.確定性質2的邊界——找箭頭2
mid = l + r >> 1
if(check(mid)): //判斷是否滿足該性質
Ture
r = mid, [l, r] -> [l, mid]
False
l = mid + 1, [l, r] -> [mid + 1, r]
板子如下:
注意:
1.l = mid時mid後面需要+1
2.這兩個板子的返回結果就是兩個邊界
3.圖中的性質1和性質2往往對應的是小於和大於
bool check(int x){/* ... */}
int bsearch_1(int l, int r){
//性質1
while(l < r){
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return r; //此時l和r一樣,返回誰都可以
}
int bsearch_2(int l, int r){
//性質2
while(l < r){
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1
}
return l;
}
AcWing 789. 數的範圍
這道題就是怎麼用了。比如1 2 2 3 3 4問你3的起始位置和終止位置。(從0開始)
既然是有序的,那很輕易可以看出1 2 2 是<3的,4>3,我們可以將問題轉化為尋找 <=3 右邊界和 >=3 的左邊界。
<=3 的右邊界是位置4,>=3 的左邊界是位置3, 很明顯不是我們想要的答案。那我們直接顛倒順序,尋找 >=3 的左邊界 和 <=3 的右邊界就可以解決問題。
如果尋找不到,那麼邊界位置的值一定不是尋找的值,一個if語句就可以解決。
#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
int a[N];
int n, q;
int main(){
scanf("%d%d", &n, &q);
for(int i = 0; i < n; i ++) scanf("%d", &a[i]);
while(q --){
int k;
scanf("%d", &k);
//尋找k的左邊界
//>=k的左邊界,滿足性質2
int l = 0, r = n - 1;
while(l < r){
int mid = (l + r) >> 1;
if(a[mid] >= k) r = mid;
else l = mid + 1;
}
if(a[l] != k) cout << "-1 -1"<<endl;
else{
printf("%d ", l);
//尋找k的右邊界
//<=k的右邊界,滿足性質1
l = 0, r = n - 1;
while(l < r){
int mid = (l + r + 1) >> 1;
if(a[mid] <= k) l = mid; // l = mid 前面的mid應該+1
else r = mid - 1;
}
printf("%d\n", r);
}
}
return 0;
}
最常用的就是上面的整數二分板子了,浮點數二分更簡單,沒有+1-1的那些事兒。板子如下:
const double eps = 1e-6 ;一般定義的比題給的小兩位
double bsearch_3(double l, double r){
while(r - l > eps){
double mid = l + r >> 1;
if(check(mid)) r = mid; //結合題
else l = mid;
}
return l;
}
eg.求三次方根
#include<bits/stdc++.h>
using namespace std;
const double eps = 1e-8 ;//經驗之談,eps比題目要求的多兩位
int main(){
double n;
scanf("%lf", &n);
double l = -10000, r = 10000;
while(r - l > eps){
double mid = (r + l)/2;
if(mid * mid * mid >= n) r = mid;
else l = mid;
}
printf("%lf", l); //double型別輸出預設是6位,符合題目要求
return 0;
}
(四)哪些情況使用
這裡談論的就不僅僅是演算法了,還有資料結構的一些東西。
儲存的資料結構應該是可以實現隨機訪問的陣列,而不是連結串列。(連結串列不支援隨機訪問)
陣列內的資料必須有序
更適合處理靜態資料(沒有頻繁的資料插入、刪除操作)
太小的不適合,因為順序查詢就可以解決
太大的也不適合,因為二分底層依賴的就是陣列,而陣列在記憶體中就是一段連續的空間,你需要1G的空間,即使記憶體剩下2G可能也不夠用,因為剩餘的2G可能是離散分配後的結果,並沒有1G的連續空間。
二分依賴的只有陣列,所以大部分情況還是可以解決的。同時二分可以解決的雜湊表、二叉樹也都可以解決。這些以後再補充,演算法這裡最常用的是二分。
————————————————
版權宣告:本文為CSDN博主「御用廚師」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/qq_45520647/article/details/119176850
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2784442/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 查詢演算法__插值查詢演算法
- 查詢演算法__二分查詢演算法
- 查詢演算法__Fibonacci查詢演算法
- 查詢演算法演算法
- 演算法(查詢)演算法
- #查詢演算法#【1】簡單查詢:順序、折半查詢演算法
- 查詢演算法之二分查詢演算法
- 查詢演算法(上)演算法
- 查詢演算法(下)演算法
- Java 查詢演算法Java演算法
- 常用查詢演算法演算法
- 二分查詢【折半查詢】演算法 PHP 版演算法PHP
- 插值查詢演算法演算法
- 子字串查詢演算法字串演算法
- 雜湊查詢演算法演算法
- 線性查詢演算法演算法
- 常用查詢演算法 (轉)演算法
- 分塊查詢【大規模資料查詢演算法優化】【索引順序查詢】演算法 PHP 版演算法優化索引PHP
- 查詢演算法集:順序查詢、二分查詢、插值查詢、動態查詢(陣列實現、連結串列實現)演算法陣列
- php查詢演算法的理解PHP演算法
- 七大查詢演算法演算法
- 演算法-->折半查詢(快排)演算法
- 二分查詢演算法演算法
- 常用的 STL 查詢演算法演算法
- 演算法->二分查詢演算法
- 【演算法】二分查詢演算法
- 如何找東西?查詢演算法之順序查詢和二分查詢詳解演算法
- 【演算法】二分查詢與暴力查詢(白名單過濾)演算法
- 路徑查詢演算法應用之A*演算法演算法
- 演算法之逆序對兒查詢演算法
- 查詢演算法及雜湊表演算法
- 【筆記】靜態查詢演算法筆記演算法
- C#演算法設計查詢篇之03-插值查詢C#演算法
- 查詢與排序01,線性查詢,時間複雜度,演算法排序時間複雜度演算法
- 資料結構與演算法——查詢演算法-斐波那契(黃金分割法)查詢資料結構演算法
- 資料結構與演算法:查詢演算法資料結構演算法
- Algorithm-search 查詢演算法 pythonGo演算法Python
- iOS 演算法之排序、查詢、遞迴iOS演算法排序遞迴