排序和二分搜
排序
排序
- 選擇排序
- 泡沫排序
- 插入排序
選擇排序
找最大值
泡沫排序
讓最大的浮上來
插入排序
找合適的位置
sort函式
#inlcude<algorithm>
預設從小到大
用法
sort(起始指標, 結尾指標)
不包含
起始指標
結尾指標
排序範圍
實際用法
有一個陣列a, 要排序a[0]~a[n - 1]
- sort(&a[0], &a[n]);
- sort(a, a + n);
如果要由大到小呢?
- 寫一個回傳bool的比較函式
- sort(起始指標, 結尾指標, 比較函式名稱)
#include<iostream>
#include<algorithm>
using namespace std;
int a[200005];
bool cmp(int a, int b) {
return a > b;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n, cmp);
for (int i = 0; i < n; i++) {
cout << a[i] << ' ';
}
cout << '\n';
}a915
排序完之後要幹嘛?
二分搜尋法
玩過猜數字嗎?
- 每次猜一個數字
- 如果猜到就結束遊戲
- 如果沒猜到,會知道答案比你猜的數字大或小
有沒有一個策略可以讓你有在最差情況下,猜的次數最少?
每次猜數字範圍的中間
- 猜到就結束
- 如果答案比猜的小,可以刪掉所有比它大的
- 如果答案比猜的大,可以刪掉所有比它小的
最慘會猜幾次?
- 假設數字範圍有\(n\)個數字
- 每次沒猜到都會刪掉一半
- 設最壞情況下會猜k次
- 在猜到第k-1次的時候,可能的範圍要多於一個
{\frac n {2^{k - 1}}} > 1
n > 2^{k - 1}
\log_{2}n > k - 1
假設要猜的範圍是\(0\)~\(2^{31} - 1\)
最多只需要猜31次!
用數學算算看
二分搜尋法
在一個有序陣列中快速找到一個值的演算法
每次找搜尋陣列範圍的中間位置
如果他比你的目標大,就往左找
如果他比你的目標小,就往右找
二分搜尋法
遞迴想法
設函式\(f(l, r, val)\)為整數\(val\)在陣列a[\(l\)~ \(r\)]區間中的位置
- 如果\(l\) == \(r\),回傳\(l\)
- 找陣列a[\(l\) ~ \(r\)]中間位置\(mid = (l + r) / 2\)
- 如果a[\(mid\)]=val,回傳\(mid\)
- 如果a[\(mid\)]>val,回傳\(f(l, mid - 1, val)\)
- 如果a[\(mid\)]<val,回傳\(f(mid + 1, r, val)\)
#include<iostream>
#include<algorithm>
using namespace std;
int a[200005];
int f(int l, int r, int val) {
if (l == r) {
if (a[l] == val)
return l;
else
return -1;
}
int mid = (l + r) / 2;
if (a[mid] == val)
return mid;
else if (a[mid] > val)
return f(l, mid - 1, val);
else
return f(mid + 1, r, val);
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
int q;
cin >> q;
cout << f(0, n - 1, q) << '\n';
}#include<iostream>
#include<algorithm>
using namespace std;
int a[200005];
int f(int l, int r, int val) {
while (l < r) {
int mid = (l + r) >> 1;
if (a[mid] == val)
return mid;
else if (a[mid] < val)
l = mid + 1;
else
r = mid - 1;
}
if (a[l] == val)
return l;
return -1;
}
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
int q;
cin >> q;
cout << f(0, n - 1, q) << '\n';
}對答案二分搜
- 答案在一個固定範圍內
- 題目要找符合條件的最小值或最大值
- 答案具單調性(必要)
對答案二分搜條件
單調性是啥?
zerojudge c575
假設現在的直徑是k
- 如果直徑k可以成為答案
- 那直徑比k大的都可以是答案
- 如果直徑k不能是答案
- 那直徑比k小的都不能是答案
假設現在的直徑是k
- 如果直徑k可以成為答案
- 那直徑比k大的都可以是答案
- 如果直徑k不能是答案
- 那直徑比k小的都不能是答案
如果直徑i可以,表示為1
如果不行,表示為0
用數列表示可能長這樣
0, 0, 0, 0, 1, 1, 1, 1, 1, 1...
二分搜第一個出現的1!
怎麼檢查答案?
- 從左邊開始,找第一個沒被覆蓋的點
- 加上一個基地台,可以覆蓋(現在位置+k)的所有點
- 再找下一個沒被覆蓋的點
- 反覆執行
複雜度
- 檢查需要\(O(n)\)
- 二分搜需要\(O(\log{c})\) c為座標範圍
- 複雜度\(O(n\log{c})\)
#include <iostream>
#include <algorithm>
using namespace std;
int N, K;
int p[50005];
bool test(int d){
int now, nowpoint = 0;
for(int i = 0; i < K; i++){
now = p[nowpoint];
now+=d;
while(p[nowpoint] <= now){
nowpoint++;
if(nowpoint >= N)
return 1;
}
}
return 0;
}
int main()
{
while(cin >> N >> K){
for(int i = 0; i < N; i++){
cin >> p[i];
}
sort(p, p+N);
int l = 1, r = 1e9;
while(l != r){
int mid = (l + r) / 2;
if(test(mid)){
r = mid;
}else{
l = mid + 1;
}
}
cout<<l<<endl;
}
return 0;
}排序和二分搜
By scottchou
排序和二分搜
- 310