红魔咖啡馆

头发越掉越多,头发越掉越少

0%

【算法】排序

排序

直接插入排序

即将每个元素一次插入到有序的部分

一开始将第一个数作为有序区

直接插入排序即按顺序对每个元素与有序区的最后一个元素比较,若比其小(默认升序),则将该元素存入temp,让temp和有序区的各个元素比较,决定插入位置,插入后有序区扩充

我们采取倒着比较,可以实现同时比较与移动

  • 时间复杂度:\(O(n)\)(最好), \(O(n^2)\)(最坏), \(O(n^2)\)(平均)

  • 空间复杂度:\(O(1)\)(辅助空间)

  • 稳定性:稳定排序

折半插入排序

区别:找插入位置的方法改为了折半查找

每次将待插入元素放入temp,让left与right指向有序区首尾,通过二分找到合适的插入位置

  • 时间复杂度:\(O(n)\)(最好), \(O(n^2)\)(最坏), \(O(n^2)\)(平均)
  • 空间复杂度:\(O(1)\)(辅助空间)
  • 稳定性:稳定排序

希尔排序

基于插入排序的优势:基本有序与数量较小时的高效率

希尔排序通过增量d来对元素分组,d初始为长度的一半

按增量从下标为0开始,间隔d的元素分成一组,组内进行插入排序,排序完将起始点++,继续按间隔d的元素分成一组,以此类推,这是第一轮

第一轮结束后,将增量减半,继续分组进行插入排序

直到增量取值为1,此时即对整个数组插入排序

  • 时间复杂度:\(O(n)\)(最好), \(O(n^2)\)(最坏), \(O(n^{1.3})\)(平均)
  • 空间复杂度:\(O(1)\)(辅助空间)
  • 稳定性:不稳定排序

冒泡排序

即每轮都从前往后比较相邻的两个,逆序就交换

直到不能移动即实现归位,排好了一个元素

改进:引入flag标记每一轮是否进行交换,若发生交换则改为true,每轮结束后判断是否为false,若为false则表示已经有序

  • 时间复杂度:\(O(n)\)(最好), \(O(n^2)\)(最坏), \(O(n^{2})\)(平均)
  • 空间复杂度:\(O(1)\)(辅助空间)
  • 稳定性:稳定排序

快速排序

任取元素作为枢轴,划分成小于枢轴与大于枢轴两个部分,然后递归处理左右,直到空或只剩一个,以取第一个为例

定义一个pivot变量,将枢轴元素存入,设left与right指向数组首位,先让right向左移动,每个元素与枢轴比较,若小则移动到left位置,否则继续右移;然后让left向右移动,与枢轴比较,若大于则移动到right位置,否则继续右移

也可以不用pivot变量,而直接swap

即:

right从右往左找比枢轴小的元素去填left的坑

left从左往右找比枢轴大的元素去填right的坑

当left==right时,第一次划分结束,划分出两个区间,将枢轴填入指向的坑,此时枢轴归位

然后递归的处理每个区间

  • 时间复杂度:\(O(n\log n)\)(最好) \(O(n^2)\)(最坏), \(O(n\log n)\)(平均)
  • 空间复杂度:\(O(\log n)\)(辅助空间 最好), \(O(n)\)(辅助空间 最差), \(O(\log n )\)(辅助空间 平均)
  • 稳定性:不稳定排序

简单选择排序

即每轮在剩下的数里选最小的换到前面

定义一个k变量,保存最小值下标,每轮设为剩下元素中的第一个位置,从下标1开始依次和k中下标变量比较,并更新最小元素下标,搜索一趟后,找到最小值位置,与下标0的元素交换,此时该位置元素归位

  • 时间复杂度:\(O(n^2)\)(最好) \(O(n^2)\)(最坏), \(O(n^2)\)(平均)
  • 空间复杂度:\(O(1)\)(辅助空间)
  • 稳定性:不稳定排序

堆排序

堆排序是使用堆实现的

  1. 建堆:倒着将每个节点为根的子树调整成堆,可以直接从非叶子节点开始调整

    寻找最后一个非叶子节点,即第\(\lfloor \frac{n}{2}\rfloor\)号节点(第n号节点是最后一个叶子节点),拿其与其子树比较,判断是否为堆,若不是则交换不符合的父子节点

    交换后,若子树非叶子节点,则需要继续往下比较,防止已有结构被破坏

    (即从最后一个非叶节点开始依次向下调整)

  2. 排序:每次将堆顶元素与最后一个元素做交换,然后向下调整新的堆顶,此时该元素归位,隐去

  • 时间复杂度:\(O(n\log n)\)(最好) \(O(n\log n)\)(最坏), \(O(n\log n)\)(平均)
  • 空间复杂度:\(O(1)\)(辅助空间)
  • 稳定性:不稳定排序

归并排序

将两个有序数组合并为一个

取两个指针指向两个数组的开头,比较更小的放入新数组并移动指针

其中一个放完后,另一个剩下的元素直接放到最后

非递归

归并排序即每轮堆相邻的子序列两两归并

一开始把每个元素作为单独子序列,此时所有序列均有序,之后开始归并,归并方法按照上文

一轮归并完后再进行归并,直到变成一个序列

递归

从自顶向下思考

首先通过左右端点下标确定中间位置,根据中间位置划分为两个区间,

递归左右区间,通过中间位置划分为两个区间,再递归左右区间

直到两个区间有序,则回溯并执行归并操作

  • 时间复杂度:\(O(n\log n)\)(最好) \(O(n\log n)\)(最坏), \(O(n\log n)\)(平均)
  • 空间复杂度:\(O(n)\)(辅助空间)
  • 稳定性:稳定排序

基数排序

这是一种非比较排序,进行逐位排序

首先统一各元素位数,不够的加前导零

按照从最高位和从最低位排序,分为LSD(最低位优先)与MSD(最高位优先)

LSD较为简单:

首先将所有数按照个位分配,对于十进制,基数为0-9,造十个桶来存储,把每个数按照个位放到对应桶里

这里的桶采用链式存储,满足先进先出,即队列

然后从小到大遍历每个桶,把元素取出并链起来(next指针指向每个桶的队头)

然后按照十位分配,操作同上,直到整体有序

  • 时间复杂度:\(O(d\times (n+r))\),其中d为最大数位数,n为要排的n个数,r为基数
  • 空间复杂度:\(O(r)\)(辅助空间)
  • 稳定性:稳定排序