首页 > 编程语言 >算法学习笔记(1)——快速排序

算法学习笔记(1)——快速排序

时间:2022-12-09 21:47:09浏览次数:60  
标签:int 交换 while 笔记 ++ 算法 数组 排序 指针

快速排序

算法思想

快排的思想是基于分治法,其思路是:

  • 确定分界点:给定要排序的数组q,先在数组中找一个数作为分界点值x,分界点可以取左边界值x = q[l],可以取右边界值x = q[r],可以取数组中间的数x = q[l + r >> 1],也可以随机取一个。
  • 调整区间:将数组划分成两个区间,使得左半部分所有的数都<= x,右半部分所有的数都>= x
  • 递归处理左右两个区间。

快排有多种实现方法,在y总的模板里,分界点的位置不一定是x,因为x参与交换之后仍然会被留在左右区间中的一个里。

注意点1:指针移动的判断不带等号

使用两个指针ij分别指向要处理的区间的左右两侧,每次向中间移动。只要q[i] < x成立就说明i位置的数在左侧区间是合理的,所以i ++,直到q[i] >= x停下来。接下来去移动j,只要q[j] > x成立就说明j位置的数在右侧区间是合理的,所以j --,直到q[j] <= x停下来。

这里考虑一个边界问题,为什么移动ij指针的条件是q[i] < xq[j] > x,而不是q[i] <= xq[j] >= x?因为如果选取的x是数组里最大的数,那么一直都满足q[i] <= x,所以i会一直++发生越界都不会停下来。同理,如果选取的x是数组里最小的数,那么一直都满足q[j] >= x,所以j会一直发生越界都不会停下来。

注意点2:在交换前检查指针相对位置

当两个指针都停下来之后,这一对数都是错位的,所以把它们交换一下,交换完成之后q[i] < x并且q[j] > x,下一轮就可以让ij(只要满足i < j)继续对向移动了。

这里考虑一个边界问题,试想q[i]q[j]i == j - 1时停下来做交换的场景,交换完成之后ij会各自前进(i ++, j --)一步,形成i > j(具体是i == j + 1)的不合法局面,这时候就不应该做交换了,所以在swap之前需要再判断一次i < j

注意点3:使用do-while而不是while循环

指针ij初始化为数组两侧外一个元素,即i = l - 1j = r + 1,然后在数组中使用do-while循环每次先进行一次指针的移动,再去看循环条件。

这里考虑一个边界问题,为什么不能让i = lj = r然后使用while循环代替do-while循环?因为如果数组中存在重复的数字,那么某一轮可能存在ij都指向重复的数字,并且划分数字x也是这个数字,那么while (q[i] < x)while (q[j] > x)判断不成立不会进入,又因为q[i] = q[j] = x,交换它们之后这个局面仍然不会改变,所以要使用do-while循环,确保每次两个指针都至少会移动一步,以保证上一次交换的结果能被走掉。

注意点4:选取数组中间的数字作为划分值

与其选取左右两端的数作为划分值,不妨选取数组中间的数字。

这里考虑一个边界问题,如果数组已经是有序的了,那么选取数组开头或者结尾的数字就意味着每次将长度为 \(n\) 的数组划分成了长度为 \(1\) 和 \(n-1\) 的两段,这时候快速排序递归处理的子问题规模还是这么大,总的时间复杂度就达到了 \(O(n^2)\),为了避免这种情况,要选取数组中间的数字作为划分值。

注意点5:使用[l, j]作为区间左半边而不是[l, i]

在快排一轮的处理结束后,递归处理的两个子区间应该是[l, j][j + 1, r]而不是[l, i][i + 1, r]

这里考虑一个边界问题,试想q[i]q[j]i == j - 1时停下来做交换的场景,交换完成之后i和j会各自前进(i ++, j --)一步,形成i > j(具体是i == j + 1)的不合法局面。在这个局面下,满足性质<= x的区间是[l, j]而不是[l, i],因此划分的两个区间是[l, j][j + 1, r]

题目链接:AcWing 785. 快速排序

时间复杂度:\(O(N\log N)\)

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int q[N];

void quick_sort(int a[], 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);
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> q[i];
    quick_sort(q, 0, n - 1);
    for (int i = 0; i < n; i ++ ) cout << q[i] << ' ';
    puts("");
    return 0;
}

标签:int,交换,while,笔记,++,算法,数组,排序,指针
From: https://www.cnblogs.com/I-am-Sino/p/16970062.html

相关文章

  • docker学习笔记
    Docker&CKA学习笔记第一章、容器存在的意义、优势、docker介绍1.1容器存在的意义1、上线流程繁琐开发->测试->申请资源->审批->部>测试等环节2、资源利用率低......
  • 小林笔记【面试】
    小林笔记【面试】​​前言​​​​推荐​​​​小林笔记【面试】​​​​最后​​推荐​​https://xiaolincoding.com/​​小林笔记【面试】​​操作系统笔记【面试】​​​......
  • crypto-gmsm国密算法库
    crypto-gmsm国密算法库一、开发背景crypto-gmsm国密算法库是国密商密算法(SM2,SM3,SM4)工具类封装,国产密码算法(国密算法)是指国家密码局认定的国产商用密码算法,目前主要使用......
  • 代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表
    203. 移除链表元素tag:#链表leetcode地址:203. 移除链表元素代码:functionremoveElements(head:ListNode|null,val:number):ListNode|null{constnewH......
  • hutools密码算法库
    hutool密码算法库一、开发背景Hutool针对BouncyCastle做了简化包装,用于实现国密算法中的SM2、SM3、SM4。国密算法工具封装包括:非对称加密和签名:SM2摘要签名算法:SM3......
  • 统计文件夹大小并排序
    linux:du-sh*2>/dev/null|sort-hrWindows(cygwin/git...):du-sh*2>NUL|sort-hr注意这个sort要用git带的sort.exe而不是System32下面的sort......
  • 【主色提取】模糊C均值(FCM )聚类算法和彩色图像快速模糊C均值( CIQFCM )聚类算法
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 学习笔记281—word不能插入公式
    点击辅助功能在文档中点击状态栏下辅助功能。 点击转换在辅助功能界面,点击转换。 点击公式点击公式,这样就可以插入公式。END方法/步......
  • 安卓APP源码和设计报告——麻雀笔记
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 软件技术基础学习笔记(4)——成立小组
    软件技术基础学习笔记(4)——成立小组这个作业属于哪个课程<首页-22软件基础-浙江理工大学-班级博客-博客园>这个作业的目标<成立小组,讨论并确定大作业的......