概述
最近在温习数据结构,现在针对排序算法写一篇随笔,其中附带原理说明和代码。
插入排序
直接插入排序(Straight Insertion Sort)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次 从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。
假设{20,30,40,10,60,50}中的前3个数已经排列过,是有序的了;接下来对10进行排列。示意图如下:

图中将数列分为有序区和无序区。我们需要做的工作只有两个:(1)取出无序区中的第1个数,并找出它在有序区对应的位置。(2)将无序区的数据插入到有序区;若有必要的话,则对有序区中的相关数据进行移位。
时间复杂度和稳定性
直接插入排序的时间复杂度是O(N2)。
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?N-1!因此,直接插入排序的时间复杂度是O(N*N)。
直接插入排序是稳定的算法,它满足稳定算法的定义。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的。
插入排序JAVA实现
1 import static javaTest.SortUtils.less;
2
3 /**
4 * ClassName InsertionSort.java
5 * author Rhett.wang
6 * version 1.0.0
7 * Description TODO
8 * createTime 2020年01月16日 16:00:00
9 */
10 public class InsertionSort implements SortAlgorithm{
11 public static void main(String[] args) {
12 Integer[] a= {22,14,35,45,67};
13 InsertionSort sort =new InsertionSort();
14 sort.sort(a);
15
16 }
17 /**
18 * Main method arrays sorting algorithms
19 *
20 * @param array - an array should be sorted
21 * @return a sorted array
22 */
23 @Override
24 public <T extends Comparable<T>> T[] sort(T[] array) {
25 for (int j = 1; j < array.length; j++) {
26
27 // Picking up the key(Card)
28 T key = array[j];
29 int i = j - 1;
30
31 while (i >= 0 && less(key, array[i])) {
32 array[i + 1] = array[i];
33 i--;
34 }
35 // Placing the key (Card) at its correct position in the sorted subarray
36 array[i + 1] = key;
37 }
38 return array;
39 }
40
41 }
实现基类接口
1 import java.util.Arrays;
2 import java.util.List;
3
4 import static javafx.scene.input.KeyCode.T;
5
6 /**
7 * ClassName SortAlgorithm.java
8 * author Rhett.wang
9 * version 1.0.0
10 * Description TODO
11 * createTime 2020年01月16日 14:29:00
12 */
13 public interface SortAlgorithm {
14 /**
15 * Main method arrays sorting algorithms
16 *
17 * @param unsorted - an array should be sorted
18 * @return a sorted array
19 */
20 <T extends Comparable<T>> T[] sort(T[] unsorted);
21
22
23 /**
24 * Auxiliary method for algorithms what wanted to work with lists from JCF
25 *
26 * @param unsorted - a list should be sorted
27 * @return a sorted list
28 */
29 default <T extends Comparable<T>> List<T> sort(List<T> unsorted){
30 return Arrays.asList(sort(unsorted.toArray((T[]) new Comparable[unsorted.size()])));
31 }
32 }
插入排序SCALA实现
1 /**
2 * ClassName InsertionSort.java
3 * author Rhett.wang
4 * version 1.0.0
5 * Description TODO
6 * createTime 2020年01月18日 11:25:00
7 */
8 object InsertionSort {
9 def main(args: Array[String]) {
10
11 }
12
13 def sort(nums: Array[Int]): Array[Int] = {
14 for (j <- 0 until nums.length -1) {
15 var temp = nums(j)
16 var i = j - 1
17 while (i >= 0 && temp < nums(i)) {
18 nums(i + 1) = nums(i)
19 i-=1
20 }
21 nums(i + 1) = temp
22 }
23 nums
24 }
25
26 }
插入排序PYTHON实现
1 def insert_sort(collection):
2 """Pure implementation of the insertion sort algorithm in Python
3 :param collection: some mutable ordered collection with heterogeneous
4 comparable items inside
5 :return: the same collection ordered by ascending
6 Examples:
7 >>> insertion_sort([0, 5, 3, 2, 2])
8 [0, 2, 2, 3, 5]
9 >>> insertion_sort([])
10 []
11 >>> insertion_sort([-2, -5, -45])
12 [-45, -5, -2]
13 """
14 for loop_index in range(1,len(collection)):
15 insertion_index=loop_index
16 while(
17 insertion_index>0
18 and collection[insertion_index -1] > collection[insertion_index-1]
19 ):
20 collection[insertion_index],collection[insertion_index -1] =(
21 collection[insertion_index -1],
22 collection[insertion_index],
23 )
24 insertion_index -=1
25 return collection
26
27 if __name__=="__main__":
28 user_input= input("Enter numbers separated by a comma:\n").strip()
29 unsorted=[int(item) for item in user_input.split(",")]
30 print(insert_sort(unsorted))
希尔排序
希尔(Shell)排序又称为缩小增量排序,它是一种插入排序。它是直接插入排序算法的一种威力加强版。该方法因DL.Shell于1959年提出而得名,
把记录按步长 gap 分组,对每组记录采用直接插入排序方法进行排序。
随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序。
我们来通过演示图,更深入的理解一下这个过程。

