首页 > 其他分享 >排序+二分

排序+二分

时间:2023-01-02 16:55:23浏览次数:24  
标签:二分 int mid while 排序 check

排序+二分

排序

快速排序

基于分治思想

  1. 确定分界点: \(q[l]\) \(q[l + r >> 1]\) \(q[r]\) 随机

    快速排序这道题目的数据已加强,划分中点取左端点或右端点时会超时,改成取中点或者随机值即可

  2. 调整区间:满足x左边的元素都小于等于x,右边的元素都大于等于x(等于x不影响),所以x不一定在中间位置

    初始情况,指针i在最左边的左边一个,指针j在最右边的右边一个的位置

    从左向右移动指针i,直到遇到第一个大于x的数,停下来;从右向左移动指针j,直到遇到第一个小于x的数停下来。

    交换此时指针i和指针j指向的数

    在继续移动指针i和j,直到i和j相遇为止

    指针j前面的数都是小于等于x的,指针i后面的数都是大于等于x的

  3. 递归处理:递归的形式处理左右两段

    两个区间:\([l,j]\) \([j+1 , r]\)

易错点

  1. quick_sort() 函数里面传入的是 q[] , 因此在swap内也要使用 q[];
  2. do - while 循环步进条件 要区分 q[i] 和 q[j];
  3. 递归处理的时间前面的右边界为j , 后面的左边界为 j+1。

快速排序算法模板

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. 确定分界点,\(mid = l + r >> 1\),(快速排序取的是数值,归并排序里面确定的是位置)

  2. 递归排序左边和右边,两边就变成了一个有序的数组

  3. 归并——将两个有序数组合二为一

    双指针法处理两个数列,i指向a数组的0,j指向b数组的0

    如果\(a[i] < a[j]\) 将\(a[i]\) 放入新的数组,\(i++\)

    如果\(a[j] < a[i]\) 将\(a[j]\) 放入新的数组,\(j++\)

    到最后一定是有一个数组已经全部处理完成,还有一个数组没有处理完

    将未处理完的数组全部接到新数组的后面

易错点

  1. 两个区间 \([l, mid]\) \([mid + 1, r]\) \(i = l, j = mid + 1\)
  2. 最后将\(tmp\)放入q的时候要注意条件是 \(i = l, i <= r\)

归并排序算法模板

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

二分

整数二分

有单调性就一定可以二分,但是不具有单调性的题目也一定可以二分,二分的本质不是单调性

在区间上定义了某种性质,该性质在左半边满足,在右半边不满足,左右半边不能相交(整数二分)

二分可以寻找性质的边界,既可以选择不满足的边界,也可以选择满足的边界

时刻保证答案在区间内部

  1. 找中间值\(mid\): \(mid = l + r >> 1\)

  2. 先写一个 \(check()\) 函数,然后判断如何更新,如果是 \(l = mid\) ,就要把 \(mid\) 改成 \(l + r >> 1\)

  3. 每次看更新区间是 \(l = mid\) ( 补上 +1 ) 还是 \(r = mid\)

  4. 找左区间的右边界

    1. \(if( check( mid ) == true )\)

      那么mid就在满足性质的区间里面 , 那么边界答案在 \([mid, r]\) 里面 (包含\(mid\))

      更新:\(l = mid\)

    2. \(if( check( mid ) == false )\)

      那么边界答案在 \([ l , mid - 1 ]\) 里面

      更新:\(r = mid - 1\)

  5. 找右区间的左边界

    1. \(if( check( mid ) == true )\)

      那么边界答案在$ [ l , mid ]$

      更新:\(r = mid\)

    2. \(if( check( mid ) == false )\)

      那么边界答案在$ [ l , mid ]$

      更新:\(l = mid + 1\)

注意点:

  1. 每次选择下一个答案所在的区间

  2. 二分是否有解和题目有关,和二分模板无关,二分是一定有解的,只需要最后判断二分得到的答案和题目要求的答案是否相同即可

给定数列,求元素的起始位置和终止位置

判断元素的起始位置,\(mid\)满足条件,说明起始位置在\(mid\)前面,\(r = mid\)

判断元素的终止位置,\(mid\)满足条件,说明终止位置在\(mid\)后面,\(l = mid\)

整数二分算法模板

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;
}
//边界答案都存在l指向的位置

浮点数二分

四位小数用 1e-6

六位小数用 1e-8

因为是浮点数二分,不需要考虑边界问题

浮点数二分算法模板

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

标签:二分,int,mid,while,排序,check
From: https://www.cnblogs.com/xushengxiang/p/17020155.html

相关文章

  • 排序模板
    目录快速排序堆排序目的在于复习常见的排序题型和实现方式。快速排序时间复杂度:最坏情况为\(O\left(n^{2}\right)\);平均时间复杂度为\(O\left(n\log_{2}n\right)\);......
  • 数据结构 玩转数据结构 7-7 基于二分搜索树的映射实现
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13709 1重点关注1.1使用二叉树实现映射Map详见3.1用二叉树实现映射Map  2......
  • 02二分
    二分问题适用于一个序列,有一个check函数,能够使得序列左边都返回false,右边都返回true,然后我们找的就是这个分界点的时候基本思想两种情况一种是求左半段最后一个元素,......
  • C#.NET 随机排序集合(列表\数组) | 打乱集合(列表\数组)
    直接上代码:///<summary>///重排列表(打乱列表)///</summary>///<paramname="arr"></param>publicstaticList<string>ConfusionArray(List<string>list){......
  • 01排序
    快速排序问题将序列q的l~r区间排序voidquick_sort(intq[],intl,intr)基本思想——分治找一个参考值x通过双指针算法交换使得左半边全部是<=x,......
  • 二分查找
    概念:适用范围:有序数组在顺序查找的基础上改进,每次查找都是用mid时间\(O(logn)\)算法intl=0,r=n-1;while(l<=r){//这里因为用来等号下面就要用减或者加int......
  • C语言基于二叉排序树的学生成绩管理系统
    C语言基于二叉排序树的学生成绩管理系统1、基于二叉排序树的学生成绩管理系统1.1题目简述本次课题要求基于二叉排序树设计和实现一个简易的学生成绩管理系统,功能包含对......
  • 【排序】【数组】LeetCode 75. 颜色分类
    题目链接75.颜色分类思路题目要求按0、1、2的顺序排序,因为数量有限,所以通过两次遍历,分别将0和1交换到合适的位置,这样两次遍历之后,剩下的2就都在尾部了。代码classSol......
  • 【排序贪心】【字符串】LeetCode 179. 最大数
    题目链接179.最大数思路转自宫水三叶大佬的题解对于nums中的任意两个值a和b,我们无法直接从常规角度上确定其大小/先后关系。但我们可以根据「结果」来决定a和......
  • 13.二叉排序树
    引入需求:给定一个数列{7,3,10,12,5,1,9}要求能够高效的完成对数据的查询和添加......