快排和二分
阿新 • • 發佈:2021-09-28
快排和二分
快排模板:
void qs(int q[] , int l , int r){
if(l >= r)return ;
int i = l - 1 , j = r + 1 , x = q[i + j >> 1] ;
while(i < j){
while(q[++ i ] < x) ;
while(q[-- j ] > x) ;
if(i < j)swap(q[i] , q[j]) ;
}
qs(q , l , j ) ;
qs(q , j + 1 , r) ;
}
二分查詢:
對一個序列,(不一定是單調的),某個位置的左邊滿足某個條件但是其右邊不滿足那種條件,就可以用二分去查詢。
在做整數二分的時候,最讓人頭疼的就是分段時邊界條件的判斷。下面記錄一下我做題的思路:
舉個栗子:789. 數的範圍 - AcWing題庫
題目大意:
輸出一個數字在非降序序列中出現的第一個位置和最後一個位置,若數列中沒有該數字,出“-1 -1”。
思路:
首先想第一個位置:k = getl(x) :在二分時,會有一箇中點mid = (l + r) / 2 。這時,目標x可能會出現在mid的左邊也可能出現在mid的右邊。先思考x出現在mid右邊的情況:
因為我們要找x的最左邊的那個位置,所以我們需要一直將右邊界r向左邊縮:
一句話,找左邊,右邊界向左縮
int getl(int x){ int l = 1 , r = n ; while(l < r){ int mid = l + r >> 1 ; if(x <= a[mid]) r = mid ;//右邊界向左邊縮 else l = mid + 1 ; } return l ; }
其次想最後一個位置:k = getr(x): 思路和getl類似
一句話,找右邊,左邊界向右縮
int getr(int x){
int l = 1 , r = n ;
while(l < r){
int mid = l + r + 1 >> 1;//注意l+1==r的邊界情況
if(a[mid] <= x) l = mid ;//l向右縮
else r = mid - 1 ;
}
return l ;
}
總程式碼:
#include<bits/stdc++.h> using namespace std ; const int N = 1e6 + 10 ; int n , m ; int a[N] ; void qs(int q[] , int l , int r){ if(l >= r)return ; int i = l - 1 , j = r + 1 ; int x = q[(i + j >> 1)] ; while(i < j){ while(q[++ i ] < x) ; while(q[-- j ] > x) ; if(i < j)swap(q[i] , q[j]) ; } qs(q , l , j ) ; qs(q , j + 1 , r) ; } int getl(int x){ int l = 1 , r = n ; while(l < r){ int mid = l + r >> 1 ; if(x <= a[mid]) r = mid ; else l = mid + 1 ; } return l ; } int getr(int x){ int l = 1 , r = n ; while(l < r){ int mid = l + r + 1 >> 1; if(a[mid] <= x) l = mid ; else r = mid - 1 ; } return l ; } void solve(){ int x ; cin>>x ; int k = getl(x) ; if(a[k] != x){ cout<<"-1 -1\n" ; return ; } cout<<k - 1<<" "<<getr(x) - 1 <<"\n" ; } int main(){ ios::sync_with_stdio(false) ; cin>>n>>m ; for(int i = 1 ; i <= n ; i ++ )cin>>a[i] ; qs(a , 1 , n ) ;//for(int i = 1 ; i <= n ; i ++ )cout<<a[i]<<" " ;cout<<endl ; while(m -- )solve() ; }