12. 希尔,堆,归并,快速排序
阅读信息
约 811 个字 5 分钟 本页总访问量:加载中... 次
:banjo:Class Twelve
排序
简单排序:
- 选择排序 seection:每次找最大的元素,放在末尾
- 交换排序 exchange:从头到尾遍历,看两个相邻元素是否符合,位置不对则交换(冒泡)
- 插入排序 insertion:每次之前的元素都排好,插入一个元素使其保持排序的特性。 最坏时间复杂度和平均时间复杂度都是 O(n^2) 但最好时间复杂度不同,选择排序为 O(n^2),冒泡排序可通过标签优化,插入排序最优为 O(n)。
inversion 逆序对:大的在前小的在后,成为一对逆序对。 如果长度为 n 的序列,最多 n(n-1)/2 个逆序对,平均约 nn/4 个逆序。 相邻两个元素对调,改变一个逆序对。 排序算法突破:跳着比较 希尔排序:分组比较+插入排序
分治法:归并排序 T(n)=2T(n/2)+Cn => T(n)=O(nlogn)
快速排序:选择 pivot,将所有元素分为比它小和比它大。
堆排序:构建树,每次选出最大后。每层只有一个元素可能是第二大。
桶排序:
基数排序:先按个位数放入不同的桶,排成序列。再按十位数放入不同的桶,再按百位数……最后排成从小到大的序列。 循环的次数等于位数。
排序的稳定性:相等的元素排序前后顺序是不是相同。
插入排序对输入顺序敏感.如果输入数据基本排好,则排序时间短.
希尔排序
跨区域比较:分组
将数据间隔分组,每组排序 减少组数,再将每组排序 继续减少分组,直到只分一组,完成排序
若数据基本有序,插入排序时间接近线性. 分组多,单组时间复杂度低;分组少时,数据接近有序,时间线性. 开始时分 k 组,组内有序的时间复杂度为全部有序的 1/k.
C | |
---|---|
按step = n / 2; step > 0; step /= 2
分组,最坏时间复杂度:\(O(N^2)\)
Hibbard's Increment Sequence: 按\(step=2^k-1\)分组
堆排序
方法 1(不好)
C | |
---|---|
堆排序:
C | |
---|---|
时间复杂度:\(N\log N-N\log\log N\)
归并排序(分治法)
先分别排序,再 merge. 时间复杂度: 分成两组:\(O(1)\) 递归将两组分别排序:\(2T(N/2)\) merge: \(O(N)\)
quicksort: merge 的步骤减小时间.分组时选择 pivot,将所有元素分成比 pivot 小和比 pivot 大的两组. 这样分组不一定是 n/2 的两组,时间复杂度为\(T(i)+T(N-i)\)
时间复杂度比较 和\(T(n)=2T(n/2)+Cn\)比较 \(T(n)=3T(n/2)+Cn\): 更复杂 \(T(n)=2T(n/3)+Cn\): 更简单
临时数组
- 外部统一申请,作为参数传入
- 每次合并新申请空间
- 原地排序,不额外申请空间(复杂)
- 两个相同长度的数组,一个排序时占用另一个数组
- 三个已排序数组合并,用堆(??)
快速排序
关键:分组时选择 pivot,将所有元素分成比 pivot 小和比 pivot 大的两组.
难点:
- 怎么选择 pivot
- 原地排序,不额外申请数组
选择 pivot 的方法:
- 选第一个(不好)
- 随机选择(仍不好)
- 头,尾,中间值三个中选中间值
- 五等分点选中间值
- 随便选一个分组,看两组是否均匀(可 ¼ 为界),若均匀则继续进行,若不均匀则重来(按期望为做两次)
蒙塔卡洛:做特定次数,做完后停止,不管是否符合最佳条件 拉斯维加斯:按特定要求,若一直不符合要求则一直进行
但是,数组小时选 pivot 快排效率低. 在数组规模小于阈值时,直接使用简单排序.
怎么原地分类?
两边扫描
基准元素放在最后 指针 i 放在开头,比 pivot 小时向右走,>=时停下 指针 j 放在结尾,比 pivot 大时向左走,<=时停下 都停下时交换 i 和 j 的值 直到 i==j 或 i>j,将 pivot 放在最终位置
一边扫描
一个指针向右走,左边是小的一堆和大的一堆 如果指针处大,继续向右走 如果指针处小,和大的那堆的第一个元素交换(需要标记大的那堆的第一个元素位置)
荷兰旗问题 三种数据排序
一个指针向右走,左边是 R 的一堆,G 的一堆,B 的一堆 如果指针处是 B,继续向右走 如果指针处是 G,和 B 堆的第一个交换 如果指针处是 R,指针处元素放在 G 堆第一个位置,G 堆第一个放在 B 堆第一个位置,B 堆第一个放在指针位置
指针移动要求:维护扫过的区域符合要求. 双边扫描:左指针左侧都小,右指针右侧都大 一边扫描:指针左侧是分好的堆