一、十種排序算法比較
排序算法可以分爲內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序算法有:插入排序、希爾排序、選擇排序、冒泡排序、歸併排序、快速排序、堆排序、基數排序等。以下爲複雜度對比:
關於穩定性的解釋:穩定性爲排序後 2 個相等鍵值的順序和排序之前它們的順序相同
二、排序算法的選擇
從上圖可以明顯的看出各種算法的優劣,在時間複雜度和空間複雜度兩者之間很難有兼顧,所以只能根據需要選擇。因此,不必記下所有算法,下面我用Java寫了其中的四種常用的算法。
三、常用算法代碼
0. 輸入輸出函數的封裝
將一行數字讀入,並按照空格分開,形成數組的方法抽象,方便其他所有排序算法的調用。同時,輸出方法也抽象於此,以備實例化調用。
import java.util.Scanner;
/**
* @description: 提供輸入和輸出方法
* @author: 宇智波Akali
* @time: 2020/4/6 15:39
* @fromProject: 10_sort_Methods
* @Version: V1.0
*/
public class In_and_Out {
public int[] setList(){
/**
* @Description: 將序列格式化爲數組
* @auther: 宇智波Akali
* @date: 15:43 2020/4/6
* @param: []
* @return: int[]
*/
System.out.println("請輸出數組:");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine(); //讀入一行字符串
String[] str_line = input.split(" ");//將字符串按空格分隔成字符串數組
int[] list = new int[str_line.length];
for (int i=0;i<list.length;i++){
list[i] = Integer.parseInt(str_line[i]);
}
return list;
}
public void printList(int[] list){
/**
* @Description: 傳入排好序的數組,執行打印
* @auther: 宇智波Akali
* @date: 15:52 2020/4/6
* @param: [list]
* @return: void
*/
for (Integer x: list){
System.out.print(x + " ");
}
}
}
1. 冒泡排序
算法步驟
-
比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
-
對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
-
針對所有的元素重複以上的步驟,除了最後一個。
-
持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
代碼
public class Bubble_Sort_01 {
public static void main(String[] args) {
int [] list = new In_and_Out().setList(); //創建匿名對象調用In_and_Out中的setList方法
new In_and_Out().printList(bubble_Sort(list));//創建匿名對象調用其中的printLis方法
}
//****************************算法部分*********************************
public static int[] bubble_Sort(int[] list){
boolean trigger = true; //使用trigger判斷原始序列是否已經有序
for (int i=list.length-1 ;i>=0;i--){
for (int j=0;j<i;j++){
if (list[j] > list[j+1]){ //將大於號改爲小於則變成從大到小排序
int temp;
temp = list[j+1];
list[j+1] = list[j];
list[j] = temp;
trigger = false;
}
}
if (trigger) break;
}
return list;
}
}
2. 插入排序
算法步驟
-
將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。
-
從頭到尾依次掃描未排序序列,將掃描到的每個元素插入有序序列的適當位置。(如果待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)
代碼
public class Bubble_Sort_01 {
public static void main(String[] args) {
int [] list = new In_and_Out().setList(); //創建匿名對象調用In_and_Out中的setList方法
new In_and_Out().printList(bubble_Sort(list));//創建匿名對象調用其中的printLis方法
}
//****************************算法部分*********************************
public static int[] bubble_Sort(int[] list){
boolean trigger = true; //使用trigger判斷原始序列是否已經有序
for (int i=list.length-1 ;i>=0;i--){
for (int j=0;j<i;j++){
if (list[j] > list[j+1]){ //將大於號改爲小於則變成從大到小排序
int temp;
temp = list[j+1];
list[j+1] = list[j];
list[j] = temp;
trigger = false;
}
}
if (trigger) break;
}
return list;
}
}
3. 歸併排序
算法步驟
-
申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列;
-
設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置;
-
比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置;
-
重複步驟 3 直到某一指針達到序列尾;
-
將另一序列剩下的所有元素直接複製到合併序列尾。
代碼
import java.util.Arrays;
public class Merge_Sort_03 {
public static void main(String[] args) {
int[] list = new In_and_Out().setList();//讀入並格式化List
new In_and_Out().printList(merge_sort(list));//打印
}
//********************算法部分*********************************
public static int[] merge_sort(int[] list){
if (list.length < 2){ //遞歸邊界:數組長度小於2,將數組遞歸劃分爲單個元素
return list;
}
int middle = list.length/2;//定義中間下標
int[] L_list = Arrays.copyOfRange(list,0,middle); //定義左數組及內容範圍
int[] R_list = Arrays.copyOfRange(list, middle,list.length);//定義右數組及內容範圍
return merge(merge_sort(L_list), merge_sort(R_list)); //遞歸調用,進行歸併排序
}
protected static int[] merge(int[] L, int[] R){
int[] result = new int[L.length+R.length];
int i = 0;
while (L.length>0 && R.length>0){
if (L[0] <= R[0]){ //小於改爲大於則變成從大到小排序
result[i++] = L[0]; //左列的第一項小於右列第一項則向結果數組中填入L[0]
L = Arrays.copyOfRange(L,1,L.length);//將L的左邊第一個去掉,之前的第二個變成第一個
}else {
result[i++] = R[0];
R = Arrays.copyOfRange(R,1,R.length);
}
//每次左or右列長度減小1,直到兩個的長度其中有一個爲0,即有一邊數組全部加到result中
//這時候還剩一個數組,剩餘部分必然都大於之前的數組,直接加入即可
}
while (L.length > 0) {
result[i++] = L[0];
L = Arrays.copyOfRange(L, 1, L.length);
}
while (R.length > 0) {
result[i++] = R[0];
R = Arrays.copyOfRange(R, 1, R.length);
}
return result;
}
}
4. 快速排序
算法步驟
-
從數列中挑出一個元素,稱爲 “基準”(pivot);
-
重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處於數列的中間位置。這個稱爲分區(partition)操作;
-
遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序;
代碼
public class Quick_Sort_04 {
public static void main(String[] args) {
int[] list = new In_and_Out().setList(); //輸入
new In_and_Out().printList(quick_sort(list)); //輸出
}
//*************************算法部分**************************
private static int[] quick_sort(int[] list){
return sort(list, 0, list.length-1);
}
private static int[] sort(int[] list, int L, int R){
if (L < R){
int partitionIndex = partition(list, L, R); //接收一個已經確定了位置的數的下標,以此爲分界線分塊遞歸
sort(list, L, partitionIndex - 1);
sort(list, partitionIndex + 1, R);
}
return list;
}
private static int partition(int[] list, int L, int R){
int pivot_key = list[L];
while (L < R){
//一次外層循環只解決一次交換
while (L < R && list[R] >= pivot_key){
R--;
}
//將小於基準的放左邊
list[L] = list[R];
while (L < R && list[L] <= pivot_key){
L++;
}
//將大於基準的放右邊
list[R] = list[L];
}
list[L] = pivot_key; //當循環結束時,L=R,兩者重合,將pivot_key的值放在此處,就確定了一個數的位置
//返回基準最後的位置
return L;
}
}