一、快速排序(分治)
1.思想
①确定分界点:x=q[l]或mid=q[r]或mid=q[(l+r)/2]
x所取并非索引,而是确确实实的值。
②调整区间。
③递归处理左右区间。
时间复杂度:O(nlogn)
2.模板:
void quick_sort(int q[], int l, int r) {
if (l >= r) //数组无元素 或 只有一个元素,则直接返回
return;
int x = q[l]; //①确定分界点
int 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);
}
注意:
分界点若选择q[l],则递归时应为 quick_sort(q, l, j); quick_sort(q, j + 1, r);
反之,若分界点为q[r],则递归应为 quick_sort(q, l, i-1); quick_sort(q, i, r);
3.题目(785.快速排序)
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10; //常量N,为数组分配足够空间
int n;
int q[N];
void quick_sort(int q[], int l, int r) {
if (l >= r) //数组无元素 或 只有一个元素,则直接返回
return;
int x = q[l]; //①确定分界点
int 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() {
scanf("%d", &n); //整数个数
for (int i = 0; i < n; i++)
scanf("%d", &q[i]); //scanf速度比cin快
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++)
printf("%d ", q[i]);
return 0;
}
二、归并排序(分治)
1.思想
①确定分界点:mid=(l+r)/2
②递归排序left,right
③归并,合二为一
时间复杂度:O(nlogn)
2.模板
void merge_sort(int q[],int l,int r){
if(l>=r) return;
int mid=l+(r-l)/2; //①确定分界点
merge_sort(q,l,mid); //②递归
merge_sort(q,mid+1,r);
int k=0; //存储位置的指针
int i=l,j=mid+1; //初始指针,便于比较大小
while(i<=mid && j<=r){ //③归并
if(q[i]<=q[j])
tmp[k++]=q[i++];
else
tmp[k++]=q[j++];
}
while(i<=mid)
tmp[k++]=q[i++];
while(j<=r)
tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++){ //存入要求数组
q[i]=tmp[j];
}
}
3.题目(787.归并排序)
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10; //常量N,为数组分配足够空间
int n;
int q[N],tmp[N]
void merge_sort(int q[],int l,int r){
if(l>=r) return;
int mid=l+(r-l)/2; //①确定分界点
merge_sort(q,l,mid); //②递归
merge_sort(q,mid+1,r);
int k=0; //存储位置的指针
int i=l,j=mid+1; //初始指针,便于比较大小
while(i<=mid && j<=r){ //③归并
if(q[i]<=q[j])
tmp[k++]=q[i++];
else
tmp[k++]=q[j++];
}
while(i<=mid) //如果左侧有剩余
tmp[k++]=q[i++];
while(j<=r) //如果右侧有剩余
tmp[k++]=q[j++];
for(int i=l,j=0;i<=r;i++,j++){ //存入要求数组
q[i]=tmp[j];
}
}
int main() {
scanf("%d", &n); //整数个数
for (int i = 0; i < n; i++)
scanf("%d", &q[i]); //scanf速度比cin快
merge_sort(q, 0, n - 1);
for (int i = 0; i < n; i++)
printf("%d ", q[i]);
return 0;
}
三、二分查找
1.整数二分
整数二分通常是指在一个整数区间中进行二分查找。与二分查找的区别在于,它通常用于解决一些需要求解整数或离散问题的场景,而不一定是查找一个目标元素。
整数二分的典型应用场景:
①求解函数的零点:例如,给定一个函数 f(x),我们想通过二分法来找到 f(x)=0 的解。
②求解最小/最大满足条件的整数值:例如,给定一个区间和一些条件,找到一个满足条件的最小或最大整数值。
整数二分的步骤:
(1)初始化左右边界:给定一个整数区间 [low, high],初始化 low 和 high 的值。
(2)计算中间元素:计算中点 mid = (low + high) / 2,并通过某些条件来判断是否找到了目标解,或者是否应该调整搜索区间。
(3)根据条件调整搜索区间:类似于二分查找,比较 mid 位置的值,并根据条件来调整 low 或 high。
(4)结束条件:满足某个精度或条件时停止。
//模板一、寻找符合左区间条件的数。
//比如:查询符合条件的最小值
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l,int r){
while(l<r){
int mid=l+(r-l)/2;
if(check(mid))
r=mid; //true---左区间
else l=mid+1; //false--右区间
}
return l;
}
//模板二、寻找符合右区间条件的数。
//比如:查询符合条件的最大值
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l,int r){
while(l<r){
int mid = (l+r+1)/2;
if(check(mid))
l=mid; //true---右区间
else r=mid-1; //false--左区间
}
return 1;
}
题目(789.数的范围)
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n, m, x;
int q[N]
int main() {
scanf("%d %d", &n, &m)
for (int i = 0; i < n; i++) //输入数组
scanf("%d", &q[i])
while (m--) { //检索m次元素
scanf("%d", &x); //每输入一个检索一次存在范围
int l=0,r=n-1;
while (l < r) { //整数二分,查找最小索引
int mid = l + (r - l) / 2;
if (q[mid] >= m)
r = mid;
else
l = mid + 1;
}
//return l; 模板中按理返回l为最小索引,下行直接将l带入不单独返回
if(q[l]!=x) //判断该元素是否在数组内存在
printf("-1 -1");
else{
printf("%d ",l) //输出最大索引
int l=0,r=n-1;
while (l < r) { //整数二分,查找最大索引
int mid = (l+r+1)/2;
if (q[mid] <= m)
l = mid;
else
r = mid - 1;
}
cout<<l<<endl;
}
}
return 0;
}
2.浮点数二分
实现方式:类似于整数二分查找,但是因为浮点数的精度限制,通常需要设定一个误差范围(例如 ε)。在每一步中,通过对区间的中点进行计算,根据函数值的大小关系来缩小区间,直到区间的宽度小于设定的误差阈值。
结束条件:因为浮点数无法完全精确表示,所以通常使用一个预设的误差阈值(如 1e-6)来判断收敛。(一般将误差范围设置为比小数位数多2)
循环条件是重点 while(r-l>eps)
double bsearch_3(double l,double r){
const double eps=1e-6; //定义常数存储 误差;
while(r-l>eps){ //注意循环条件
double mid = (l+r)/2;
if (check(mid))
r=mid;
else
l=mid;
}
return l;
题目
求平凡根。
#include <algorithm>
#include <bits/stdc++.h>
using namespace std;
int main() {
double x;
scanf("%lf", &x); //注意格式问题:double→%lf
double l = 0, r = x, eps = 1e-6;
while (r - l > eps) {
double mid = (l + r) / 2;
if (mid * mid >= x)
r = mid;
else
l = mid;
}
printf("%lf", l);
return 0;
}
四、总结
①快排 与 归并 的实现思想有相似之处,三步走。
②整数二分与平常理解的二分查找(分三类)实现有差别,注意理解。整数二分的两种模板按需使用。
③浮点数二分通过确定误差实现精度要求。