十大排序算法 JAVA代碼


 O是指計算機執行命令所需的時間

nlogn是算法的時間復雜度,一般排序用的是log2n

總體總結表:這個有個錯誤就是歸並排序需要一個o(n)的輔助數組 
這里寫圖片描述

冒泡排序

主要思想:外層循環從1到n-1,內循環從當前外層的元素的下一個位置開始,依次和外層的元素比較,出現逆序就交換。 
特點:stable sort(穩定性排序)、In-place sort(不占用額外的空間,只是交換元素) 
最優復雜度:當輸入數組就是排好序的時候,復雜度為O(n),而快速排序在這種情況下會產生O(n^2)的復雜度。 
最差復雜度:當輸入數組為倒序時,復雜度為O(n^2) 
插入排序比較適合用於“少量元素的數組”。

其實插入排序的復雜度和逆序對的個數一樣,當數組倒序時,逆序對的個數為n(n-1)/2,因此插入排序復雜度為O(n^2)。

public class BubbleSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 4, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { int n = array.length; for (int i = 0; i < n-1; i++) { for (int j = i + 1; j < n; j++) { if (array[j] < array[i]) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } } } } }

 

在算法導論思考題2-2中又問了”冒泡排序和插入排序哪個更快“呢?

插入排序的速度直接是逆序對的個數,而冒泡排序中執行“交換“的次數是逆序對的個數,因此冒泡排序執行的時間至少是逆序對的個數,因此插入排序的執行時間至少比冒泡排序快

插入排序

主要思想: 
特點:stable sort(穩定性排序)、In-place sort(不占用額外空間) 
最優復雜度:當輸入數組就是排好序的時候,復雜度為O(n),而快速排序在這種情況下會產生O(n^2)的復雜度。 
最差復雜度:當輸入數組為倒序時,復雜度為O(n^2) 
插入排序比較適合用於“少量元素的數組”。

其實插入排序的復雜度和逆序對的個數一樣,當數組倒序時,逆序對的個數為n(n-1)/2,因此插入排序復雜度為O(n^2)。

public class InsertSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 4, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { int n = array.length; for (int i = 1; i < n; i++) { for (int j = i ; j >0 ; j--) { if (array[j] < array[j - 1]) { int temp = array[j]; array[j] = array[j - 1]; array[j-1] = temp; } } } } }

 

 

插入排序的改進:內循環發現逆序不交換,采用整體右移,直到沒有逆序的時候把元素放在該位置

public class InsertSort2 { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { int n = array.length; for (int i = 1; i < n; i++) { int key = array[i]; int j = i -1; while (j >= 0 && array[j]>key) { array[j + 1] = array[j]; j--; } array[j+1] = key; } } }

 

問題:快速排序(不使用隨機化)是否一定比插入排序快?

答:不一定,當輸入數組已經排好序時,插入排序需要O(n)時間,而快速排序需要O(n^2)時間。

選擇排序

特性:In-place sort,unstable sort。 
思想:每次找一個最小值。 
最好情況時間:O(n^2)。 
最壞情況時間:O(n^2)。

public class SelectSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { int n = array.length; for (int i = 0; i < n-1; i++) { int min = i; for (int j = i+1; j < n; j++) { if (array[j] < array[min]) min = j; } int temp = array[i]; array[i] = array[min]; array[min] = temp; } } }

 

希爾排序

思想:基於插入排序,交換不相鄰的元素已對數組的局部進行排序,並最終用插入排序將局部有序的數組排序。思想是使數組中任意間隔為h的元素都是有序的,這樣的數組稱為h有序數組. 
特性:In-place sort,unstable sort。 
思想:每次找一個最小值。 
最好情況時間:O(n)。 
最壞情況時間:O(n^2)。

public class ShellSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7, 15}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { int n = array.length; int h = 1; while (h<n/3) h = 3*h +1; while (h >= 1) { for (int i = h; i < n; i++) { for (int j = i; j >= h && (array[j] < array[j - h]); j -= h) { int temp = array[j]; array[j] = array[j - h]; array[j-h]= temp; } } h /=3; } } }

