基础算法 快速排序、归并排序、二分答案
基础算法 快速排序、归并排序、二分答案
一、快速排序
步骤:
-
确定分界点:
q[l]
q[r]
q[(l + r) / 2]
随机
-
调整区间,从小到大为例:|------ \(\le x\)--|----------------\(\ge x\)---------------|
-
递归处理左右两段
调整划分区间的几种方法:
- 简单方法(0),额外开辟两个空间
a[]
b[]
,\(\le x\) 的放a[]
\(\ge x\) 放b[]
,最后合并回q[]
。 - 方法(1),利用两个双指针
i
j
,i
向右移动直到q[i]
\(\ge x\),j 向左一移动直到q[j]
\(\le x\),然后swap(q[i], q[j])
,重复以上操作直到i
j
相遇。说明:任何时刻,i
左边的数都小与x,j
右边的数都大于x。 - 方法(2),利用两个双指针,
i
指向q[l]
,此时q[l]
空着,j
从右向左移动,直到q[r]
\(\le x\) ,q[i++] = q[j]
,然后i
开始从左向右移动,重复以上操作,直到i == j
,最后q[i] = x
。
时间复杂度:
- 期望左右划分为\(\frac{n}{2}\) ,则一共用n除以2,\(\log _2n\)次,所以一共有\(\log _2n\)层。
- 每层处理的复杂度为\(O\left(n\right)\)。
- 则一共的复杂度为\(O\left(nlogn\right)\)。
代码模板以及边界问题
方法(1):
注意边界问题,分别用 j 和 i 划分
void quick_sort(int *q, int l, int r) {
if (l >= r) return ;
int i = l - 1, j = r + 1, x = q[(l + r) >> 1]; // q[l]
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); // 以j划分
}
void quick_sort(int *q, int l, int r) {
if (l >= r) return ;
int i = l - 1, j = r + 1, x = q[(l + r + 1) >> 1]; // q[r]
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, i - 1), quick_sort(q, i, r); // 以i划分
}
二、归并排序
步骤:
- 确定边界
mid = (l + r) >> 1
- 递归排序
left
right
- 合并
时间复杂度:
- 左右划分为\(\frac{n}{x}\) ,则一共用n除以x,\(\log_x n\)次,所以一共有\(\log_x n\)层。
- 每层处理的复杂度为\(O\left(n\right)\)。
- 则一共的复杂度为\(O\left(n\log_xn\right)\)。
代码模板
void merge_sort(int *num, int l, int r) {
if (l >= r) {
return ;
}
int mid = (l + r) >> 1;
merge_sort(num, l, mid);
merge_sort(num, mid + 1, r);
int k = 0, x = l, y = mid + 1;
while (x <= mid && y <= r) {
if (num[x] <= num[y]) temp[k++] = num[x++];
else temp[k++] = num[y++];
}
while (x <= mid) temp[k++] = num[x++];
while (y <= r) temp[k++] = num[y++];
for (int i = l, j = 0; i <= r; i++, j++) {
num[i] = temp[j];
}
}
自己原来学的方法
void merge_sort(int *num, int l, int r) {
if (r - l <= 1) {
if (r - l == 1 && num[r] < num[l]) {
int t = num[r];
num[r] = num[l];
num[l] = t;
}
return ;
}
int mid = (l + r) >> 1;
merge_sort(num, l, mid);
merge_sort(num, mid + 1, r);
int p1 = l, p2 = mid + 1, k = 0;
while (p1 <= mid || p2 <= r) {
if (p2 > r || (p1 <= mid && num[p1] <= num[p2])) temp[k++] = num[p1++];
else temp[k++] = num[p2++];
}
for (int i = l, j = 0; i <= r; i ++, j ++ ) num[i] = temp[j];
return ;
}
借助库函数申请空间malloc
和 深度拷贝合并memcpy
,更快一些
void merge_sort(int *num, int l, int r) {
if (r - l <= 1) {
if (r - l == 1 && num[r] < num[l]) {
int t = num[r];
num[r] = num[l];
num[l] = t;
}
return ;
}
int mid = (l + r) >> 1;
merge_sort(num, l, mid);
merge_sort(num, mid + 1, r);
int p1 = l, p2 = mid + 1, k = 0;
int *temp = (int *)malloc(sizeof(int) * (r - l + 1));
while (p1 <= mid || p2 <= r) {
if (p2 > r || p1 <= mid && num[p1] <= num[p2]) temp[k++] = num[p1++];
else temp[k++] = num[p2++];
}
memcpy(num + l, temp, sizeof(int) * (r - l + 1));
free(temp);
return ;
}
三、二分
整数二分
二分本质不是单调性,是删除不存在答案的区间
|--------------------|x|-|y|-----------------------------|
找到一个性质,将待求解区间一分为二
情况一:\(mid = \frac{l + r + 1}{2}\),1111111100000000,求最后一个1
if(check(mid))
:
true
: 表明mid在1里面,答案区间在:[mid ~ r]
,令l = mid
。flase
: 表明mid在0里面,答案区间在:[l ~ mid - 1]
,令r = mid - 1
。
问题:为什么\(mid = \frac{l + r + 1}{2}\) ,要额外加1
解释:由于C/C++默认向下取整,当 l = r - 1
,令l = mid
会进入死循环 l = l
。
情况二: \(mid = \frac{l + r}{2}\),000000001111111,求第一个1
if(check(mid))
:
true
: 表明mid在1里面,答案区间在:[l ~ mid]
,令r = mid
。flase
: 表明mid在0里面,答案区间在:[mid + 1, r]
,令l = mid + 1
。
再次强调:二分本质不是单调性,是删除不存在答案的区间
代码模板:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r) {
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r) {
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
浮点数二分
例:求一个数的平方根
double bin_sqrt(double x) {
double l = 0, r = max(1.0, x);
while (r - l > 1e-8) {
double mid = (l + r) / 2;
if (mid * mid > x) r = mid;
else l = mid;
}
return l;
}
三、简单练习
快速排序:
归并排序:
二分:
更多等待更新。。。。。。