標籤:
講 Map集合 ,還是喜歡學到哪裡總結吧。最近面試期準備準備,我是一員,成功被阿里線上筆試秒殺回絕。平常心,繼續努力。這次帶來 Collections 和 Arrays 類中的經典演算法剖析。
一、Colletions和Arrays
Collentions 此類完全是服務容器的”封裝器“。提供了一些操作或者返回容器的靜態方法。而Arrays是用來運算元組的各種方法。其中它們的聯絡在於其中的Sort方法,也就是這次部落格的主題。
二、插入,快速、歸併基本演算法
① 插入排序
{a1},{a2,a3,a4,…,an}}
{{a1⑴,a2⑴},{a3⑴,a4⑴ …,an⑴}}
…
{{a1(n-1),a2(n-1) ,…},{an(n-1)}}
原理及記憶方法:每次處理就是將無序數列的第一個元素與有序數列的元素從後往前逐個進行比較,找出插入位置,將該元素插入到有序數列的合適位置中。這通俗的是找座位思想。Java版實現如下
import java.util.Arrays; public class InsertionSort{ public static void main(String[] args) { int[] intA = new int[]{2,1,3,4,6,7,5}; System.out.println(Arrays.toString(intA)); insertionSort(intA); System.out.println(Arrays.toString(intA)); } public static void insertionSort(int[] a) { int p,right; int temp; for (p = 0; p < a.length; p++) { temp = a[p]; /** * 將a[p]值往左側有序列比較,插入。 */ for (right = p; right > 0 && a[right-1] > temp ; right--) a[right] = a[right-1];// 置換 a[right] = temp; } }}
右鍵,run一下可以看到控制台結果:
[2, 1, 3, 4, 6, 7, 5][1, 2, 3, 4, 5, 6, 7]
② 快速排序
快排是基於分治策略的演算法,不是一種穩定的排序演算法,也就是說,多個相同的值的相對位置也許會在演算法結束時產生變動。 Java版實現如下:
package javaBasic.algorithm; import java.util.Arrays; public class QuickSort{ public static void main(String[] args) { int[] intA = new int[]{2,1,3,4,6,7,5}; System.out.println(Arrays.toString(intA)); //middleSort(intA, 0, intA.length - 1); //System.out.println(Arrays.toString(intA)); sort(intA, 0, intA.length - 1); System.out.println(Arrays.toString(intA)); } // 快速排序中的一個劃分過程 public static int middleSort(int a[] , int left , int right) { int temp = a[left]; // 作為中間軸數 while( left < right) { /** * 從右至左,找到第一個比中間軸數小的,移到左端 */ while( left < right && a[right] > temp ) right--; a[left] = a[right]; /** * 從左至右,找到第一個比中間軸數大的,移到右端 */ while( left < right && a[left] < temp) left++; a[right] = a[left]; } /** * 將中間軸數賦值 */ a[left] = temp; return left; } // 快速排序 public static void sort(int[] a , int left, int right) { if (left < right) { /** * 根據左右索引相同才停止。 * 不同的話,按著分治思想。 * 找到中間軸數,一分為二,以此類推。 */ int middle = middleSort(a, left, right); sort(a, left, middle - 1); sort(a, middle + 1, right); } } }
記憶方法:分治,就是分工。這裡示範的是對分。大量經驗資料表面,採用兩個樞軸來劃分成3份的演算法更高效,這就是DualPivotQuicksort。這樣也是我們後面講的JDK源碼。右鍵,run一下可以看到控制台和插入排序一樣的結果。
③ 歸併排序
,來自百度百科。歸併排序也是一種分治思想的演算法,之不用快速是對分。歸併是一種分解到合并的演算法。如下實現方式:
package javaBasic.algorithm; import java.util.Arrays; public class MergeSort{ public static void main(String[] args) { int[] intA = new int[]{10,4,6,3,8,2,5,7}; System.out.println(Arrays.toString(intA)); mergeSort(intA,0,intA.length-1); System.out.println(Arrays.toString(intA)); } public static void mergeSort(int[] a, int left ,int right) { if (left < right) { int middle = (left + right) / 2; // 中間索引 mergeSort(a, left, middle); // 對左側數組遞迴 mergeSort(a, middle+1, right); // 對右側數組遞迴 merge(a,left,middle,right); // 歸併演算法 } } private static void merge(int[] a, int left, int middle, int right) { int [] tmpArr = new int[a.length]; int mid = middle+1; int tmpArrLeft = left;// 記錄左側數組的索引 int tmpLeft = left; /** * 從兩個數組中取出小的一部分複製 */ while (left <= middle && mid <= right) { if (a[left] <= a[mid]) tmpArr[tmpArrLeft++] = a[left++]; else tmpArr[tmpArrLeft++] = a[mid++]; } /** * 剩餘部分右側複製 */ while (mid <= right) { tmpArr[tmpArrLeft++] = a[mid++]; } /** * 剩餘部分左側複製 */ while (left <= middle) { tmpArr[tmpArrLeft++] = a[left++]; } /** * 分了再合 */ while(tmpLeft <= right) { a[tmpLeft] = tmpArr[tmpLeft++]; } } }
結果和一樣:
[10, 4, 6, 3, 8, 2, 5, 7][2, 3, 4, 5, 6, 7, 8, 10]
三、JDK數則
在此謝謝@江南白衣大哥的文章,對我協助很大。以下引用的:
5. JDK7/8中排序演算法的改進面試季的同學背一腦袋的插入、歸併、冒泡、快排,那,JDK到底看上了哪家的排序演算法? Colletions.sort(list) 與 Arrays.sort(T[])Colletions.sort()實際會將list轉為數組,然後調用Arrays.sort(),排完了再轉回List。而Arrays.sort(),對原始類型(int[],double[],char[],byte[]),JDK6裡用的是快速排序,對於物件類型(Object[]),JDK6則使用歸併排序。為什麼要用不同的演算法呢? JDK7的進步到了JDK7,快速排序升級為雙基準快排(雙基準快排 vs 三路快排);歸併排序升級為歸併排序的改進版TimSort,一個JDK的自我進化。 JDK8的進步再到了JDK8, 對大集合增加了Arrays.parallelSort()函數,使用fork-Join架構,充分利用多核,對大的集合進行切分然後再歸併排序,而在小的連續片段裡,依然使用TimSort與DualPivotQuickSort。 結論JDK團隊的努力,從一些簡單的New Features / Change List 根本看不到,所以沒事升級一下JDK還是好的
我也查看了關於演算法追蹤到DualPivotQuicksort類,但是這類卻不在JDK API。(拋出個問題:為什麼這個類不出現在API裡面?)哈哈,一看作者是Java之父參與寫的,瞬間有研究的激情。根據白衣大哥說的,快速排序由雙基準排序到三路快速排序。這也是在大量經驗資料表面,採用兩個樞軸來劃分成3份的演算法更高效。演算法的思想也是分治思想。
下面又看到了一段發人自省的注釋:
/** * If the length of an array to be sorted is less than this * constant, Quicksort is used in preference to merge sort. * 當數組長度小於286,為什麼快速排序比歸併排序好? */ private static final int QUICKSORT_THRESHOLD = 286; /** * If the length of an array to be sorted is less than this * constant, insertion sort is used in preference to Quicksort. * 當數組長度小於47,為什麼插入排序比快速排序好? */ private static final int INSERTION_SORT_THRESHOLD = 47;
為什嗎?第二個問題,歡迎大神解答。
我的理解:第一,建立在大量經驗資料結果。第二,根據演算法時間複雜度和空間複雜度。至於深入瞭解需要大神解答。
JDK排序次序圖如下:
轉載自:http://www.bysocket.com/?p=219
(轉載)Java 容器 & 泛型:四、Colletions.sort 和 Arrays.sort 的演算法