排序主要分爲兩類:內部排序和外部排序。java
一般所說的各類排序算法其實指的是內部排序算法。內部排序是基於內存的,整個排序過程都是在內存中完成的,而外部排序指的是因爲數據量太大,內存不能徹底容納,排序的時候須要藉助外存才能完成(經常是算計着某一部分已經計算過的數據移出內存讓另外一部分未被計算的數據進入內存)。算法
在一個有序的集合中,將當前值插入到適合位置上,使得插入結束以後整個集合依然是有序的。數組
一、直接插入排序ui
將第 i 個記錄插入到前面 i-1 個已經有序的集合中。spa
i=1,一個元素確定是有序的,code
i =2 ,將第二個元素插入到前 i-1個有序集合中;blog
i =3,將第三個元素插入到前 i-1(2)集合中,等等。排序
直到去插入最後一個元素時,前面的 i-1 個元素構成的集合已經有序,因而找到第 i 個元素的合適位置插入便可,整個插入排序完成。遞歸
下面是具體的實現代碼:索引
@Test public void test1() { int[] array = {1,13,72,9,22,4,6,781,29,2}; InsertSort(array); System.out.println(Arrays.toString(array)); } private void InsertSort(int[] array) { for(int i=1;i<array.length;i++){ int temp = array[i]; int j=i-1; while(j>=0&&array[j]>temp){ //須要移動位置,將較大的值array[j]向後移動一個位置 array[j+1]=array[j]; j--; } //循環結束說明找到適當的位置了,是時候插入值了 array[j+1]=temp; } }
整個程序的邏輯是從數組的第二個元素開始,每一個元素都以其前面全部的元素爲基本,找到合適的位置進行插入。對於這種按照從小到大的排序原則,程序使用一個臨時變量temp保存當前須要插入的元素的值,從前面的子序列的最後一個元素開始,循環的與temp進行比較,一旦發現有大於temp的元素,讓它順序的日後移動一個位置,直到找到一個元素小於temp,那麼就找到合適的插入位置了。
由於咱們使用的判斷條件是,key>array[j]。因此來講,插入排序算法也是穩定的算法。對於值相同的元素並不會更改他們原來的位置順序。至於該算法的效率,最好的狀況是全部元素都已有序,比較次數爲n-1,最壞的狀況是全部元素都是逆序的,比較次數爲(n+2)(n-1)/2,因此該算法的時間複雜度爲O(n*n)。
既然咱們每次要插入的序列是有序的,咱們徹底可使用二分查找到合適位置再進行插入,這顯然要比直接插入的效率要高一些。
@Test public void test1() { int[] array = {1,13,72,9,22,4,6,781,29,2}; halfInsertSort(array); System.out.println(Arrays.toString(array)); } private void halfInsertSort(int[] array) { for(int i=1;i<array.length;i++){ int temp = array[i]; //找到合適的位置 int low =0,high=i-1,mid; while(low<=high){ mid=(low+high)/2; if(temp==array[mid]){ break; }else if(temp>array[mid]){ low=mid+1; }else{ high=mid-1; } } //low的索引位置就是即將插入的位置 //移動low索引位置後面的全部元素 for(int j=i-1;j>=low;j--){ array[j+1]=array[j]; } array[low]=temp; } }
雖然,折半插入改善了查找插入位置的比較次數,可是移動的時間耗費並無獲得改善,因此效率上優秀的量可觀,時間複雜度仍然爲O(n*n)。
二、希爾排序
它將待排序序列拆分紅多個子序列,保證每一個子序列的組成元素相對較少,而後經過對子序列使用直接排序。
希爾排序算法使用一個距離增量來切分子序列。
如圖,咱們初始有一個序列,按照距離增量爲4來拆分的話,能夠將整個序列拆分紅四個子序列,咱們對四個子序列內部進行直接插入排序獲得結果以下:
修改距離增量從新劃分子序列:
很顯然,當距離增量變小的時候,序列的個數也會變少,可是這些子序列的內部都基本有序,當對他們進行直接插入排序的時候會使得效率變高。一旦距離增量減小爲1,那麼子序列的個數也將減小爲1,也就是咱們的原序列,而此時的序列內部基本有序,最後執行一次直接插入排序完成整個排序操做。
/*漸減delete的值*/ public static void ShellSort(){ int[] array = {46,55,13,42,94,17,5,70}; int[] delets = {4,2,1}; for (int i=0;i<delets.length;i++){ oneShellSort(array,delets[i]); } //遍歷輸出數組內容 for(int value : array){ System.out.print(value + ","); } } /*根據距離增量的值劃分子序列並對子序列內部進行直接插入排序*/ public static void oneShellSort(int[] array,int delet){ int temp; for(int i=delet;i<array.length;i++){ //從第二個子序列開始交替進行直接的插入排序 //將當前元素插入到前面的有序隊列中 if(array[i-delet] > array[i]){ temp = array[i]; int j=i-delet; while(j>=0 && array[j] > temp){ array[j+delet] = array[j]; j -= delet; } array[j + delet] = temp; } } }
利用兩個元素之間的值的大小進行比較運算,而後移動外置實現的,這類排序算法主要有兩種:冒泡排序+快速排序
一、冒泡排序
冒泡排序經過兩兩比較,每次將最大或者最小的元素移動到整個序列的一端:
冒泡排序的算法實現以下:【排序後,數組從小到大排列】
@Test public void test1() { int[] array = {49,38,65,97,76,13,27,49}; bubbleSort(array); System.out.println(Arrays.toString(array)); } public void bubbleSort(int[] array){ for(int i=0;i<array.length-1;i++){ for(int j=0;j<array.length-1-i;j++){ if(array[j]>array[j+1]){ int temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
2.快速排序
經過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分關鍵字小,則分別對這兩部分繼續進行排序,直到整個序列有序。
@Test public void test1() { int[] array = {49, 38, 65, 97, 76, 13, 27, 49}; quickSort(array, 0, array.length - 1); System.out.println(Arrays.toString(array)); } public void quickSort(int[] a, int low, int high) { //1,找到遞歸算法的出口 if (low > high) { return; } //2, 存 int i = low; int j = high; //3,key int key = a[low]; //4,完成一趟排序 while (i < j) { //4.1 ,從右往左找到第一個小於key的數 while (i < j && a[j] > key) { j--; } // 4.2 從左往右找到第一個大於key的數 while (i < j && a[i] <= key) { i++; } //4.3 交換 if (i < j) { int p = a[i]; a[i] = a[j]; a[j] = p; } } // 4.4,調整key的位置 int p = a[i]; a[i] = a[low]; a[low] = p; //5, 對key左邊的數快排 quickSort(a, low, i - 1); //6, 對key右邊的數快排 quickSort(a, i + 1, high); }