歸並排序

特點:stable sort、Out-place sort 
思想:運用分治法思想解決排序問題。 
最壞情況運行時間:O(nlgn) 
最佳運行時間:O(nlgn)

程序中merge的精髓(也就是排序):左半邊用盡,則取右半邊元素;右半邊用盡,則取左半邊元素;右半邊的當前元素小於左半邊的當前元素,則取右半邊元素;右半邊的當前元素大於左半邊的當前元素,則取左半邊的元素。實際上大部分發生的都是后面兩句話,前面兩句只是特殊情況而已。

自頂向下:

public class MergeSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; mergeSort(array); System.out.println(Arrays.toString(array)); } private static void mergeSort(int[] array) { int[] aux = new int[array.length]; sort(array, aux, 0, array.length - 1); } private static void sort(int[] array, int[] aux, int lo, int hi) { if (hi<=lo) return; int mid = lo + (hi - lo)/2; sort(array, aux, lo, mid); sort(array, aux, mid + 1, hi); merge(array, aux, lo, mid, hi); } private static void merge(int[] array, int[] aux, int lo, int mid, int hi) { System.arraycopy(array,0,aux,0,array.length); int i = lo, j = mid + 1; for (int k = lo; k <= hi; k++) { if (i>mid) array[k] = aux[j++]; else if (j > hi) array[k] = aux[i++]; else if (aux[j]<aux[i]) array[k] = aux[j++]; else array[k] = aux[i++]; } } }

 

自底向上:

