MIT-归并排序和快速排序
文章目录
- 问题描述
- 例子
- 算法实现
- 冒泡排序(暴力排序)
- 归并排序(分治)
- 快速排序(分治)
问题描述
将一组无序的元素按某种顺序(通常是升序或降序)排列的过程。
例子
输入:arr = [5, 2, 9, 1, 5, 6]
输出:arr = [1, 2, 5, 5, 6, 9]
算法实现
冒泡排序(暴力排序)
冒泡排序是一种简单的排序算法,其核心思想是:通过不断交换相邻的元素,将较大的元素“冒泡”到数组的末尾,直到整个数组有序。
- 从数组的第一对元素开始,比较相邻的两个元素。如果前一个元素大于后一个元素,则交换它们。
- 继续比较下一对元素,直到比较到数组的末尾,这样每一轮结束时,最大的元素会被“冒泡”到数组的末尾。
- 对剩下的未排序部分继续重复步骤1和步骤2,直到整个数组排序完成。
假设我们有一个数组:
arr = [5, 2, 9, 1, 5, 6]
第一轮:
- 比较 5 和 2,交换它们,得到
[2, 5, 9, 1, 5, 6]。 - 比较 5 和 9,不交换。
- 比较 9 和 1,交换它们,得到
[2, 5, 1, 9, 5, 6]。 - 比较 9 和 5,交换它们,得到
[2, 5, 1, 5, 9, 6]。 - 比较 9 和 6,交换它们,得到
[2, 5, 1, 5, 6, 9]。
第一轮结束时,最大的元素 9 被“冒泡”到数组的最后一位。
第二轮:
- 比较 2 和 5,不交换。
- 比较 5 和 1,交换它们,得到
[2, 1, 5, 5, 6, 9]。 - 比较 5 和 5,不交换。
- 比较 5 和 6,不交换。
第二轮结束时,第二大的元素 6 被“冒泡”到数组倒数第二位。
第三轮:
- 比较 2 和 1,交换它们,得到
[1, 2, 5, 5, 6, 9]。 - 比较 2 和 5,不交换。
- 比较 5 和 5,不交换。
第三轮结束时,第三大的元素 5 被“冒泡”到数组的倒数第三位。
第四轮:
- 比较 1 和 2,不交换。
此时数组已经排序完成,结果为:
[1, 2, 5, 5, 6, 9]
void bubbleSort(int &A[], int n)
{for (int i = 0; i < n - 1; i ++ ) // 后 i 个数已经有序{bool isSwap = false; for (int j = 0; j < n - i - 1; j ++ ){if (A[j] > A[j + 1]) swap(A[j], A[j + 1]), isSwap = true;}if (isSwap) break;}
}
时间复杂度:O(n2)O(n^2)O(n2)
归并排序(分治)
归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

void mergeSort(int &A[], int low, int high)
{if (low >= high) return;int mid = (low + high) / 2;mergeSort(A, low, mid); // 左边mergeSort(A, mid + 1, high); // 右边Merge(A, low, mid, high); // 合并
}void Merge(int &A[], int low, int mid, int high)
{int n = high - low + 1;int B[n];int i = 0;int l = low, r = mid + 1;while (l <= mid && r <= high){if (A[l] < A[r]) B[i ++ ] = A[l ++ ];else B[i ++ ] = B[r ++ ];}while (l <= mid) B[i ++ ] = A[l ++ ];while (r <= high) B[i ++ ] = B[r ++ ];for (i = 0; i < n; i ++ )A[low + i] = B[i];
}
时间复杂度:O(nlog2n)O(n\log_2 n)O(nlog2n)
假设 n=2hn=2^hn=2h 且 T(1)=O(1)T(1)=O(1)T(1)=O(1),T(n)T(n)T(n) 是执行一次大小为 nnn 次的 mergeSort 所需的时间。
mergeSort(A, low, mid):T(n2)T(\frac{n}{2})T(2n)mergeSort(A, mid + 1, high):T(n2)T(\frac{n}{2})T(2n)Merge(A, low, mid, high):O(n)O(n)O(n)
T(n)=2T(n2)+O(n)≤2T(n2)+cn≤2[2T(n22)+cn2]+cn=22T(n22)+2cn≤...=2hT(n2h)+hcn=nT(1)+cnlog2n=O(nlog2n)\begin{aligned} T(n)&=2T(\frac{n}{2})+O(n) \\&\leq 2T(\frac{n}{2})+cn \\&\leq2\left[2T(\frac{n}{2^2})+c\frac{n}{2}\right]+cn \\&=2^2T(\frac{n}{2^2})+2cn \\& \leq... \\&=2^hT(\frac{n}{2^h})+hcn \\&=nT(1)+cn\log_2n \\&=O(n\log_2 n) \end{aligned}T(n)=2T(2n)+O(n)≤2T(2n)+cn≤2[2T(22n)+c2n]+cn=22T(22n)+2cn≤...=2hT(2hn)+hcn=nT(1)+cnlog2n=O(nlog2n)
快速排序(分治)
给定一个中轴元素 A[w]A[w]A[w],在待排序的数组 A[low,high]A[low, high]A[low,high] 通过划分、比较和交换,使得:
- A[low,w−1]<A[w]A[low,w-1]<A[w]A[low,w−1]<A[w]
- A[w]>A[w+1,high]A[w]>A[w + 1,high]A[w]>A[w+1,high]
其中 iii 是当前数组最后一个小于中轴元素 A[w]A[w]A[w] 的下标;jjj 是当前数组最后一个大于中轴元素 A[w]A[w]A[w] 的下标。

void quickSort(int &A[], int low, int high)
{if (low >= high) return;int mid = partition(A, low, high);quickSort(A, low, mid - 1);quickSort(A, mid + 1, high);
}int partition(int &A[], int low, int high)
{int i = low - 1, j = low;int x = A[high];while (j <= high - 1){if (A[j] < x) swap(A[ ++ i], A[j]);j ++ ;}swap(A[i + 1], A[high]);return i + 1;
}
时间复杂度:O(nlog2n)O(n\log_2 n)O(nlog2n)
假设 n=2hn=2^hn=2h 且 T(1)=O(1)T(1)=O(1)T(1)=O(1),T(n)T(n)T(n) 是执行一次大小为 nnn 次的 quickSort 所需的时间。
quickSort(A, low, mid - 1):T(n2)T(\frac{n}{2})T(2n)quickSort(A, mid + 1, high):T(n2)T(\frac{n}{2})T(2n)int mid = partition(A, low, high):O(n)O(n)O(n)
T(n)=2T(n2)+O(n)≤2T(n2)+cn≤2[2T(n22)+cn2]+cn=22T(n22)+2cn≤...=2hT(n2h)+hcn=nT(1)+cnlog2n=O(nlog2n)\begin{aligned} T(n)&=2T(\frac{n}{2})+O(n) \\&\leq 2T(\frac{n}{2})+cn \\&\leq2\left[2T(\frac{n}{2^2})+c\frac{n}{2}\right]+cn \\&=2^2T(\frac{n}{2^2})+2cn \\& \leq... \\&=2^hT(\frac{n}{2^h})+hcn \\&=nT(1)+cn\log_2n \\&=O(n\log_2 n) \end{aligned}T(n)=2T(2n)+O(n)≤2T(2n)+cn≤2[2T(22n)+c2n]+cn=22T(22n)+2cn≤...=2hT(2hn)+hcn=nT(1)+cnlog2n=O(nlog2n)
