对应习题题解
排序
学习方法:
- 学习代码的主要思想
- 模板理解+记忆
- 刷题(模板题默写3~5遍)
快速排序
快排 --> 基于分治
y总快排讲解
思想:
- 确定分界点(通常为
q[l]
、q[(l+r)/2]
、q[r]
、随机(不常用)
) - ★调整区间☆ —>划分为两个区间,第一个区间所有数都小于等于x,第二个区间所有数都大于等于x
- 递归处理左右两端
模板:
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];
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);
}
经常使用的快排思想:
-
快速排序其实是在冒泡排序的基础上做出的一个改进.
-
快速排序算法利用的是一趟快速排序,基本内容是选择一个数作为准基数,然后利用这个准基数将遗传数据分为两个部分,第一部分比这个准基数小,都放在准基数的左边,第二部分都比这个准基数大,放在准基数的右边.
-
接下来这两部分都是用快排(可以使用递归的方法)完成从小到大的排序.
-
冒泡排序的原理:从第一个数开始,依次往后比较,如果前面的数比后面的数大就交换,否则不作处理。这就类似烧开水时,壶底的水泡往上冒的过程。
模板
void Quick_sort(int *dp, int l, int r) // l,r为左右边界
{
int i = l;
int j = r;
int mid = dp[l];
if (i >= j) return;
while (i < j) {
while (i < j && dp[j] >= mid) j--;
dp[i] = dp[j];
while (i < j && dp[i] <= mid) i++;
dp[j] = dp[i];
}
dp[i] = mid;
Quick_sort(dp,l, i - 1); //递归左边
Quick_sort(dp,i + 1, r); //递归右边
}
归并排序
基本思想:分治
排序算法稳定概念:序列中两个相等的值在排序后位置不变则为稳定
思想:
- 确定分界点
mid=(l+r)/2
- 递归排序左右
- ★(双路)归并---->合二为一 ☆
双指针算法
模板
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 tot=0;
int i=l;
int j=mid+1;
while(i<=mid&&j<=r){
if(q[i]<=q[j]) dp[tot++]=q[i++];
else dp[tot++]=q[j++];
}
while(i<=mid) dp[tot++]=q[i++];
while(j<=r) dp[tot++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++) q[i]=dp[j];
}
二分
整数二分
二分本质:二分某区间中满足某种性质的边界点
(边界。一个区间,左边不满足一个性质,右边满足这个性质,二分寻找这个性质的边界。 )
注意:
- 单调可二分,二分未必单调
- 二分一定有解
分类:
- 若要找到蓝色箭头指向的点
mid=l+r>>1;
if(check(mid)) r=mid;//mid满足性质,边界存在区间为[l,mid];
else l=mid+1; //mid不满足性质,边界存在区间为[mid+1,r];
- 若要找到红色箭头指向的点
找中间值mid=(l+r)/2
,并判断中间值是否满足该性质
mid=l+r+1>>1;
if(check(mid)) l=mid;//mid满足性质,边界存在区间为[mid,r];
else r=mid-1; //mid不满足性质,边界存在区间为[l,mid-1];
二分模板一共有两个,分别适用于不同情况。
算法思路:假设目标值在闭区间[l, r]
中, 每次将区间长度缩小一半,当l = r
时,我们就找到了目标值。
-
版本1
当我们将区间[l, r]
划分成[l, mid]
和[mid + 1, r]
时,其更新操作是r = mid
或者l = mid + 1
;,计算mid
时不需要加1 -
版本2
当我们将区间[l, r]
划分成[l, mid - 1]
和[mid, r]
时,其更新操作是r = mid - 1
或者l = mid
;,此时为了防止死循环,计算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;
}
浮点数二分
模板
思路同整数二分
经验:若题目让我们保留小数点后n位,则eps=1e-(n+2)
更好
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r){
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps){
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
//法二 直接循环100次
double bsearch_3(double l, double r){
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
for(int i=0;i<100;i++){
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}