数据结构之排序基础知识笔记

北战南征 提交于 2020-01-18 22:14:17

概述

最近在温习数据结构,现在针对排序算法写一篇随笔,其中附带原理说明和代码。

插入排序

直接插入排序(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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!