算法基础之排序与二分

1、排序

1.1、章节思路

在这里插入图片描述
在这里插入图片描述

1.2 快速排序

在这里插入图片描述
暴力做法
在这里插入图片描述

优美做法 一个头指针,一个尾指针。
在这里插入图片描述
直到 i和j相遇为止。 任何时候i左边的数小于等于x,j右边的数大于等于x。当i>x时停止,j<=x时停止。当两个指针都停止时,交换两个数据。
模拟数据
在这里插入图片描述
准备交换。
在这里插入图片描述
交换之后,两个数字向中间移动一位。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

此时i指针前(不包括i)所有数<=3
j指针后(不包括j)所有数>=3

1.2.1 快排模板

在这里插入图片描述

1.2.2 快排死循环

在这里插入图片描述

这样写会出现死循环
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
左l,r【0,-1】return 右l,r【0,1】 与原区间l,r【0,1】一样会陷入死循环。
在这里插入图片描述
因此在递归时,传入的参数含i时,x的值不能为q【l】。

同上 这样写也会出现死循环
在这里插入图片描述

1.2.3 快排最优解

在这里插入图片描述
位运算优先级低于加减
位运算等同于除以2.

1.3 归并排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3.1 归并排序与快速排序时间复杂度证明

在这里插入图片描述
分析归并排序时间复杂度 o(n logn)以2为底
在这里插入图片描述
在这里插入图片描述
快速排序同归并排序 时间复杂度(o(nlogn))
快排每次划分 不一定时n/2,但是期望是n/2,递归层数期望也是logn 因此时间复杂度同归并。

1.3.2 归并排序模板

在这里插入图片描述

2、二分

在这里插入图片描述
在这里插入图片描述

2.1 整数二分

有单调性可以二分,没单调性也可以二分。单调性不是二分的本质。
在这里插入图片描述
根据二分得出不同的点,两个点。则需要两个不同的模板。

二分红色的点 check 是否满足红色性质
满足的话->在红色区间中 l=mid
不满足->不在红色区间中 r=mid-1
在这里插入图片描述
二分绿色的点,check是否满足绿色。
在绿色区间中->r=mid
不在->l=mid+1
在这里插入图片描述

2.1.1 代码

在这里插入图片描述

2.1.2 +1思路 为什么+1?

在这里插入图片描述
l=mid 则加1。

为什么+1。
c++下取整
在这里插入图片描述
在这里插入图片描述
循环一遍之后,没变。进入死循环。
需要+1
在这里插入图片描述

2.1.3 如何选择模板 习题思路

写一个check函数,观察l=mid? mid=(l+r+1)/2
否则执行其它的。不需要思考上述画的红绿图。
check 函数的编写
找到满足x的左边界值。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
r=mid 不需要补齐+1

无解判断:如果数组中不包含x,此时二分出来的是,数组从前到候第一个>x的数值。
在这里插入图片描述
输出l与r均可,循环结束的时候,l与r相等
在这里插入图片描述
找到满足x的有边界值
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1.4 数的范围

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int q[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    while (m -- )
    {
        int x;
        scanf("%d", &x);
		//位置从0开始计算
        int l = 0, r = n - 1; 
        //找x的起始位置
        while (l < r)
        {
            int mid = l + r >> 1;
            //大于等于x的第一个数
            if (q[mid] >= x) r = mid;
            else l = mid + 1;
        }

        if (q[l] != x) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' ';

            int l = 0, r = n - 1;
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                //小于等于x的最后一个数
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }

            cout << l << endl;
        }
    }

    return 0;
}


2.1.5 二分思想

在一个区间内部取二分边界
在这里插入图片描述
每一次选择的区间,选择答案所在的区间去处理。
每次把区间缩小一半,选择答案所在的区间。
当区间长度为1时,就是答案。
二分一定有解。题目可能无解。
举例:
在这里插入图片描述
判断条件为大于等于x,找到了第一个大于x的数。这是二分的结果。但是题目要求等于x,因此题目无解。
根据二分判断出的边界去判断题目有没有解。

2.2 浮点数二分

在这里插入图片描述
r减l为一个很小的数的时候,可以将l或r当做二分的结果。

2.2.1 开平方

在这里插入图片描述
在这里插入图片描述
通过提高精度,防止误差出现
在这里插入图片描述
代码有错误,应该为r=max(1,x);
因为计算0.01开根号时,r=0.01,l=0。取不到0.1。必须让r=1。在习题中有分析
在这里插入图片描述
在这里插入图片描述
经验

在这里插入图片描述
误差要比保留的有效数字位数大2。

3、数的三次方根

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4、逆序对的数量

在这里插入图片描述
在这里插入图片描述
当q[i]>q[j]时,i包括i之后的所有数都大于q[j]
在这里插入图片描述
在这里插入图片描述
数据量是10万
最大逆序数对有几个?
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、求第k个数

用到快速选择算法。
在这里插入图片描述
在这里插入图片描述
对于本题 时间复杂度是o(n) 只需要求半边‘
在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值