- 插入排序
算法思路: 假想有一个已经排好序的序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数后序列仍然有序。于是,这种算法就叫做“插入排序法”。
算法特点:每次插入一个新的数,此有序序列的长度增加1,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。
具体实现:1. 每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。
2. 插入排序使用增量(Incremental)方法;在排好子序列A[1..j-1]后,将A[j]插入,形成排好序的子数组A[1..j]。
3. 从有序数列和无序数列{a2,a3,…,an}开始进行排序。
4. 处理第i个元素时(i=2,3,…,n),数列{a1,a2,…,ai-1}是已有序的,而数列{ai,ai+1,…,an}是无序的。
用ai与ai-1,a i-2,…,a1进行比较,找出合适的位置将ai插入。
5. 重复第4步,得到最终的有序数列需要进行n-1次插入操作。
样例说明:(假如原始数列为:[12,15,9,20,6,31,24])
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } for (int i = 1; i < disorderedArray.Length; i++) { int curValue = disorderedArray[i]; int temp = i; while (temp >= 1 && disorderedArray[temp - 1] > curValue) { disorderedArray[temp] = disorderedArray[temp - 1]; temp--; } disorderedArray[temp] = curValue; } return disorderedArray; }
- 冒泡排序
算法特性:时间复杂度O(N^2);具有稳定性;编程复杂度低,很容易写出代码;
实现思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。
实现过程:设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,
凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。
性能分析:若记录序列的初始状态为"正序",则冒泡排序过程只需进行一趟排序,在排序过程中只需进行n-1次比较,且不移动记录;
反之,若记录序列的初始状态为"逆序",则需进行n(n-1)/2次比较和记录移动。因此冒泡排序总的时间复杂度为O(n*n)
实现原理图解:
图1(向前比较) 图2(向后比较)
向前比较:从A[i-1]...A[0],两两进行比较,按照大小顺序进行交换,如图1所示。
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } // 依次从数列的第2个到第n个数 for (int i = 1; i < disorderedArray.Length; i++) { // 依次两两比较第i...0元素,并进行交换 for (int j = i; j > 0; j--) { //如果后一个比前一个小,则交互位置 if (disorderedArray[j] < disorderedArray[j - 1]) { int smaller = disorderedArray[j]; disorderedArray[j] = disorderedArray[j - 1]; disorderedArray[j - 1] = smaller; } } } return disorderedArray; }
向后比较:从A[i+1]...A[n],两两进行比较,按照大小顺序进行交换,如图2所示
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } // 从第1个元素---倒数第2个元素 for (int i = 0; i < disorderedArray.Length - 1; i++) { // 从倒数第一个元素---第i+1个元素 for (int j = disorderedArray.Length - 1; j > i; j--) { if (disorderedArray[j] < disorderedArray[j - 1]) { int smaller = disorderedArray[j]; disorderedArray[j] = disorderedArray[j - 1]; disorderedArray[j - 1] = smaller; } } } return disorderedArray; } }
上面两种不同方法,实现方式不同,但是具有相同的时间复杂度O(N^2)。
冒牌排序算法的缺点:上面2中冒泡排序的实现方法,无论是向前或向后的方式,都无法避免两种情况:
1. 对一个已经排好序的序列直接退出;
2. 对A[i+1]...A[n-1]已经是有序子集,在排好A[0]...A[i]后如果提前结束排序循环。
解决思路:在外层循环里每次循环开始时加入一个flag并标记为0,如果发生了交互则flag=1,否则说明序列已经排序好,直接退出。
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } // 是否发生过交换 bool isExchanged = false; // 从第1个元素---倒数第2个元素 for (int i = 0; i < disorderedArray.Length - 1; i++) { isExchanged = false; // 从倒数第一个元素---第i+1个元素 for (int j = disorderedArray.Length - 1; j > i; j--) { if (disorderedArray[j] < disorderedArray[j - 1]) { int smaller = disorderedArray[j]; disorderedArray[j] = disorderedArray[j - 1]; disorderedArray[j - 1] = smaller; isExchanged = true; } } if (!isExchanged) { break; } } return disorderedArray; }
- 选择排序
实现思路:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序是不稳定的排序方法。
排序方法实现示例:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [76 97 65 49 ]
第五趟排序后 13 27 38 49 49 [97 65 76]
第六趟排序后 13 27 38 49 49 65 [97 76]
第七趟排序后 13 27 38 49 49 65 76 [97]
最后排序结果 13 27 38 49 49 65 76 97
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } int min, k = 0; for (int i = 0; i < disorderedArray.Length - 1; i++) { // 假定当前第i个元素为最小的一个元素 min = disorderedArray[i]; // 循环目的:从第i+1...第n-1序列中查找是否有比当前第i个元素更小的元素存在 for (int j = i + 1; j < disorderedArray.Length; j++) { if (min > disorderedArray[j]) { min = disorderedArray[j]; // 记录下最小元素的位置信息 k = j; } } // 的确发现有比当前第i个元素更小的元素存在 if (disorderedArray[i] != min) { int temp = disorderedArray[i]; disorderedArray[i] = disorderedArray[k]; disorderedArray[k] = temp; } } return disorderedArray; }
- 快速排序
实现思路:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法解释:假设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第1个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
快速排序演示动画:
快速排序示例:
初始化设置:Seed=A[0], Seed永远保持不变 | |||||||||
序列 |
A[0]
|
A[1]
|
A[2]
|
A[3]
|
A[4]
|
A[5]
|
A[6]
|
i, j变化情况 | 方向 |
原始序列 |
49
|
38
|
65
|
97
|
76
|
13
|
27
|
i=0/j=6 | ← |
第1次排序 | 27 | 38 | 65 | 97 | 76 | 13 | 49 | i=0/j=6 | → |
第2次排序 | 27 | 38 | 49 | 97 | 76 | 13 | 65 | i=2/j=6 | → |
第3次排序 | 27 | 38 | 13 | 97 | 76 | 49 | 65 | i=2/j=5 | ← |
第4次排序 | 27 | 38 | 13 | 49 | 76 | 97 | 65 | i=3/j=5 | → |
从上述示例可以看出,在Seed=49的情况下,整个序列被划分成了前后两部分,分别是小于/大于49。
public int[] Sort(int[] disorderedArray) { if (disorderedArray == null) { throw new ArgumentNullException("disorderedArray"); } if (disorderedArray.Length == 1) { return disorderedArray; } QuickSorting.Sort(disorderedArray, 0, disorderedArray.Length - 1); return disorderedArray; } /// <summary> /// 排序 /// </summary> /// <param name="disorderedArray">待排序数组</param> /// <param name="left">数组第一个元素索引Index</param> /// <param name="right">数组最后一个元素索引Index</param> private static void Sort(int[] disorderedArray, int left, int right) { // 左边索引小于右边,则还未排序完成 if (left < right) { // 取中间的元素作为比较基准,小于他的往左边移,大于他的往右边移 int seed = disorderedArray[(left + right) / 2]; int i = left - 1; int j = right + 1; while (true) { while (disorderedArray[++i] < seed && i < right) ; while (disorderedArray[--j] > seed && j > 0) ; if (i >= j) { break; } Swap(disorderedArray, i, j); } Sort(disorderedArray, left, i - 1); Sort(disorderedArray, j + 1, right); } } /// <summary> /// 交换元素值 /// </summary> /// <param name="disorderedArray">数组</param> /// <param name="i">当前左边索引</param> /// <param name="j">当前右边索引</param> private static void Swap(int[] disorderedArray, int i, int j) { int number = disorderedArray[i]; disorderedArray[i] = disorderedArray[j]; disorderedArray[j] = number; }