1. 选择排序
开启n-1轮循环,每次从未排序的元素中选择最小(或最大)的元素,将其与未排序序列的第一个元素交换位置,从而逐步构建有序序列。
选择排序的具体流程如下:
- 初始状态:将整个序列视为两部分,一部分是已排序序列,另一部分是未排序序列。
- 选择最小元素:从未排序序列中选择最小的元素,并将其与未排序序列的第一个元素交换位置。
- 扩大已排序序列:将交换后的元素视为已排序序列的一部分,未排序序列减少一个元素。
- 重复步骤2和步骤3:直到所有元素都被选择并放置到正确的位置上。
伪代码:
1 | 选择排序(A): |
时间复杂度为O(n^2)
2. 冒泡排序
多次遍历待排序序列,每轮连续比较相邻的元素,如果“左元素 > 右元素”则交换位置,使较大的元素逐步“冒泡”到数组的末端。
冒泡排序的具体流程如下:
- 比较相邻元素:从数组的第一个元素开始,比较相邻的两个元素,如果前一个元素比后一个元素大,则交换它们的位置,这样较大的元素就会向数组的末端移动。
- 遍历整个数组:重复步骤1,遍历整个数组,直到所有的元素都已经比较过一次。
- 缩小范围:每次遍历过程中,都会使得数组末端的一个元素排好序,因此在下一次遍历时,可以将待排序序列的范围缩小一个元素。
伪代码:
1 | 冒泡排序(A): |
时间复杂度:O(n^2)
3. 插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法。其基本思想是将待排序的数据序列分为已排序区间和未排序区间,通过构建有序序列,对未排序的数据逐个进行插入,从而达到排序的目的。
插入排序的具体思路如下:
- 初始状态:将第一个元素视为已排序序列,剩余的元素为未排序序列。
- 遍历未排序序列:从第二个元素开始,逐个遍历未排序序列的元素。
- 将当前元素插入已排序序列:对于每个未排序序列中的元素,将其与已排序序列中的元素逐个比较,找到合适的位置并插入。
- 移动元素:为了给当前元素腾出位置,需要将已排序序列中比当前元素大的元素依次向后移动。
- 重复步骤3和步骤4:直到所有元素都被插入到已排序序列中。
伪代码:
1 | 插入排序(A): |
时间复杂度:O(n^2)
4. 快速排序
快速排序(quick sort)是一种基于分治策略的排序算法。基本思想是选择一个基准元素,将序列分割成两个子序列,其中一个子序列中的所有元素小于基准元素,另一个子序列中的所有元素大于基准元素,然后对这两个子序列分别进行递归排序。
快速排序的具体步骤如下:
- 选择基准元素:从待排序序列中选择一个元素作为基准元素。通常选择序列的第一个元素、最后一个元素或者中间的元素作为基准元素。
- 分割序列:根据基准元素,将序列分割成两个子序列,其中一个子序列中的元素小于基准元素,另一个子序列中的元素大于基准元素。通常使用两个指针(左指针和右指针)来实现分割,左指针指向序列的起始位置,右指针指向序列的末尾位置,然后从左右两端向中间移动指针,交换不符合条件的元素,直到左右指针相遇。
- 递归排序:对两个子序列分别进行递归排序,直到子序列的长度为1或0。
- 合并结果:将经过排序的子序列合并成最终的有序序列。
伪代码:
1 | 快速排序(A, low, high): |
快速排序的时间复杂度为 O(n log n),其中 n 是待排序序列的长度。在平均情况下,快速排序的性能非常好,但是在最坏情况下(例如,当选择的基准元素总是序列中的最大或最小元素时),快速排序的时间复杂度可能会退化为 O(n^2)。
空间复杂度为O(n):在输入数组完全倒序的情况下,达到最差递归深度 n ,使用 O(n)栈帧空间。若采用尾递归优化,则复杂度为O(log n)。
尾递归优化是一种编译器优化技术,用于优化递归调用的栈空间使用。在尾递归优化中,如果函数的最后一个操作是对自身的递归调用,编译器会将这种递归调用转换为循环,从而避免在调用栈上创建新的帧,节省了栈空间的使用。
5. 归并排序
归并排序(Merge Sort)是一种基于分治思想的排序算法。其基本思路是将待排序序列分成两个子序列,分别对这两个子序列进行递归排序,然后将两个已经排好序的子序列合并成一个有序序列。
具体的算法流程:
- 分割序列:不断地将待排序序列划分成两个子序列,直到子序列的长度为1或0
- 递归排序:递归地对分割后的两个子序列进行排序。
- 合并序列:将两个已经排好序的子序列合并成一个有序序列。
伪代码:
1 | 归并排序(A): |
归并排序的时间复杂度为 O(n log n)。虽然归并排序的时间复杂度较低,但是需要额外的空间来存储归并过程中产生的临时序列,因此其空间复杂度为 O(n)。
6. 堆排序
堆排序(Heap Sort)是一种基于堆数据结构的排序算法,其基本思想是利用堆的性质实现排序。堆是一个特殊的树形数据结构,具有以下性质:
- 它是一个完全二叉树,即除了最后一层,其他层都是满的。
- 在一个小顶堆(Min Heap)中,任意节点的值都小于或等于其子节点的值。
- 在一个大顶堆(Max Heap)中,任意节点的值都大于或等于其子节点的值。
将待排序的序列构建成一个大顶堆,重复从堆中取出堆顶元素,并重新构建堆,直到堆为空,最终得到即为排序好的序列。
算法的流程:
构建堆:
首先需要将待排序的序列构建成一个堆。一种常见的方法是从最后一个非叶子节点开始,依次向前进行调整,保证每个节点都满足堆的性质。
大顶堆的调整过程:
- 从最后一个非叶子节点开始(即 n/2-1),向前遍历到根节点。
- 对于每个节点,如果它的子节点中有比它大的,就将其与最大的子节点进行交换,直到它没有比它大的子节点或者到达叶子节点为止。
排序:
构建好堆之后,需要重复从堆中取出最大(或最小)的元素,并重新调整堆,直到堆为空。具体的过程如下:
- 将堆顶元素(最大或最小值)与堆中的最后一个元素交换位置。
- 从堆中移除最后一个元素(即最大或最小值)。
- 对剩余的元素重新调整堆,使其满足堆的性质。
重复以上步骤,直到堆为空。