步骤:
1.确定分界点x(q[ l ], q[(l + r) / 2], q[r])
2.调整区间(左区间<=x,右区间>=x)
3.递归处理左右两段
① 暴力做法
1.确定分界点x(q[ l ], q[(l + r) / 2], q[r])
2. 开两个数组a[ ]、b[ ]
3. 遍历当前数组q[L ~ R] (从左到右全部遍历一遍),小于等于x的数存到a,大于x的数存到b
4.
② 双指针做法
1.确定分界点x(q[ l ], q[(l + r) / 2], q[r])
2. 定义左右指针L和R分别指向左右
3. 当L <= x时,L继续往右走,R > x时,R继续往左走,L > x时, L停下,R <= x时,R停下,如果只有一个停下的时候另一个继续走,当两个都停下时,swap交换两个值,两个指针都向前移动一位,并继续走直至两指针不能再走,记下L 和 R 的值(此过程中两指针会相互穿过)
4. 此时的数组,左边到L <= x, R到右边 > x
例1:785. 快速排序
给定你一个长度为 nn 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在 1∼10^9 范围内),表示整个数列。
输出共一行,包含 n 个整数,表示排好序的数列。
1≤n≤100000
5 3 1 2 4 5
1 2 3 4 5
#include <iostream> using namespace std; const int N = 1e5 + 10; int n; int q[N]; void quick_sort(int q[], int l, int r) { if (l >= r) return; int x = q[(l + r) / 2], i = l - 1, j = r + 1; while (i < j) { do i ++; while (q[i] < x); do j --; while (q[j] > x); if (i < j) swap (q[i], q[j]); } //左区间 quick_sort(q, l, j); //右区间 quick_sort(q, j + 1, r); } int main () { cin >> n; for (int i = 0; i < n; i ++) scanf ("%d", &q[i]); quick_sort (q, 0, n - 1); for (int i = 0; i < n; i ++) printf ("%d ", q[i]); return 0; }
步骤:
1.确定分界点mid = (l + r) / 2
2.递归
3.合并
例1:787. 归并排序
给定你一个长度为 n 的整数数列。
请你使用归并排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在 1∼10^9 范围内),表示整个数列。
输出共一行,包含 n 个整数,表示排好序的数列。
1≤n≤100000
5 3 1 2 4 5
1 2 3 4 5
#include <iostream> using namespace std; const int N = 1e5 + 10; int q[N], temp[N]; void merge_sort(int q[], int l, int r) { if (l >= r) return; int mid = l + r >> 1; merge_sort(q, l, mid), merge_sort(q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i <= mid && j <= r) { if (q[i] <= q[j]) temp[k ++] = q[i ++]; else temp[k ++] = q[j ++]; } while (i <= mid) temp[k ++] = q[i ++]; while (j <= r) temp[k ++] = q[j ++]; for (i = l, j = 0; i <= r; i ++, j ++) q[i] = temp[j]; } int main() { int n; cin >> n; for (int i = 0; i < n; i ++) scanf ("%d", &q[i]); merge_sort (q, 0, n - 1); for (int i = 0; i < n; i ++) printf ("%d ", q[i]); return 0; }
例2:788. 逆序对的数量
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出一个整数,表示逆序对的个数。
1≤n≤100000
数列中的元素的取值范围 [1,10^9]
6 2 3 4 5 6 1
5
#include <iostream> using namespace std; typedef long long LL; const int N = 1e5 + 10; int n; int q[N], temp[N]; LL merge_sort (int q[], int l, int r) { if (l >= r) return 0; int mid = l + r >> 1; LL res = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i <= mid && j <= r) { if (q[i] <= q[j]) temp[k ++] = q[i ++]; else { temp[k ++] = q[j ++]; res += mid - i + 1; } } while (i <= mid) temp[k ++] = q[i ++]; while (j <= r) temp[k ++] = q[j ++]; for (int i = l, j = 0; i <= r; i ++, j ++) q[i] = temp[j]; return res; } int main () { cin >> n; for (int i = 0; i < n; i ++) scanf ("%d", &q[i]); cout << merge_sort (q, 0, n - 1); return 0; }
有单调性的一定可以二分,可以二分的不一定有单调性
例1:789. 数的范围
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1
。
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1
。
1≤n≤100000
1≤q≤10000
1≤k≤10000
6 3 1 2 2 3 3 4 3 4 5
3 4 5 5 -1 -1
#include <iostream> using namespace std; const int N = 1e5 + 10; int n, m; int q[N]; int main () { cin >> n >> m; for (int i = 0; i < n; i ++) scanf ("%d", &q[i]); while (m --) { int x; cin >> x; int l = 0, r = n - 1; while (l < r) { int mid = l + r >> 1; if (q[mid] >= x) r = mid; else l = mid + 1; } if (q[l] != x) cout << "-1 -1" << endl; else { cout << l << ' '; int l = 0, r = n - 1; while (l < r) { int mid = l + r + 1 >> 1; if (q[mid] <= x) l = mid; else r = mid - 1; } cout << l << endl; } } return 0; }
四、浮点数二分(不用管边界) 例:790. 数的三次方根
给定一个浮点数 n,求它的三次方根。
共一行,包含一个浮点数 n。
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
−10000≤n≤10000
1000.00
10.000000
#include <iostream> using namespace std; int main() { double x; cin >> x; double l = -10000, r = 10000; while (r - l > 1e-8) { double mid = (l + r) / 2; if (mid * mid * mid >= x) r = mid; else l = mid; } // printf 默认保留6位小数 printf ("%lf\n", l); return 0; }