排序演算法整理:氣泡排序、堆排序、插入排序、歸併操作、快速排序、希爾排序、選擇排序
阿新 • • 發佈:2019-01-04
package prms.utils.sort; import java.util.Arrays; /** * @ClassName: SortUtils * @Description: <p> 排序演算法 工具類 </p> * @author edgar * @email 【[email protected]】 * @version V 1.0 * @date 2017-3-28 15:35:12 */ public class SortUtils { /** * @Title: SortUtils * @Description: 私有化構造 :類不能例項化 */ private SortUtils(){} public static void main(String[] args) { int[] arr = { 2, 5, 3, 1, 4 }; System.out.println("排序前:" + Arrays.toString(arr)); bubbleSort(arr); heapSort(arr); insertSort(arr); mergeSort(arr); quickSort(arr); selectionSort(arr); shellSort(arr); System.out.println("排序後:" + Arrays.toString(arr)); } /** * @Title: bubbleSort * @Description: 氣泡排序 * 依次比較相鄰的兩個數,將小數放在前面,大數放在後面。 * 即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。 * 然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續, * 直至比較最後兩個數,將小數放前,大數放後。至此第一趟結束, * 將最大的數放到了最後。在第二趟:仍從第一對數開始比較 * (因為可能由於第2個數和第3個數的交換,使得第1個數不再小於第2個數), * 將小數放前,大數放後,一直比較到倒數第二個數(倒數第一的位置上已經是最大的), * 第二趟結束,在倒數第二的位置上得到一個新的最大數 * (其實在整個數列中是第二大的數)。如此下去,重複以上過程,直至最終完成排序。 * @param data * @return void */ public static void bubbleSort(int[] data){ for (int i = 0; i < data.length - 1; i++) { for (int j = 0; j < data.length - 1 - i; j++) { if (data[j] > data[j + 1]) { swap(data, j, j + 1); } } } } /** * @Title: heapSort * @Description: 堆排序利用了大根堆(或小根堆)堆頂記錄的關鍵字最大(或最小)這一特徵, 使得在當前無序區中選取最大(或最小)關鍵字的記錄變得簡單。 * (1)用大根堆排序的基本思想 * ① 先將初始檔案R[1..n]建成一個大根堆,此堆為初始的無序區 * ②再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個 記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n], * 且滿足R[1..n-1].keys≤R[n].key ③由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整為堆。 * 然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換, * 由此得到新的無序區R[1..n-2]和有序區R[n-1..n], * 且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整為堆。 直到無序區只有一個元素為止。 * (2)大根堆排序演算法的基本操作: * ① 初始化操作:將R[1..n]構造為初始堆; * ②每一趟排序的基本操作:將當前無序區的堆頂記錄R[1]和該區間的最後一個記錄交換, 然後將新的無序區調整為堆(亦稱重建堆)。 * * @param data * @return void */ public static void heapSort(int[] data) { MaxHeap h = new MaxHeap(); h.init(data); for (int i = 0; i < data.length; i++) h.remove(); System.arraycopy(h.queue, 1, data, 0, data.length); } private static class MaxHeap { private int size = 0; private int[] queue; void init(int[] data) { this.queue = new int[data.length + 1]; for (int i = 0; i < data.length; i++) { queue[++size] = data[i]; fixUp(size); } } @SuppressWarnings("unused") public int get() { return queue[1]; } public void remove() { swap(queue, 1, size--); fixDown(1); } // fixdown private void fixDown(int k) { int j; while ((j = k << 1) <= size) { if (j < size && queue[j] < queue[j + 1]) j++; if (queue[k] > queue[j]) // 不用交換 break; swap(queue, j, k); k = j; } } private void fixUp(int k) { while (k > 1) { int j = k >> 1; if (queue[j] > queue[k]) break; swap(queue, j, k); k = j; } } } /** * @Title: insertSort * @Description: 插入排序基本思想 * 將n個元素的數列分為已有序和無序兩個部分,如插入排序過程示例下所示: * {{a1},{a2,a3,a4,…,an}} * {{a1⑴,a2⑴},{a3⑴,a4⑴ …,an⑴}} * {{a1(n-1),a2(n-1) ,…},{an(n-1)}} * 每次處理就是將無序數列的第一個元素與有序數列的元素從後往前逐個進行比較, * 找出插入位置,將該元素插入到有序數列的合適位置中。 * @param data * @return void */ public static void insertSort(int[] data) { for (int i = 1; i < data.length; i++) { for (int j = i; (j > 0) && (data[j] < data[j - 1]); j--) { swap(data, j, j - 1); } } } /** * @Title: mergeSort * @Description: 歸併操作(merge),也叫歸併演算法,指的是將兩個已經排序的序列合併成一個序列的操作。 * 如設有數列{6,202,100,301,38,8,1} * 初始狀態: [6] [202] [100] [301] [38] [8] [1] 比較次數 * i=1 [6 202 ] [ 100 301] [ 8 38] [ 1 ] 3 * i=2 [ 6 100 202 301 ] [ 1 8 38 ] 4 * i=3 [ 1 6 8 38 100 202 301 ] 4 * @param data * @return void */ public static void mergeSort(int[] data) { int[] temp = new int[data.length]; mergeSort(data, temp, 0, data.length - 1); } private static void mergeSort(int[] data, int[] temp, int l, int r) { int mid = (l + r) / 2; if (l == r){ return; } mergeSort(data, temp, l, mid); mergeSort(data, temp, mid + 1, r); for (int i = l; i <= r; i++) { temp[i] = data[i]; } int i1 = l; int i2 = mid + 1; for (int cur = l; cur <= r; cur++) { if (i1 == mid + 1){ data[cur] = temp[i2++]; }else if (i2 > r){ data[cur] = temp[i1++]; }else if (temp[i1] < temp[i2]){ data[cur] = temp[i1++]; }else{ data[cur] = temp[i2++]; } } } /** * @Title: quickSort * @Description:快速排序 * 一趟快速排序的演算法是: * 1)設定兩個變數i、j,排序開始的時候:i=0,j=N-1; * 2)以第一個陣列元素作為關鍵資料,賦值給key,即 key=A[0]; * 3)從j開始向前搜尋,即由後開始向前搜尋(j=j-1即j--),找到第一個小於key的值A[j],A[i]與A[j]交換; * 4)從i開始向後搜尋,即由前開始向後搜尋(i=i+1即i++),找到第一個大於key的A[i],A[i]與A[j]交換; * 5)重複第3、4、5步,直到 I=J; (3,4步是在程式中沒找到時候j=j-1,i=i+1,直至找到為止。 * 找到並交換的時候i, j指標位置不變。另外當i=j這過程一定正好是i+或j-完成的最後令迴圈結束。) * @param data * @return void */ public static void quickSort(int[] data) { quickSort(data, 0, data.length - 1); } private static void quickSort(int[] data, int i, int j) { int pivotIndex = (i + j) / 2; // swap swap(data, pivotIndex, j); int k = partition(data, i - 1, j, data[j]); swap(data, k, j); if ((k - i) > 1){ quickSort(data, i, k - 1); } if ((j - k) > 1){ quickSort(data, k + 1, j); } } /** * @param data * @param i * @param j * @return */ private static int partition(int[] data, int l, int r, int pivot) { do { while (data[++l] < pivot); while ((r != 0) && data[--r] > pivot); swap(data, l, r); } while (l < r); swap(data, l, r); return l; } /** * @Title: shellSort * @Description: 希爾排序: * 先取一個小於n的整數d1作為第一個增量,把檔案的全部記錄分成(n除以d1)個組。所有距離為d1的倍數的記錄放在同一個組中。 * 先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1), * 即所有記錄放在同一組中進行直接插入排序為止。 * * * 屬於插入類排序,是將整個無序列分割成若干小的子序列分別進行插入排序 * 排序過程:先取一個正整數d1<n,把所有序號相隔d1的陣列元素放一組, * 組內進行直接插入排序;然後取d2<d1,重複上述分組和排序操作;直至di=1, 即所有記錄放進一個組中排序為止 * 初始:d=5 49 38 65 97 76 13 27 49 55 04 * 49 13 |-------------------| * 38 27 |-------------------| * 65 49 |-------------------| * 97 55 |-------------------| * 76 04 |-------------------| * 一趟結果 13 27 49 55 04 49 38 65 97 76 * d=3 13 27 49 55 04 49 38 65 97 76 * 13 55 38 76 |------------|------------|------------| * 27 04 65 |------------|------------| * 49 49 97 |------------|------------| * 二趟結果 13 04 49* 38 27 49 55 65 97 76 * d=1 13 04 49 38 27 49 55 65 97 76 * |----|----|----|----|----|----|----|----|----| 三趟結果 * 04 13 27 38 49 49 55 65 76 97 * @param data * @return void */ public static void shellSort(int[] data) { for (int i = data.length / 2; i > 2; i /= 2) { for (int j = 0; j < i; j++) { insertSort(data, j, i); } } insertSort(data, 0, 1); } /** * @param data * @param j * @param i */ private static void insertSort(int[] data, int start, int inc) { for (int i = start + inc; i < data.length; i += inc) { for (int j = i; (j >= inc) && (data[j] < data[j - inc]); j -= inc) { swap(data, j, j - inc); } } } /** * @Title: selectionSort * @Description: 選擇排序基本思路: * 把第一個元素依次和後面的所有元素進行比較。第一次結束後,就會有最小值出現在最前面。依次類推 * @param data * @return void */ public static void selectionSort(int[] data) { for (int x = 0; x < data.length - 1; x++) { for (int y = x + 1; y < data.length; y++) { if (data[y] < data[x]) { swap(data, x, y); } } } } /** * @Title: swap * @Description: 交換陣列中的兩個元素 * @param data * @param i * @param j * @return void */ public static void swap(int[] data, int i, int j) { int temp = data[i]; data[i] = data[j]; data[j] = temp; } }