1. 程式人生 > >對一些常見的排序演算法進行演算測試,對比效率。(JAVA 實現)

對一些常見的排序演算法進行演算測試,對比效率。(JAVA 實現)

大三下了,正在找實習,把一些常用的排序演算法重新複習一遍,同時簡單的比較了一下各個演算法的效率,總的來說,快排效率最高,冒泡效率最低。-----具體的排序演算法包括:直接插入排序、折半插入排序、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);
	}
}