在上面这幅图中:
初始时,有一个大小为 10 的无序序列。
在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。接下来,按照直接插入排序的方法对每个组进行排序。
在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2 的元素组成一组,可以分为 2 组。按照直接插入排序的方法对每个组进行排序。
在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组。按照直接插入排序的方法对每个组进行排序。此时,排序已经结束。
需要注意一下的是,图中有两个相等数值的元素 5 和 5 。我们可以清楚的看到,在排序过程中,两个元素位置交换了。
所以,希尔排序是不稳定的算法。
算法分析
算法性能

时间复杂度
步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。
算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。
Donald Shell 最初建议步长选择为N/2并且对步长取半直到步长达到1。虽然这样取可以比O(N2)类的算法(插入排序)更好,但这样仍然有减少平均时间和最差时间的余地。
可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的。比如,如果一个数列以步长5进行了排序然后再以步长3进行排序,那么该数列不仅是以步长3有序,而且是以步长5有序。如果不是这样,那么算法在迭代过程中会打乱以前的顺序,那就
不会以如此短的时间完成排序了。

算法稳定性:由上文的希尔排序算法演示图即可知,希尔排序中相等数据可能会交换位置,所以希尔排序是不稳定的算法。
插入排序和希尔排序比较:
直接插入排序是稳定的;而希尔排序是不稳定的。
直接插入排序更适合于原始记录基本有序的集合。
希尔排序的比较次数和移动次数都要比直接插入排序少,当N越大时,效果越明显。
在希尔排序中,增量序列gap的取法必须满足:最后一个步长必须是 1 。
直接插入排序也适用于链式存储结构;希尔排序不适用于链式结构。
希尔排序JAVA实现
1 import java.util.Arrays;
2
3 import static javaTest.SortUtils.less;
4
5 /**
6 * ClassName ShellSort.java
7 * author Rhett.wang
8 * version 1.0.0
9 * Description TODO
10 * createTime 2020年01月18日 12:21:00
11 */
12 public class ShellSort implements SortAlgorithm {
13
14 /**
15 * This method implements Generic Shell Sort.
16 *
17 * @param array the array to be sorted
18 */
19 @Override
20 public <T extends Comparable<T>> T[] sort(T[] array) {
21 int gap = array.length / 2;
22
23 while (1 <= gap) {
24 // 把距离为 gap 的元素编为一个组,扫描所有组
25 for (int i = gap; i < array.length; i++) {
26 int j = 0;
27 T temp = array[i];
28
29 // 对距离为 gap 的元素组进行排序
30 for (j = i - gap; j >= 0 &&less(temp,array[j]); j = j - gap) {
31 array[j + gap] = array[j];
32 }
33 array[j + gap] = temp;
34 }
35
36 System.out.format("gap = %d:\t", gap);
37 printAll(array);
38 gap = gap / 2; // 减小增量
39 }
40 return array;
41 }
42
43 // 打印完整序列
44 public <T extends Comparable<T>> void printAll(T[] list) {
45 for (T value : list) {
46 System.out.print(value + "\t");
47 }
48 System.out.println();
49 }
50
51 /* Driver Code */
52 public static void main(String[] args) {
53 Integer[] toSort = {4, 23, 6, 78, 1, 54, 231, 9, 12};
54
55 ShellSort sort = new ShellSort();
56 System.out.println(Arrays.toString(sort.sort(toSort)));
57 }
58 }
希尔排序SCALA实现
1 package scala
2
3 /**
4 * ClassName ShellSort.java
5 * author Rhett.wang
6 * version 1.0.0
7 * Description TODO
8 * createTime 2020年01月18日 13:25:00
9 */
10 object ShellSort {
11 def main(args: Array[String]) {
12 var nums: Array[Int] = Array(2, 3, 8, 5, 4)
13 sort(nums)
14 nums.foreach(x => println(x))
15 }
16
17 def sort(nums: Array[Int]): Array[Int] = {
18 var gap = nums.length / 2
19 while (1 <= gap) {
20 for (i <- gap until nums.length) {
21 var j = i
22 while (j >= 0 && nums(j-gap) > nums(j)) {
23 var temp = nums(j-gap)+nums(j)
24 nums(j - gap) =temp- nums(j-gap)
25 nums(j)=temp- nums(j-gap)
26 j=j-gap
27 }
28 }
29 gap = gap / 2
30 }
31 nums
32 }
33 }
希尔排序PYTHON实现
1 def shell_sort(collection): 2 gap = len(collection) // 2 3 while (1 <= gap): 4 for i in range(gap, len(collection)): 5 j = i 6 while (j >= 0 and collection[j - gap] > collection[j]): 7 tmp = collection[j - gap] + collection[j] 8 collection[j - gap] = tmp - collection[j - gap] 9 collection[j] = tmp - collection[j - gap] 10 j = j- gap 11 gap = gap // 2 12 return collection 13 if __name__=="__main__": 14 15 print(shell_sort([2, 3, 8, 5, 4, 9]))
简单选择排序
选择排序(Selection sort)是一种简单直观的排序算法。
它的基本思想是:首先在未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置;接着,再从剩余未排序的元素中继续寻找最小(or最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
下面以数列{20,40,30,10,60,50}为例,演示它的选择排序过程(如下图):

第1趟:i=0。找出a[1...5]中的最小值a[3]=10,然后将a[0]和a[3]互换。 数列变化:20,40,30,10,60,50 -- > 10,40,30,20,60,50
第2趟:i=1。找出a[2...5]中的最小值a[3]=20,然后将a[1]和a[3]互换。 数列变化:10,40,30,20,60,50 -- > 10,20,30,40,60,50
第3趟:i=2。找出a[3...5]中的最小值,由于该最小值大于a[2],该趟不做任何处理。
第4趟:i=3。找出a[4...5]中的最小值,由于该最小值大于a[3],该趟不做任何处理。
第5趟:i=4。交换a[4]和a[5]的数据。 数列变化:10,20,30,40,60,50 -- > 10,20,30,40,50,60。
时间复杂度和稳定性
选择排序的时间复杂度是O(N2)。
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?N-1!因此,选择排序的时间复杂度是O(N2)。
选择排序是稳定的算法,它满足稳定算法的定义。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
选择排序JAVA实现
1 import java.util.Arrays;
2
3 import static javaTest.SortUtils.less;
4 import static javaTest.SortUtils.swap;
5
6 /**
7 * ClassName SelectionSort.java
8 * author Rhett.wang
9 * version 1.0.0
10 * Description TODO
11 * createTime 2020年01月18日 17:10:00
12 */
13 public class SelectionSort implements SortAlgorithm {
14 public static void main(String[] args) {
15 Integer[] arr = {4, 23, 6, 78, 1, 54, 231, 9, 12};
16 SelectionSort sort = new SelectionSort();
17 System.out.println(Arrays.toString(sort.sort(arr)));
18 }
19 /**
20 * Main method arrays sorting algorithms
21 *
22 * @param unsorted - an array should be sorted
23 * @return a sorted array
24 */
25 @Override
26 public <T extends Comparable<T>> T[] sort(T[] unsorted) {
27 int n=unsorted.length;
28 for(int i=0;i<n-1;i++){
29 int min =i;
30 for(int j=i+1;j<n;j++){
31 if(less(unsorted[j],unsorted[i])){
32 min=j;
33 }
34 }
35 if(min != i){
36 swap(unsorted,i,min);
37 }
38 }
39 return unsorted;
40 }
41 }
工具类SortUtils
1 /**
2 * ClassName SortUtils.java
3 * author Rhett.wang
4 * version 1.0.0
5 * Description TODO
6 * createTime 2020年01月16日 17:49:00
7 */
8 import java.util.Arrays;
9 import java.util.List;
10
11 /**
12 * The class contains util methods
13 *
14 * @author Podshivalov Nikita (https://github.com/nikitap492)
15 **/
16 final class SortUtils {
17
18 /**
19 * Helper method for swapping places in array
20 *
21 * @param array The array which elements we want to swap
22 * @param idx index of the first element
23 * @param idy index of the second element
24 */
25 static <T> boolean swap(T[] array, int idx, int idy) {
26 T swap = array[idx];
27 array[idx] = array[idy];
28 array[idy] = swap;
29 return true;
30 }
31
32
33 /**
34 * This method checks if first element is less then the other element
35 *
36 * @param v first element
37 * @param w second element
38 * @return true if the first element is less then the second element
39 */
40 static <T extends Comparable<T>> boolean less(T v, T w) {
41 return v.compareTo(w) < 0;
42 }
43
44
45 /**
46 * Just print list
47 *
48 * @param toPrint - a list which should be printed
49 */
50 static void print(List<?> toPrint) {
51 toPrint.stream()
52 .map(Object::toString)
53 .map(str -> str + " ")
54 .forEach(System.out::print);
55
56 System.out.println();
57 }
58
59
60 /**
61 * Prints an array
62 *
63 * @param toPrint - the array which should be printed
64 */
65 static void print(Object[] toPrint) {
66 System.out.println(Arrays.toString(toPrint));
67 }
68
69
70 /**
71 * Swaps all position from {@param left} to @{@param right} for {@param array}
72 *
73 * @param array is an array
74 * @param left is a left flip border of the array
75 * @param right is a right flip border of the array
76 */
77 static <T extends Comparable<T>> void flip(T[] array, int left, int right) {
78 while (left <= right) {
79 swap(array, left++, right--);
80 }
81 }
82 }
选择排序SCALA实现
1 /**
2 * ClassName SelectionSort.java
3 * author Rhett.wang
4 * version 1.0.0
5 * Description TODO
6 * createTime 2020年01月18日 19:28:00
7 */
8 object SelectionSort {
9 def main(args: Array[String]) {
10 var nums: Array[Int] = Array(2, 3, 8, 5, 4, 9)
11 sort(nums)
12 nums.foreach(x=>print(x))
13
14 }
15 def sort(nums:Array[Int]):Array[Int]={
16 for(i <- 0 until nums.length){
17 var min:Int=i
18 var minVal=nums(i)
19 for(j <- i+1 to nums.length-1){
20 if(nums(j) < minVal){
21 min = j
22 minVal=nums(j)
23 }
24 }
25 val temp :Int=nums(i)
26 nums(i)=nums(min)
27 nums(min)=temp
28 }
29 nums
30 }
31
32 }
选择排序PYTHON实现
1 def selection_sort(arr): 2 for i in range(1, len(arr)): 3 key = arr[i] 4 j = i - 1 5 while j >= 0 and key < arr[j]: 6 arr[j + 1] = arr[j] 7 j -= 1 8 arr[j + 1] = key 9 return arr 10 if __name__=="__main__": 11 12 print(selection_sort([2, 3, 8, 5, 4, 9, 2, 1]))
冒泡排序
冒泡排序(Bubble Sort),又被称为气泡排序或泡沫排序。
它是一种较简单的排序算法。它会遍历若干 次要排序的数列,每次遍历时,它都会从前往后依次的比较相邻两个数的大小;如果前者比后者大,则交换它们的位置。这样,一次遍历之后,最大的元素就在数列 的末尾! 采用相同的方法再次遍历时,第二大的元素就被排列在最大元素之前。重复此操作,直到整个数列都有序为止!
下面以数列{20,40,30,10,60,50}为例,演示它的冒泡排序过程(如下图)

当i=5,j=0时,a[0]<a[1]。此时,不做任何处理!
当i=5,j=1时,a[1]>a[2]。此时,交换a[1]和a[2]的值;交换之后,a[1]=30,a[2]=40。
当i=5,j=2时,a[2]>a[3]。此时,交换a[2]和a[3]的值;交换之后,a[2]=10,a[3]=40。
当i=5,j=3时,a[3]<a[4]。此时,不做任何处理!
当i=5,j=4时,a[4]>a[5]。此时,交换a[4]和a[5]的值;交换之后,a[4]=50,a[3]=60。
于是,第1趟排序完之后,数列{20,40,30,10,60,50}变成了{20,30,10,40,50,60}。此时,数列末尾的值最大。
时间复杂度和稳定性
冒泡排序的时间复杂度是O(N2)。
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?N-1次!因此,冒泡排序的时间复杂度是O(N2)。
冒泡排序是稳定的算法,它满足稳定算法的定义。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
冒泡排序JAVA实现
1 import java.util.Arrays;
2
3 import static javaTest.SortUtils.less;
4 import static javaTest.SortUtils.swap;
5
6 /**
7 * ClassName BubbleSort.java
8 * author Rhett.wang
9 * version 1.0.0
10 * Description TODO
11 * createTime 2020年01月18日 20:06:00
12 */
13 class BubbleSort implements SortAlgorithm {
14 /**
15 * This method implements the Generic Bubble Sort
16 *
17 * @param array The array to be sorted
18 * Sorts the array in increasing order
19 **/
20
21 @Override
22 public <T extends Comparable<T>> T[] sort(T[] array) {
23 for (int i = 0, size = array.length; i < size - 1; ++i) {
24 boolean swapped = false;
25 for (int j = 0; j < size - 1 - i; ++j) {
26 if (less(array[j], array[j + 1])) {
27 swap(array, j, j + 1);
28 swapped = true;
29 }
30 }
31 if (!swapped) {
32 break;
33 }
34 }
35 return array;
36 }
37
38 // Driver Program
39 public static void main(String[] args) {
40
41 // Integer Input
42 Integer[] integers = {4, 23, 6, 78, 1, 54, 231, 9, 12};
43 BubbleSort bubbleSort = new BubbleSort();
44 bubbleSort.sort(integers);
45
46 // Output => 231, 78, 54, 23, 12, 9, 6, 4, 1
47 System.out.println(Arrays.toString(integers));
48
49
50 // String Input
51 String[] strings = {"c", "a", "e", "b", "d"};
52 //Output => e, d, c, b, a
53 System.out.println(Arrays.toString(strings));
54 }
55 }
冒泡排序SCALA实现
1 import util.control.Breaks._
2 /**
3 * ClassName BubbleSort.java
4 * author Rhett.wang
5 * version 1.0.0
6 * Description TODO
7 * createTime 2020年01月18日 20:23:00
8 */
9 object BubbleSort {
10 def main(args: Array[String]) {
11 var nums: Array[Int] = Array(2, 3, 8, 5, 4, 9)
12 bubbleSort(nums)
13 nums.foreach(x=>print(x))
14 }
15
16 def bubbleSort(array: Array[Int]) : Array[Int] = {
17 breakable {
18 for (i <- 0 to array.length - 1) {
19 var swap = false
20
21 for (j <- 0 to array.length - 2) {
22 if (array(j) > array(j + 1)) {
23 val temp = array(j)
24 array(j) = array(j + 1)
25 array(j + 1) = temp
26 swap = true
27 }
28 }
29
30 if (!swap) {
31 break
32 }
33 }
34 }
35 array
36 }
37 }
冒泡排序PYTHON实现
1 def bubble_sort(collection): 2 length = len(collection) 3 for i in range(length - 1): 4 swapped = False 5 for j in range(length - 1 - i): 6 if collection[j] > collection[j + 1]: 7 swapped = True 8 collection[j], collection[j + 1] = collection[j + 1], collection[j] 9 if not swapped: 10 break # Stop iteration if the collection is sorted. 11 return collection 12 if __name__=="__main__": 13 14 print(bubble_sort([2, 3, 8, 5, 4, 9, 2, 1]))
快速排序
快速排序(Quick Sort)使用分治法策略。
它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序流程:
从数列中挑出一个基准值。
将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边);在这个分区退出之后,该基准就处于数列的中间位置。
递归地把"基准值前面的子数列"和"基准值后面的子数列"进行排序
下面以数列a={30,40,60,10,20,50}为例,演示它的快速排序过程(如下图):

