本文归纳了三种排序算法模板 + 二分查找模板,为玩算法竞赛的同学提供思路。本苟蒻发文,有任何不足的欢迎大佬们斧正~ˋ( ° ▽、° )
时间复杂度:O(n log 2 n \log_2n log2n)
快排结构:
- 确定分界点:有四种,分别是q[ l ]、q[ (l+r)/2 ]、q[r]、随机
- 调整范围:找到分界点后,以分界点划分为左边、右边两段
- 递归处理左右两段
#include
using namespace std;
const int N = 1e5+10;
int nums[N];
void quick_sort(int l, int r){
if(l >= r) return;
srand(time(NULL));
int middle = q[l]; //分界点
int i = l-1, j = r+1; //-1和+1的原因是因为下面要进行do while
while(i < j){
do i++; while(nums[i] < middle);
do j--; while(nums[j] > middle);
if(i < j) swap(nums[middle], nums[i]);
}
//递归处理
quick_sort(l, j);
quick_sort(j+1, r);
}
int main(){
int n = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &nums[i]);
quick_sort(0, n-1);
for(int i = 0; i < n; i++)
if(i != n-1) printf("%d ", nums[i]);
else{
printf("%d\n", nums[i]);
}
return 0;
}
/**
* 关于middle分界点
*/
//随机化版本
srand((unsigned)time(NULL));
int middle = nums[rand()%(r-l+1) + l];
//取中值版本
int middle = nums[(l + r)/2];
时间复杂度:O(nlogn)
归并结构:
- 确定分界点:mid = (l + r) / 2
- 递归排序:分别递归 left、right
- 归并:合二为一(上图就是该过程的演示,两个虚拟指针分别指向它们组的最小值,再比对两个虚拟指针值得大小,较小的那一块记录到 res 数组中。如果其中一组循环完了而另外一组没循环完,那么就直接把没循环完的放到res中)
#include
using namespace std;
const int N = 100010;
int n;
int q[N],temp[N];
void merge_sort(int l,int r){
if(l >= r) return;
int mid = l + r >> 1;
merge_sort(l,mid), merge_sort(mid+1,r);
int i = l,j = mid + 1, k = 0;
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++];
//最后还要把 temp 里的排序好的放回 q 数组 l 的位置
for(int i = l,j = 0;i <= r;i++,j++) q[i] = temp[j];
}
int main(){
scanf("%d",&n);
for(int i = 0;i < n;i++) scanf("%d",&q[i]);
merge_sort(0,n-1);
for(int i = 0;i < n;i++) printf("%d ",q[i]);
return 0;
}
时间复杂度:O(n log 2 n \log_2n log2n)
自定义排序:
- 重载:通过重在 < 、 > 等之类运算符
- 函数重载:自定义bool类型的函数判断
- 匿名函数:直接在 sort 函数的第三个参数位置实现匿名函数
#include
using namespace std;
//第一种
struct Person{
int id , age;
bool operator<(const Person& p) const{
return p.age > age;
}
};
//第二种
bool cmp(int a, int b){
return a/2 < b+2;
}
int main(){
Person person[20];
int nums1[]={1,5,6,8,2,7,9,10};
int nums2[]={20,11,6,8,28,7,9,32};
for(int i = 0; i < 20; i++)
person[i] = {i, (i+8)/2};
sort(person, person+20); //第一种
sort(nums1, nums1+8, cmp); //第二种
sort(nums2, nums2+8, [](int a, int b){
return a+b < b;
}); //第三种
for(int i = 0; i < 20; i++)
printf("%d ", person[i].age);
puts("");
for(int i = 0; i < 20; i++)
printf("%d ", nums1[i]);
puts("");
for(int i = 0; i < 20; i++)
printf("%d ", nums2[i]);
puts("");
return 0;
}
至于 check(mid) 是自己实现的函数,该条件称为分界点,二分出的分界点应为题目问的分界点。如果写完之后发现是l = mid,那么在计算mid时需要加上1,否则如果写完之后发现是r = mid,那么在计算mid时不能加1。
为什么模板二会有死循环?
答: 我们先不考虑别的问题,只考虑二分的最后一步,R =L+1的情况,如果计算mid时不加1,那么mid=(L+R)/2向下取整后就是L,加入此时发现check(mid)是true的情况,那么代码执行到L = mid这一步,然后继续循环——等等,那这L不就没改嘛!