《算法零基础100讲》(第37讲) 排序进阶 - 快速排序
Posted 英雄哪里出来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法零基础100讲》(第37讲) 排序进阶 - 快速排序相关的知识,希望对你有一定的参考价值。
零、写在前面
这是《算法零基础100讲》 专栏打卡学习的第三十七天了。
每天打卡的题,做不出来没关系,因为困难的题涉及知识点较多,后面还是会开放出来的,就像昨天的 最大公约数 那道题今天还是会有,所以不要着急,内容能看懂,能自己分析,能做出简单题,就可以打卡。
在刷题的过程中,总结自己遇到的坑点,写出 「 解题报告 」 供他人学习,也是一种自我学习的方式。这就是经典的帮助他人的同时,成就自己。目前, 「 万人千题 」 社区 每天都会有五六篇高质量的 「 解题报告 」 被我 「 加精 」。如果觉得自己有能力的,也可以来发布你的 「 解题报告 」。千万级流量,你我共同拥有。
一、概念定义
排序问题是最经典的算法问题了,一般一开始学的算法就是排序。但是由于 C/C++ 都有现成的库函数(C里是 qsort、C++里是sort),所以导致很多人就没有学排序算法本身,其实排序算法的思路非常经典,从最简单的学起,对日后学习复杂算法是非常有帮助的。这就是举一反三,粗类旁通的道理。
从今天开始,我们来试着掌握每个排序算法的思路,今天是快速排序。
快速排序,采用的是分治的思想,选择一个基准点,把所有小于基准点的数放到它左边,把所有大于基准点的数放到它右边,如图所示:
图示 | 含义 |
---|---|
■ 的柱形 | 代表尚未排好序的数 |
■ 的柱形 | 代表随机选定的基准数 |
■ 的柱形 | 代表已经排序好的数 |
■ 的柱形 | 代表正在遍历比较的数 |
■ 的柱形 | 代表比基准数小的数 |
■ 的柱形 | 代表比基准数大的数 |
我们发现,首先随机选择了一个 7 作为「 基准数 」,并且将它和最左边的数交换。然后往后依次遍历判断,小于 7 的数为 「 绿色 」 ,大于 7 的数为「 紫色 」,遍历完毕以后,将 7 和 「 下标最大的那个比 7 小的数 」交换位置,至此,7的左边位置上的数都小于它,右边位置上的数都大于它,左边和右边的数继续递归求解即可。
二、题目描述
给你一个 n ( n ≤ 1 0 5 ) n(n \\le 10^5) n(n≤105) 元素的整数数组 nums,请你将该数组升序排列。
三、算法详解
整个算法的执行过程用 quickSort(a[], l, r)
描述,代表 当前待排序数组
a
a
a,左区间下标
l
l
l,右区间下标
r
r
r,分以下几步:
1) 随机生成基准点
p
i
v
o
x
=
P
a
r
t
i
t
i
o
n
(
l
,
r
)
pivox = Partition(l, r)
pivox=Partition(l,r);
2) 递归调用quickSort(a[], l, pivox - 1)
和quickSort(a[], pivox +1, r)
;
3) Partition(l, r)
返回一个基准点,并且保证基准点左边的数都比它小,右边的数都比它大;Partition(l, r)
称为分区。
四、源码剖析
void Swap(int *a, int *b)
int tmp = *a;
*a = *b;
*b = tmp;
int Partition(int a[], int l, int r)
int i, j, pivox;
int idx = l + rand() % (r - l + 1); // (1)
pivox = a[idx]; // (2)
Swap(&a[l], &a[idx]); // (3)
i = j = l + 1; // (4)
//
while( i <= r ) // (5)
if(a[i] < pivox) // (6)
Swap(&a[i], &a[j]);
++j;
++i; // (7)
Swap(&a[l], &a[j-1]); // (8)
return j-1;
//递归进行划分
void QuickSort(int a[], int l, int r)
if(l < r)
int mid = Partition(a, l, r);
QuickSort(a, l, mid-1);
QuickSort(a, mid+1, r);
int* sortArray(int* nums, int numsSize, int* returnSize)
QuickSort(nums, 0, numsSize-1);
*returnSize = numsSize;
return nums;
- ( 1 ) (1) (1) 随机选择一个基准;
- ( 2 ) (2) (2) pivox 代表基准值;
- ( 3 ) (3) (3) 将基准和最左边的值交换;
- ( 4 ) (4) (4) i i i 和 j j j 是两个同步指针,都从 l + 1 l+1 l+1 开始; j − 1 j-1 j−1 以后的数都是大于等于 基准值 的;
- ( 5 ) (5) (5) 开始遍历整个排序区间, i i i 一定比 j j j 走的快,当 i i i 到达最右边的位置时,遍历结束;
- ( 6 ) (6) (6) 如果比基准值小的,交换 a [ i ] a[i] a[i] 和 a [ j ] a[j] a[j],并且自增 j j j ;
- ( 7 ) (7) (7) 每次遍历 i i i 都需要自增;
- ( 8 ) (8) (8) 第 j j j 个元素以后都是不比基准值小的元素 ;
五、推荐专栏
六、习题练习
序号 | 题目链接 | 难度 |
---|---|---|
1 | 最小时间差 | ★☆☆☆☆ |
2 | 有序数组的平方 | ★☆☆☆☆ |
3 | 优势洗牌 | ★★☆☆☆ |
4 | 救生艇 | ★★☆☆☆ |
以上是关于《算法零基础100讲》(第37讲) 排序进阶 - 快速排序的主要内容,如果未能解决你的问题,请参考以下文章