上图只是给出了第1趟快速排序的流程。在第1趟中,设置x=a[i],即x=30。
从"右 --> 左"查找小于x的数:找到满足条件的数a[j]=20,此时j=4;然后将a[j]赋值a[i],此时i=0;接着从左往右遍历。
从"左 --> 右"查找大于x的数:找到满足条件的数a[i]=40,此时i=1;然后将a[i]赋值a[j],此时j=4;接着从右往左遍历。
从"右 --> 左"查找小于x的数:找到满足条件的数a[j]=10,此时j=3;然后将a[j]赋值a[i],此时i=1;接着从左往右遍历。
从"左 --> 右"查找大于x的数:找到满足条件的数a[i]=60,此时i=2;然后将a[i]赋值a[j],此时j=3;接着从右往左遍历。
从"右 --> 左"查找小于x的数:没有找到满足条件的数。当i>=j时,停止查找;然后将x赋值给a[i]。此趟遍历结束!
按照同样的方法,对子数列进行递归遍历。最后得到有序数组。
快速排序的时间复杂度和稳定性
快速排序稳定性
快速排序是不稳定的算法,它不满足稳定算法的定义。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
快速排序时间复杂度
快速排序的时间复杂度在最坏情况下是O(N2),平均的时间复杂度是O(N*lgN)。
这句话很好理解:假设被排序的数列中有N个数。遍历一次的时间复杂度是O(N),需要遍历多少次呢?至少lg(N+1)次,最多N次。
(01) 为什么最少是lg(N+1)次?快速排序是采用的分治法进行遍历的,我们将它看作一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。因此,快速排序的遍历次数最少是lg(N+1)次。
(02) 为什么最多是N次?这个应该非常简单,还是将快速排序看作一棵二叉树,它的深度最大是N。因此,快读排序的遍历次数最多是N次。
快速排序JAVA实现
1 import static Sorts.SortUtils.*;
2
3 /**
4 * @author Varun Upadhyay (https://github.com/varunu28)
5 * @author Podshivalov Nikita (https://github.com/nikitap492)
6 * @see SortAlgorithm
7 */
8 class QuickSort implements SortAlgorithm {
9
10 /**
11 * This method implements the Generic Quick Sort
12 *
13 * @param array The array to be sorted
14 * Sorts the array in increasing order
15 **/
16
17 @Override
18 public <T extends Comparable<T>> T[] sort(T[] array) {
19 doSort(array, 0, array.length - 1);
20 return array;
21 }
22
23
24 /**
25 * The sorting process
26 *
27 * @param left The first index of an array
28 * @param right The last index of an array
29 * @param array The array to be sorted
30 **/
31
32 private static <T extends Comparable<T>> void doSort(T[] array, int left, int right) {
33 if (left < right) {
34 int pivot = randomPartition(array, left, right);
35 doSort(array, left, pivot - 1);
36 doSort(array, pivot, right);
37 }
38 }
39
40 /**
41 * Ramdomize the array to avoid the basically ordered sequences
42 *
43 * @param array The array to be sorted
44 * @param left The first index of an array
45 * @param right The last index of an array
46 * @return the partition index of the array
47 */
48
49 private static <T extends Comparable<T>> int randomPartition(T[] array, int left, int right) {
50 int randomIndex = left + (int)(Math.random()*(right - left + 1));
51 swap(array, randomIndex, right);
52 return partition(array, left, right);
53 }
54
55 /**
56 * This method finds the partition index for an array
57 *
58 * @param array The array to be sorted
59 * @param left The first index of an array
60 * @param right The last index of an array
61 * Finds the partition index of an array
62 **/
63
64 private static <T extends Comparable<T>> int partition(T[] array, int left, int right) {
65 int mid = (left + right) / 2;
66 T pivot = array[mid];
67
68 while (left <= right) {
69 while (less(array[left], pivot)) {
70 ++left;
71 }
72 while (less(pivot, array[right])) {
73 --right;
74 }
75 if (left <= right) {
76 swap(array, left, right);
77 ++left;
78 --right;
79 }
80 }
81 return left;
82 }
83
84 // Driver Program
85 public static void main(String[] args) {
86
87 // For integer input
88 Integer[] array = {3, 4, 1, 32, 0, 1, 5, 12, 2, 5, 7, 8, 9, 2, 44, 111, 5};
89
90 QuickSort quickSort = new QuickSort();
91 quickSort.sort(array);
92
93 //Output => 0 1 1 2 2 3 4 5 5 5 7 8 9 12 32 44 111
94 print(array);
95
96 String[] stringArray = {"c", "a", "e", "b", "d"};
97 quickSort.sort(stringArray);
98
99 //Output => a b c d e
100 print(stringArray);
101 }
102 }
快速排序SCALA实现
1 object QuickSort {
2 /**
3 *
4 * @param array - a sequence of unsorted integers
5 * @return - sequence of sorted integers @array
6 */
7
8 def quickSort(array: Array[Int]): Array[Int] = {
9
10 def quickSortImpl(array: Array[Int], first: Int, last: Int): Array[Int] = {
11 var pivot: Int = 0
12 var i: Int = 0
13 var j: Int = 0
14 var temp: Int = 0
15
16 if (first < last) {
17 pivot = first
18 i = first
19 j = last
20
21 while (i < j) {
22 while (array(i) <= array(pivot) && i < last) {
23 i += 1
24 }
25
26 while (array(j) > array(pivot)) {
27 j -= 1
28 }
29
30 if (i < j) {
31 temp = array(i)
32 array(i) = array(j)
33 array(j) = temp
34 }
35 }
36
37 temp = array(pivot)
38 array(pivot) = array(j)
39 array(j) = temp
40 quickSortImpl(array, first, j - 1)
41 quickSortImpl(array, j + 1, last)
42 }
43
44 array
45 }
46
47 quickSortImpl(array,0, array.length-1)
48 }
49 }
快速排序PYTHON实现
1 def quick_sort_3partition(sorting, left, right):
2 if right <= left:
3 return
4 a = i = left
5 b = right
6 pivot = sorting[left]
7 while i <= b:
8 if sorting[i] < pivot:
9 sorting[a], sorting[i] = sorting[i], sorting[a]
10 a += 1
11 i += 1
12 elif sorting[i] > pivot:
13 sorting[b], sorting[i] = sorting[i], sorting[b]
14 b -= 1
15 else:
16 i += 1
17 quick_sort_3partition(sorting, left, a - 1)
18 quick_sort_3partition(sorting, b + 1, right)
19
20
21 if __name__ == "__main__":
22 user_input = input("Enter numbers separated by a comma:\n").strip()
23 unsorted = [int(item) for item in user_input.split(",")]
24 quick_sort_3partition(unsorted, 0, len(unsorted) - 1)
25 print(unsorted)
归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分 治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶 段得到的各答案"修补"在一起,即分而治之)。

