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