public class MergeSort2 { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } public static void sort(int[] array) { int N = a.length; int[] aux = new int[N]; for (int n = 1; n < N; n = n+n) { for (int i = 0; i < N-n; i += n+n) { int lo = i; int m = i+n-1; int hi = Math.min(i+n+n-1, N-1); merge(array, aux, lo, m, hi); } } } private static void merge(int[] array, int[] aux, int lo, int mid, int hi) { for (int k = lo; k <= hi; k++) { aux[k] = array[k]; } // merge back to a[] int i = lo, j = mid+1; for (int k = lo; k <= hi; k++) { if (i > mid) array[k] = aux[j++]; // this copying is unneccessary else if (j > hi) array[k] = aux[i++]; else if (aux[j]<aux[i]) array[k] = aux[j++]; else array[k] = aux[i++]; } } }

 

注意:當數組長度為2的冪時,自頂向下和自底向上的歸並排序所用的次數和數組訪問的次數正好相同,只是順序不同。其他時候,兩種方法的比較和數組的訪問次數會有所不同

問:歸並排序的缺點是什么?

答:他是Out-place sort,因此相比快排,需要很多額外的空間。

問:為什么歸並排序比快速排序慢?

答:雖然漸近復雜度一樣,但是歸並排序的系數比快排大。

問:對於歸並排序有什么改進?

答:就是在數組長度為k時,用插入排序,因為插入排序適合對小數組排序。在算法導論思考題2-1中介紹了。復雜度為O(nk+nlg(n/k)) ,當k=O(lgn)時,復雜度為O(nlgn)

改進: 
對小規模子數組采用插入排序: 
因為遞歸會使小規模問題中方法的調用過於頻繁,所以改進對它們的處理方法就能改進整個算法。使用插入排序處理小規模的子數組,一般可以將歸並排序的運行時間雖短10%~15%。無代碼

測試數組是否已經有序:可以添加一個判斷條件,如果a[mid]小於a[mid+1],我們就任務數組已經是有序的並跳過merge方法(指的是兩個sort后面的merge)。這個改動不影響排序的遞歸調用,但是任意有序的子數組算法的運行時間就變成線性的了。

不將元素復制到輔助數組:我們可以節省將數組復制到用於歸並的輔助數組所用的時間。要做到這一點我們要調用兩種排序方法,一種將數據從輸入數組排序到輔助數組,一種將數據從輔助數組排序到輸入數組,這種方法需要一些技巧,我們要在遞歸調用的每個層次交換輸入數組和輸出數組的角色。無代碼

快速排序

特性:unstable sort、In-place sort。 
最壞運行時間:當輸入數組已排序時,時間為O(n^2),當然可以通過隨機化來改進(shuffle array 或者 randomized select pivot),使得期望運行時間為O(nlgn)。 
最佳運行時間:O(nlgn) 
快速排序的思想也是分治法。 
當輸入數組的所有元素都一樣時,不管是快速排序還是隨機化快速排序的復雜度都為O(n^2),而在算法導論第三版的思考題7-2中通過改變Partition函數,從而改進復雜度為O(n)。

public class QuickSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { shuffle(array); sort(array, 0, array.length - 1); } private static void sort(int[] array, int lo, int hi) { if (hi<=lo) return; int j = partition(array, lo, hi); sort(array, lo, j - 1); sort(array, j+1, hi); } private static int partition(int[] array, int lo, int hi) { int i = lo; int j = hi + 1; int v = array[lo]; while (true) { while (array[++i] < v) if (i == hi) break; while (v < array[--j]) if (j == lo) break; if (i>=j) break; int temp = array[i]; array[i] = array[j]; array[j] = temp; } int temp = array[lo]; array[lo] = array[j]; array[j] = temp; return j; } /** *打亂數組 */ private static void shuffle(int[] array) { Random random = new Random(System.currentTimeMillis()); if (array == null) throw new NullPointerException("argument array is null"); int n = array.length; for (int i = 0; i < n; i++) { int r = i + random.nextInt(n-i); // between i and n-1 int temp = array[i]; array[i] = array[r]; array[r] = temp; } } }

 

算法改進: 
1. 切換到插入排序 
和大多數排序算法一樣,改進快速排序的一個簡單辦法基於以下兩點: 
對於小數組,快速排序比插入排序慢; 
因為遞歸,快速排序的sort()方法在小數組中也會調用自己

簡單的改動:將sort中的:

if(hi<=lo) return;

 

改成

if(hi<=lo+M) {
Insert.sort(a,lo,hi);
return;}

 

2. 三取樣切分 
3. 三向切分(將數組分成小於切分元素,等於切分元素和大於切分元素三組數組),代碼如下:

public class Quick3way { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } private static void sort(int[] array) { shuffle(array); sort(array, 0, array.length - 1); } private static void sort(int[] array, int lo, int hi) { if (hi <= lo) return; int lt = lo, gt = hi; int v = array[lo]; int i = lo; while (i <= gt) { if (array[i]<v) exch(array, lt++, i++); else if (array[i]>v) exch(array, i, gt--); else i++; } // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. sort(array, lo, lt-1); sort(array, gt+1, hi); } private static void exch(int[] a, int i, int j) { int swap = a[i]; a[i] = a[j]; a[j] = swap; } /** *打亂數組 */ private static void shuffle(int[] array) { Random random = new Random(System.currentTimeMillis()); if (array == null) throw new NullPointerException("argument array is null"); int n = array.length; for (int i = 0; i < n; i++) { int r = i + random.nextInt(n-i); // between i and n-1 int temp = array[i]; array[i] = array[r]; array[r] = temp; } } }

堆排序

特性:unstable sort、In-place sort。 
最優時間:O(nlgn) 
最差時間:O(nlgn)

public class HeapSort { public static void main(String[] args) { int[] array = new int[]{2, 3, 5, 8, 9, 0, 7, 5, 1, 6, 8, 7}; sort(array); System.out.println(Arrays.toString(array)); } public static void sort(int[] a){ int N = a.length; int[] keys = new int[N+1]; //注意,堆的數據結構是從1開始的,0不用 for (int i = 1; i < keys.length; i++) { keys[i] = a[i-1]; } // //構造堆,使得堆是有序的 for(int k = N/2;k>=1;k--) sink(keys,k,N); //排序,相當於毀掉堆 while(N>1){ exch(keys,1,N--); sink(keys,1,N); } //重新寫回數組 for (int i = 0; i < a.length; i++) { a[i] = keys[i+1]; } } private static void sink(int[] a, int k, int N) { // TODO Auto-generated method stub while(2*k<=N){ int j = 2*k; if (j < N && less(a[j], a[j+1])) j++; if (less(a[j], a[k])) break; exch(a, k, j); k = j; } } private static boolean less(int k, int j) { // TODO Auto-generated method stub return k < j; } private static void exch(int[] a, int i, int n) { // TODO Auto-generated method stub int temp = a[i]; a[i] = a[n]; a[n] = temp; } }

計數排序

特性:stable sort、out-place sort。 
最壞情況運行時間:O(n+k) 
最好情況運行時間:O(n+k)

public class CountingSort { public static void main(String[] args) throws Exception { int[] array = { 9, 9, 8, 8, 7, 5, 3, 2, 6, 0, 5 }; int[] sort = sort(array, 9); System.out.println(Arrays.toString(sort)); } /** * 輸入數組的元素都是介於0..k之間的 * @param data 待排序數組 * @param k 最大元素 * @return 排序結果 */ public static int[] sort(int[] data, int k) { // 存放臨時數據的數組tmp,初始元素都是0;k為數組中最大元素 int[] tmp = new int[k + 1]; // 計算數組中每個元素i出現的次數,存入數組tmp中的第i項,即原數組中的元素值為tmp數組中的下標 for (int i = 0; i <= data.length - 1; i++) { tmp[data[i]]++; } // 計算數組中小於等於每個元素的個數,即從tmp中的第一個元素開始,每一項和前一項相加 for (int j = 1; j <= k; j++) { tmp[j] = tmp[j] + tmp[j - 1]; } // result數組用來來存放排序結果 int[] result = new int[data.length]; for (int i = data.length - 1; i >= 0; i--) { result[tmp[data[i]] - 1] = data[i]; tmp[data[i]]--; } return result; } }

基數排序:

本文假定每位的排序是計數排序。 
特性:stable sort、Out-place sort。 
最壞情況運行時間:O((n+k)d) 
最好情況運行時間:O((n+k)d)

public class RadixSort { public static void main(String[] args) { int[] array = {3,2,3,2,5,333,45566,2345678,78,990,12,432,56}; radixSort(array,10,7); System.out.println(Arrays.toString(array)); } private static void radixSort(int[] array,int radix, int distance) { int length = array.length; int[] temp = new int[length]; int[] count = new int[radix]; int divide = 1; for (int i = 0; i < distance; i++) { System.arraycopy(array, 0,temp, 0, length); Arrays.fill(count, 0); for (int j = 0; j < length; j++) { int tempKey = (temp[j]/divide)%radix; count[tempKey]++; } for (int j = 1; j < radix; j++) { count [j] = count[j] + count[j-1]; } for (int j = length - 1; j >= 0; j--) { int tempKey = (temp[j]/divide)%radix; count[tempKey]--; array[count[tempKey]] = temp[j]; } divide = divide * radix; } } }

桶排序

假設輸入數組的元素都在[0,1)之間。 
特性:out-place sort、stable sort。 
最壞情況運行時間:當分布不均勻時,全部元素都分到一個桶中,則O(n^2),當然[算法導論8.4-2]也可以將插入排序換成堆排序、快速排序等,這樣最壞情況就是O(nlgn)。 
最好情況運行時間:O(n)

public class BucketSort { public static void main(String[] args) { double array[] = { 0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.26, 0.12, 0.23, 0.68 }; bucketSort(array); System.out.println(Arrays.toString(array)); } public static void bucketSort(double array[]) { int length = array.length; ArrayList arrList[] = new ArrayList[length]; for (int i = 0; i < length; i++) { //0.7到0.79放在第8個桶里,編號7;第一個桶放0到0.09 int temp = (int) Math.floor(10 * array[i]); if (null == arrList[temp]) arrList[temp] = new ArrayList(); arrList[temp].add(array[i]); } // 對每個桶中的數進行插入排序 for (int i = 0; i < length; i++) { if (null != arrList[i]) { Collections.sort(arrList[i]); } } int count = 0; for (int i = 0; i < length; i++) { if (null != arrList[i]) { Iterator iter = arrList[i].iterator(); while (iter.hasNext()) { Double d = (Double) iter.next(); array[count] = d; count++; } } } } }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM