涉及的一些知识
- 逆序对
设 A A A为一个有 n n n个数字的有序集( n > 1 n>1 n>1),其中所有数字各不相同。如果存在正整数 i i i、 j j j使得 1 ≤ i < j ≤ n 1 \leq i < j \leq n 1≤i<j≤n而且 A [ i ] > A [ j ] A[i]>A[j] A[i]>A[j],则 ( A [ i ] , A [ j ] ) (A[i],A[j]) (A[i],A[j])这个有序对称为A的一个逆序对。
- 定理
任意 N N N个不同元素组成的序列平均具有 N ( N − 1 ) / 4 N(N-1)/4 N(N−1)/4个逆序对。
- 稳定性
能保证两个相等的数,经过排序之后,其在序列的前后位置顺序不变。( A 1 = A 2 A_1=A_2 A1=A2,排序前 A 1 A_1 A1在 A 2 A_2 A2前面,排序后 A 1 A_1 A1还在 A 2 A_2 A2前面)
简单排序
简单排序有三种,选择排序(Selection Sort)、冒泡排序(Bubble Sort)和插入排序(Insertion Sort)。
选择排序
-
选择排序
从待排序序列选择最小的元素与序列第1个元素交换;从剩下的元素中选择最小的元素与序列第2个元素交换;依此类推直到最后一个元素。 -
代码实现:
/* 选择排序 */ void SelectionSort(ListElementType *A, int Size) { int i = 0, j = 0, Min = 0; /* 从待排序序列选择最小的元素与序列第1个元素交换;从剩下的元素中选择最小的元素与序列第2个元素交换;依此类推直到最后一个元素。 */ for (i = 0; i < Size; i++) { for (j = i, Min = j; j < Size; j++) { if (A[j] < A[Min]) { Min = j; /* Min存储最小元素下标 */ } } Swap(&A[Min], &A[i]); /* 将未排序列中最小元素与A[i]交换位置 */ } }
-
时间复杂度
最坏情况时间复杂度: T w o r s t ( N ) = O ( N 2 ) T_{worst}(N)=O(N^2) Tworst(N)=O(N2)
平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2) -
稳定性
选择排序是不稳定排序。
冒泡排序
-
冒泡排序
- 假设数组有 n n n个元素,从头到尾依次比较元素A[i]和A[i+1],如果顺序不对,将两个元素调换位置。当比较完元素A[n-2]和A[n-1]的时候,数组中最大的(假设从小到大排序)元素就会被放到数组最后一个位置。
- 对数组中前 n − 1 n-1 n−1个元素重复第一步的操作,可将 n − 1 n-1 n−1个元素中最大的元素放在 n − 1 n-1 n−1个元素的最后位置。
- 重复执行第一步 n − 1 n-1 n−1次,可将数组排序完成。
-
代码实现
/* 冒泡排序 */ void BubbleSort(ListElementType *A, int Size) { int i = 0, j = 0, flog = 0; for (i = 0; i < Size - 1; i++) { for (j = 0; j < Size - i - 1; j++) { if (A[j] > A[j + 1]) /* 比较两个元素的大小,将较大的放在较小的后面 */ { Swap(&A[j], &A[j + 1]); } } } }
-
优化
当某次遍历的之后,如果序列已经排好序了,那么就没有必要继续遍历下去了,所以我们可以增加一个标记flag,用来监控某一次遍历过程中是否发生元素位置的交换,如果未发生交换,就可以直接停止循环了。/* 冒泡排序 */ void BubbleSort(ListElementType *A, int Size) { int i = 0, j = 0, flag = 0; for (i = 0; i < Size - 1; i++) { flag = 0; /* 用来检测一趟冒泡之中是否发生交换,没发生交换证明序列已经排好序了 */ for (j = 0; j < Size - i - 1; j++) { if (A[j] > A[j + 1]) { Swap(&A[j], &A[j + 1]); flag = 1; } } /* 没发生交换,直接退出循环 */ if (flag == 0) { break; } } }
-
时间复杂度
- 优化前:
最坏情况时间复杂度: T w o r s t ( N ) = O ( N 2 ) T_{worst}(N)=O(N^2) Tworst(N)=O(N2)
平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2) - 优化后:
时间复杂度: T ( N ) = O ( N + I ) T(N)=O(N+I) T(N)=O(N+I)(其中 I I I是数组中逆序对的个数)
平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2)
- 优化前:
-
稳定性
冒泡排序是稳定排序。
插入排序
-
插入排序
- 认为数组第一个元素是有序的,第二个元素和第一个元素比较大小,如果第二个元素比较小,则两者交换位置。此时,第一第二个元素就是有序的了。
- 第三个元素和它前一个元素对比,如果第三个元素比较小,互换位置,然后该元素再与互换位置后的它前面的一个元素做同样的操作,直到它比它前一个元素值大或它变成了第一个元素。操作结束后前三个元素就是有序的了。
- 一直重复上面的操作,直到所有元素都变成有序的。
-
代码实现
/* 插入排序 */ void InsertionSort(ListElementType *A, int Size) { int i = 0, j = 0; ListElementType Temp; for (i = 1; i < Size; i++) { Temp = A[i]; for (j = i; j > 0 && A[j - 1] > Temp; j--) { A[j] = A[j - 1]; /* 向后移动 */ } A[j] = Temp; } }
-
时间复杂度
时间复杂度: T ( N ) = O ( N + I ) T(N)=O(N+I) T(N)=O(N+I)(其中 I I I是数组中逆序对的个数)
平均时间复杂度: T a v g ( N ) = O ( N 2 ) T_{avg}(N)=O(N^2) Tavg(N)=O(N2) -
稳定性
插入排序是稳定排序。