概述
最近在温习数据结构,现在针对排序算法写一篇随笔,其中附带原理说明和代码。
插入排序
直接插入排序(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