可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n。
合并相邻有序子序列
再来看看治阶段,我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],来看下实现步骤。

归并排序JAVA实现
1 import static Sorts.SortUtils.print;
2
3 /**
4 * This method implements the Generic Merge Sort
5 *
6 * @author Varun Upadhyay (https://github.com/varunu28)
7 * @author Podshivalov Nikita (https://github.com/nikitap492)
8 * @see SortAlgorithm
9 */
10
11 class MergeSort implements SortAlgorithm {
12
13 /**
14 * This method implements the Generic Merge Sort
15 *
16 * @param unsorted the array which should be sorted
17 * @param <T> Comparable class
18 * @return sorted array
19 */
20 @Override
21 @SuppressWarnings("unchecked")
22 public <T extends Comparable<T>> T[] sort(T[] unsorted) {
23 doSort(unsorted, 0, unsorted.length - 1);
24 return unsorted;
25 }
26
27 /**
28 * @param arr The array to be sorted
29 * @param left The first index of the array
30 * @param right The last index of the array
31 * Recursively sorts the array in increasing order
32 **/
33 private static <T extends Comparable<T>> void doSort(T[] arr, int left, int right) {
34 if (left < right) {
35 int mid = left + (right - left) / 2;
36 doSort(arr, left, mid);
37 doSort(arr, mid + 1, right);
38 merge(arr, left, mid, right);
39 }
40
41 }
42
43 /**
44 * This method implements the merge step of the merge sort
45 *
46 * @param arr The array to be sorted
47 * @param left The first index of the array
48 * @param mid The middle index of the array
49 * @param right The last index of the array
50 * merges two parts of an array in increasing order
51 **/
52
53 private static <T extends Comparable<T>> void merge(T[] arr, int left, int mid, int right) {
54 int length = right - left + 1;
55 T[] temp = (T[]) new Comparable[length];
56 int i = left;
57 int j = mid + 1;
58 int k = 0;
59
60 while (i <= mid && j <= right) {
61 if (arr[i].compareTo(arr[j]) <= 0) {
62 temp[k++] = arr[i++];
63 } else {
64 temp[k++] = arr[j++];
65 }
66 }
67
68 while (i <= mid) {
69 temp[k++] = arr[i++];
70 }
71
72 while (j <= right) {
73 temp[k++] = arr[j++];
74 }
75
76 System.arraycopy(temp, 0, arr, left, length);
77 }
78
79 // Driver program
80 public static void main(String[] args) {
81
82 // Integer Input
83 Integer[] arr = {4, 23, 6, 78, 1, 54, 231, 9, 12};
84 MergeSort mergeSort = new MergeSort();
85 mergeSort.sort(arr);
86
87 // Output => 1 4 6 9 12 23 54 78 231
88 print(arr);
89
90 // String Inpu
91 String[] stringArray = {"c", "a", "e", "b", "d"};
92 mergeSort.sort(stringArray);
93 //Output => a b c d e
94 print(stringArray);
95 }
96 }
归并排序SCALA实现
1 object MergeSort {
2
3 /**
4 *
5 * @param array - a sequence of unsorted integers
6 * @return - sequence of sorted integers @array
7 */
8
9 def mergeSort(array: Array[Int]): Array[Int] = {
10
11 def sort(array: Array[Int]): Array[Int] = {
12 MS(array, 0, array.length - 1)
13 }
14
15 def MS(array: Array[Int], low: Int, high: Int): Array[Int] = {
16 if (low < high) {
17 val mid = (low + high) / 2
18 MS(array, low, mid)
19 MS(array, mid + 1, high)
20 merge(array, low, mid, high)
21 } else {
22 array
23 }
24 }
25
26 def merge(array: Array[Int], low: Int, mid: Int, high: Int): Array[Int] = {
27 // copy subarrays
28 val left = array.slice(low, mid + 1)
29 val right = array.slice(mid + 1, high + 1)
30
31 var i = 0
32 var j = 0
33 var k = low
34 while (k < high + 1) {
35 // must check if empty to avoid exceptions
36 if (i > left.length - 1) {
37 array(k) = right(j)
38 j = j + 1
39 } else if (j > right.length - 1) {
40 array(k) = left(i)
41 i = i + 1
42 } else if (left(i) <= right(j)) {
43 array(k) = left(i)
44 i = i + 1
45 } else {
46 array(k) = right(j)
47 j = j + 1
48 }
49 k = k + 1
50 }
51
52 array
53
54 }
55
56 sort(array)
57 }
58
59 }
归并排序PYTHON实现
1 def merge_sort(collection):
2 """Pure implementation of the merge sort algorithm in Python
3 :param collection: some mutable ordered collection with heterogeneous
4 comparable items inside
5 :return: the same collection ordered by ascending
6 Examples:
7 >>> merge_sort([0, 5, 3, 2, 2])
8 [0, 2, 2, 3, 5]
9 >>> merge_sort([])
10 []
11 >>> merge_sort([-2, -5, -45])
12 [-45, -5, -2]
13 """
14
15 def merge(left, right):
16 """merge left and right
17 :param left: left collection
18 :param right: right collection
19 :return: merge result
20 """
21 result = []
22 while left and right:
23 result.append((left if left[0] <= right[0] else right).pop(0))
24 return result + left + right
25
26 if len(collection) <= 1:
27 return collection
28 mid = len(collection) // 2
29 return merge(merge_sort(collection[:mid]), merge_sort(collection[mid:]))
30
31
32 if __name__ == "__main__":
33 user_input = input("Enter numbers separated by a comma:\n").strip()
34 unsorted = [int(item) for item in user_input.split(",")]
35 print(*merge_sort(unsorted), sep=",")
总结
感谢网络大神的分享:
https://github.com/TheAlgorithms/
https://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247487903&idx=3&sn=f482041f5b560ca85db9ad51c33f07f3&chksm=9bed30edac9ab9fbb523b1ce15e545d126e2b6a651951f8ea672d5f6d05809b9ae8f99d81411&mpshare=1&scene=1&srcid=0113MrSScIDzFwL6luR7bevY&sharer_sharetime=1579050754003&sharer_shareid=d40e8d2bb00008844e69867bcfc0d895#rd
来源:https://www.cnblogs.com/boanxin/p/12208642.html