第一章 基础算法(一)快速排序、归并排序、整数二分、浮点数二分

梦开始的地方 

目录

 快速排序----分治

时间复杂度分析

稳定分析

 调整区间的做法:

模版

归并排序----分治

时间复杂度分析

稳定分析

归并的方法    合二为一

 模版:

整数二分  二分查找

模版:

浮点数二分

模版


 快速排序----分治

时间复杂度分析

平均复杂情况=O(nlogn)   用数学期望证明此处省略

稳定分析

如果原序列中有两个相等的数,排序后这两个数并不交换位置,那么称这个排序为稳定的排序

快排不稳定:将快排变稳定的方法把相同的元素开二元组,下标作为区分条件

①确定分界点:x

1.左边界 q[l]

2.右边界 q[r]

3.中间位置  q[(l+r)/2]

4.随机

②调整区间     ☆

所有小于x在左面大于x在右边

   

③递归处理左右两端

 调整区间的做法:

①暴力做法: 开两个数组a[] b[],扫描q[],小于x插入a大于x插入b,再将a、b插入q

空间复杂度比较高,要开很多数组

②优美做法  开两个指针i,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];  
// l+r的值右移1位,相当l+r的值除以2取整。取中间值为边界
    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 + r >> 1],即取中间位置的元素作为枢轴元素。

如果将选择的枢轴元素改为q[l],即取第一个元素作为枢轴元素,可能导致算法的性能下降。这是因为如果原始数组已经是有序或接近有序的,如果每次选择第一个元素作为枢轴元素,就会导致分区的不平衡,因此快速排序算法的时间复杂度可能会增加。

当原始数组接近有序时,选择中间位置的元素作为枢轴元素可以尽可能地保证分区的平衡,从而使得算法的时间复杂度更接近O(nlogn)。

如果将选择的枢轴元素改为q[l],相当于总是选择第一个元素作为枢轴元素,而不管原始数组的有序程度。这种情况下可能会出现分区的不平衡,导致算法的时间复杂度变为O(n^2),从而导致超时。

例题:活动 - AcWing

归并排序----分治

时间复杂度分析

最好=最坏=平均复杂情况=O(nlogn)

共有logn行数(log以2为底)

 每一行都是O(n) 如第二行2*O(n/2)=O(n)

总复杂度为n*O(logn)=O(nlogn)

稳定分析

归并排序是稳定的

①确定分界点  mid=(l+r)/2

②递归排序 left、right

③归并---合二为一  ☆

归并的方法    合二为一

归并方法,假设有两个有序数组a b

再开一个数组q,比较两个指针指向的值,哪一个小就插入哪一个,并将指针后移,重复上述过程,当某一个数组元素全被插入以后,另一个数组剩余部分直接插入q数组

注:当两个数的值相等时,课随意插入某一个,但一般插入第一个数组的元素

 模版:

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 k = 0, 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 (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}


例题:活动 - AcWing

整数二分  二分查找

思想:将一个有序的序列每次都分成两个序列,查找中间值是否满足要查找的性质,改变左右指针的值缩小查找范围

情况一:满足左边的性质,也可以理解为查找一个数最大的右边界(假设序列从左往右由小变大)

情况二:满足右边的性质,也可以理解为查找一个数最小的左边界(假设序列从左往右由小变大)

两种情况分别对应以下模版的bsearch_1和bsearch_2

模版:

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

例题:活动 - AcWing

浮点数二分

由于整数二分需要处理是否能整除问题因此要分两种情况,浮点数可直接除,循环的条件可以改为l-r<1e-6等,当l和r无限接近就可认为查找完毕

模版

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

例题:活动 - AcWing

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值