算法07:常用算法排序

常用排序算法

一、十種排序算法比較

​ 排序算法可以分爲內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序算法有:插入排序、希爾排序、選擇排序、冒泡排序、歸併排序、快速排序、堆排序、基數排序等。以下爲複雜度對比:

在這裏插入圖片描述

​ 關於穩定性的解釋:穩定性爲排序後 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;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章