排序和二分搜
排序
排序
- 選擇排序
 - 泡沫排序
 - 插入排序
 
選擇排序
找最大值
泡沫排序
讓最大的浮上來
插入排序
找合適的位置
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
排序和二分搜
- 302