AcWing算法基础课----基础算法(一) 笔记 (排序 + 二分)

对应习题题解

传送门

排序

学习方法:

  1. 学习代码的主要思想
  2. 模板理解+记忆
  3. 刷题(模板题默写3~5遍)

快速排序

快排 --> 基于分治

y总快排讲解

思想:

  1. 确定分界点(通常为 q[l]q[(l+r)/2]q[r]随机(不常用)
  2. ★调整区间☆ —>划分为两个区间,第一个区间所有数都小于等于x,第二个区间所有数都大于等于x
  3. 递归处理左右两端

模板:

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);
}

经常使用的快排思想:

  1. 快速排序其实是在冒泡排序的基础上做出的一个改进.

  2. 快速排序算法利用的是一趟快速排序,基本内容是选择一个数作为准基数,然后利用这个准基数将遗传数据分为两个部分,第一部分比这个准基数小,都放在准基数的左边,第二部分都比这个准基数大,放在准基数的右边.

  3. 接下来这两部分都是用快排(可以使用递归的方法)完成从小到大的排序.

  4. 冒泡排序的原理:从第一个数开始,依次往后比较,如果前面的数比后面的数大就交换,否则不作处理。这就类似烧开水时,壶底的水泡往上冒的过程。

模板

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);  //递归右边
}

归并排序

基本思想:分治

排序算法稳定概念:序列中两个相等的值在排序后位置不变则为稳定

思想:

  1. 确定分界点 mid=(l+r)/2
  2. 递归排序左右
  3. ★(双路)归并---->合二为一 ☆

双指针算法

模板

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];
}

二分

整数二分

二分本质二分某区间中满足某种性质的边界点(边界。一个区间,左边不满足一个性质,右边满足这个性质,二分寻找这个性质的边界。 )

注意:

  • 单调可二分,二分未必单调
  • 二分一定有解

分类:

  1. 若要找到蓝色箭头指向的点

在这里插入图片描述

mid=l+r>>1;
if(check(mid)) r=mid;//mid满足性质,边界存在区间为[l,mid];
else l=mid+1; //mid不满足性质,边界存在区间为[mid+1,r];
  1. 若要找到红色箭头指向的点

在这里插入图片描述

找中间值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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值