對一些常見的排序演算法進行演算測試,對比效率。(JAVA 實現)
阿新 • • 發佈:2018-11-16
大三下了,正在找實習,把一些常用的排序演算法重新複習一遍,同時簡單的比較了一下各個演算法的效率,總的來說,快排效率最高,冒泡效率最低。-----具體的排序演算法包括:直接插入排序、折半插入排序、Shell排序(插入排序) 冒泡、快速排序(交換排序)
歡迎大家一起討論,如有錯誤務必指出~~~
先驗證排序演算法的正確性:
接著比較效率(註釋掉了列印陣列的功能):
得出結論:當待排數的數量較大時(示例中採用102400個隨機整數),效率對比為:
快速排序 > 折半插入排序 > Shell排序 > 直接插入排序 > 氣泡排序
package com.ky415.loop; import java.util.Arrays; import java.util.Random; import java.util.Scanner; /** * 測試陣列的常用方法:最大值、求和、平均值、陣列的翻轉 * 使用多種排序方法對輸入的陣列進行排序(升序) * 依次包括:直接插入排序、折半插入排序、Shell排序(插入排序) * 冒泡、快速排序(交換排序) * @author Mypc */ public class TestArray { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("請輸入需要生成的隨機數的個數:"); int count = input.nextInt(); Random random = new Random(); int[] number = new int[count]; for (int i = 0; i < number.length; i++) { number[i] = random.nextInt(); } // System.out.println("生成的隨機數如下:"); // printArray(number); System.out.println("對" + count + "個數進行排序,各個排序所需時間的對比如下" ); directInsertSort(number); halfInsertSort(number); shellSort(number); bubbleSort(number); quickSort(number); } /** * 列印整型陣列的值 * @param number */ public static void printArray(int [] number) { int time = 0; for(int i : number) { System.out.print(i + "\t"); time ++; if(time % 5 == 0) { time = 0; System.out.println(); } } System.out.println(); } /** * 測試陣列的常用方法:最大值、求和、平均值、陣列的翻轉 * @param number */ public static void arrayCommonMethod(int[] number) { //求陣列最大值 int[] number1 = number; System.out.print("陣列的最大值為:"); int max = number1[0]; for (int i = 1; i < number1.length; i++) { max = number1[i] > max ? number1[i] : max; } System.out.println(max); //求數值所有的元素之和 System.out.print("陣列的和為:"); int sum = number1[0]; for (int i = 1; i < number1.length; i++) { sum += number1[i]; } System.out.println(sum); //求陣列元素的平均值 System.out.print("陣列的平均值為:"); int avge = 0; for (int i : number1) { avge += i; } System.out.println(avge / number1.length); //對陣列進行翻轉 System.out.print("陣列翻轉後為:"); int[] number2 = new int[number.length]; for (int i = 0, j = number1.length - 1; j >= 0; i++, j--) { number2[i] = number1[j]; } for(int i : number2) { System.out.print(i + "\t"); } } /** * 直接插入排序 * --基本思想是每一趟將一個待排序的記錄,按其關鍵字的大小插入到已經排好序的一組記錄的適當位置上,直到所有待排序記錄全部插入為止。 * @param number */ public static void directInsertSort(int[] number) { //用以記錄排序開始的時間 long start = System.currentTimeMillis(); int[] number1 = new int[number.length]; for (int i = 0, j = number.length - 1; j >= 0; i++, j--) { number1[i] = number[j]; } for (int i = 1; i < number1.length; i++) { //判斷當前值是否需要移動 if(number1[i] < number1[i - 1]) { //記錄需要移動的關鍵字位置 int moveSubscript = i; //暫存需要移動的關鍵字 int temp = number1[moveSubscript]; //將已經排序好的記錄,從需要移動的關鍵字前一個開始,將每一個比關鍵字大的元素都向後移一位 for (int j = i - 1; j >= 0 && number1[j] > temp; j--) { moveSubscript = j; number1[j + 1] = number1[j]; } //將需要移動的關鍵字,放置在最終的位置上 number1[moveSubscript] = temp; } } //用以記錄排序完成的時間 long end = System.currentTimeMillis(); System.out.println("直接插入排序--"+ "完成排序需要的時間為:" + (end - start) +" :"); } /** * 折半插入排序 -- 折半插入演算法是對直接插入排序演算法的改進,排序原理同直接插入演算法。 * 兩者區別在於:在有序表中尋找待排序資料的正確位置時,使用了折半查詢/二分查詢。 * @param number */ public static void halfInsertSort(int [] number) { //構建一個新的陣列進行排序操作 int[] number1 = new int[number.length]; for (int i = 0, j = number.length - 1; j >= 0; i++, j--) { number1[i] = number[j]; } //用以記錄排序開始的時間 long start = System.currentTimeMillis(); for (int i = 1; i < number1.length; i++) { //判斷當前值是否需要移動 if(number1[i] < number1[i - 1]) { //暫存需要移動的關鍵字 int temp = number1[i]; //查詢需要移動的關鍵字應該插入的位置 目標位置為low int low = 0, high = i - 1, mid = 0; while(low <= high){ mid = (low + high) / 2; if(number1[mid] < temp) { low = mid + 1; } else { high = mid - 1; } } //將已經排序好的記錄,從需要移動的關鍵字前一個開始,將每一個比關鍵字大的元素都向後移一位 for (int j = i;j > low; j--) { number1[j] = number1[j - 1]; } //將需要移動的關鍵字,放置在最終的位置上 number1[low] = temp; } } //用以記錄排序完成的時間 long end = System.currentTimeMillis(); System.out.println("折半插入排序--"+ "完成排序需要的時間為:" + (end - start) +" :"); } /** * Shell排序--也是對直接插入排序的改進; 基本思想:先將整個待排元素序列切割成若干個子序列(由相隔某個“增量”的元素組成的) * 分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。 * @param number */ public static void shellSort(int[] number) { //構建一個新的陣列進行排序操作 int[] number1 = new int[number.length]; for (int i = 0, j = number.length - 1; j >= 0; i++, j--) { number1[i] = number[j]; } //用以記錄排序開始的時間 long start = System.currentTimeMillis(); //設定每次分組的大小--當 gap 為 0 時排序完成 for(int gap = number1.length / 2; gap > 0; gap /= 2) { for(int j = gap; j < number1.length; j += gap) { if(number1[j] < number1[j - gap]) { int temp = number1[j]; int k = j; while(k - gap >= 0 && number1[k - gap] > temp) { number1[k] = number1[k - gap]; k -= gap; } number1[k] = temp; } } } //用以記錄排序完成的時間 long end = System.currentTimeMillis(); System.out.println("Shell排序--"+ "完成排序需要的時間為:" + (end - start) +" :"); // printArray(number1); } /** * 氣泡排序--依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。 * 然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。重複第一趟步驟,直至全部排序完成。 * @param number */ public static void bubbleSort(int[] number) { //構建一個新的陣列進行排序操作 int[] number1 = new int[number.length]; for (int i = 0, j = number.length - 1; j >= 0; i++, j--) { number1[i] = number[j]; } //用以記錄排序開始的時間 long start = System.currentTimeMillis(); for (int i = 0; i < number1.length -1; i++) { for (int j = 0; j < number1.length - 1 - i; j++) { //遇到不滿足排序要求的數,交換兩個數的位置 if(number1[j] > number1[j + 1]) { int temp = number1[j]; number1[j] = number1[j + 1]; number1[j + 1] = temp; } } } //用以記錄排序完成的時間 long end = System.currentTimeMillis(); System.out.println("氣泡排序--"+ "完成排序需要的時間為:" + (end - start) +" :"); // printArray(number1); } /** * 基本思想主要分為三個步驟:1、先從數列中取出一個數作為基準數(標兵-通常取第一個數), * 2、先從右到左找到第一個比基準數小數,然後停下來(大哨兵移動 j--),從左到右找到第一個比基準數大的數,然後停下來(小哨兵移動 i++) * 此時大小哨兵交換值,然後繼續移動,直到大小哨兵相遇(i >= j),然後以此值為界限,將數列進行左右兩區 * 3、再對左右區間重複第二步,直到各區間只有一個數 * @param number */ public static void quickSort(int [] number) { //構建一個新的陣列進行排序操作 int[] number1 = new int[number.length]; for (int i = 0, j = number.length - 1; j >= 0; i++, j--) { number1[i] = number[j]; } //用以記錄排序開始的時間 long start = System.currentTimeMillis(); //呼叫快速排序演算法 quick(number1, 0, number1.length - 1); //用以記錄排序完成的時間 long end = System.currentTimeMillis(); System.out.println("快速排序--"+ "完成排序需要的時間為:" + (end - start) +" :"); // printArray(number1); } /** * 對陣列的 low 到 high 的區間進行一次快速排序 * @param number 陣列名 * @param low 快排區間開始的下標 * @param high 快排區間結束的下標 */ public static void quick(int[] number, int low, int high) { //遞迴跳出條件--所有的數都已經歸位 if(low >= high) { return ; } //取出基準數(標兵) int key = number[low]; //取左右標兵 int left = low; int right = high; //只有當左哨兵與右哨兵相等時,才結束迴圈 while(left < right) { //右哨兵左移--直到遇見一個比key小的值 while(number[right] >= key && right > left) { right --; } //左哨兵右移--直到遇見一個比key大的值 while (number[left] <= key && right > left) { left ++; } //交換左右哨兵的值 int temp = number[right]; number[right] = number[left]; number[left] = temp; } //將標兵置於最終的位置--標兵歸位 number[low] = number[left]; number[left] = key; //對餘下的左右兩部分繼續進行快排 quick(number, low, left - 1); quick(number, left + 1, high); } }