Java 排序算法 - 冒泡、快速、選擇、插入

冒泡排序

    依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。重複以上過程,仍從第一對數開始比較(因爲可能由於第2個數和第3個數的交換,使得第1個數不再大於第2個數),將小數放前,大數放後,一直比較到最小數前的一對相鄰數,將小數放前,大數放後,第二趟結束,在倒數第二個數中得到一個新的最小數。如此下去,直至最終完成排序。
    由於在排序過程中總是小數往前放,大數往後放,相當於氣泡往上升,所以稱作冒泡排序。

冒泡排序動態圖:
這裏寫圖片描述
冒泡排序核心思路代碼:

public static void sort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int minVal = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = minVal;
            }
        }
    }
}

全部代碼:

package com.ggsddu.domain.sort;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;

public class BubbleSort {
    public static void main(String[] args) {
        System.out.print("輸入數組長度:");
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] arr = new int[n];
        Random random = new Random();
        for (int i = 0; i < arr.length; i++) {
            arr[i] = random.nextInt(100);
        }
        scanner.close();

        System.out.println("排序前:" + Arrays.toString(arr));
        System.out.println("--------------------- 排序結束 ---------------------");
        sort(arr);
        System.out.println("--------------------- 排序結束 ---------------------");
        System.out.println("排序後:" + Arrays.toString(arr));
    }

    public static void sort(int[] arr) {
        /**
         * 假設數組有10個元素,外層循環9次,每輪排序將此輪參與比較元素的最大值“冒泡”出去
         * ----- 排序開始
         * 第一輪排序:從10個元素中比較出最大值,將最大值“冒泡”到數組arr[9]的位置
         * 第二輪排序:從9個元素中(已“冒泡”元素不參與後續排序)比較出最大值,“冒泡”到數組arr[8]的位置
         * 第三輪排序:從8個元素中比較出最大值,“冒泡”到最後數組arr[7]的位置
         * ...
         * 第八輪排序:從3個元素中比較出最大值,“冒泡”到最後數組arr[2]的位置
         * 第九輪排序:從2個元素中比較最大值,“冒泡”到數組arr[1]的位置,則arr[0]最小不動,排在arr[0] 
         * ----- 排序結束
         */
        for (int i = 0; i < arr.length - 1; i++) {
            /**
             * 內層循環控制當前輪排序的參與比較的元素(已“冒泡”元素不再參與後續排序)
             * 第一輪排序,參與排序的元素個數爲arr.length - 1 - 0
             * 第二輪排序,參與排序的元素個數爲arr.length - 1 - 1
             * 第三輪排序,參與排序的元素個數爲arr.length - 1 - 2
             * ...
             * 第i輪排序,參與排序的元素個數爲arr.length - 1 - i
             */
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int minVal = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = minVal;
                }
            }
            System.out.println("第 " + (i + 1) + " 次排序:" + Arrays.toString(arr));
        }
    }
}

 
控制檯打印結果:

輸入數組長度:10
排序前:[64, 89, 36, 57, 82, 70, 40, 61, 81, 15]
--------------------- 排序開始 ---------------------
第 1 次排序:[64, 36, 57, 82, 70, 40, 61, 81, 15, 89]
第 2 次排序:[36, 57, 64, 70, 40, 61, 81, 15, 82, 89]
第 3 次排序:[36, 57, 64, 40, 61, 70, 15, 81, 82, 89]
第 4 次排序:[36, 57, 40, 61, 64, 15, 70, 81, 82, 89]
第 5 次排序:[36, 40, 57, 61, 15, 64, 70, 81, 82, 89]
第 6 次排序:[36, 40, 57, 15, 61, 64, 70, 81, 82, 89]
第 7 次排序:[36, 40, 15, 57, 61, 64, 70, 81, 82, 89]
第 8 次排序:[36, 15, 40, 57, 61, 64, 70, 81, 82, 89]
第 9 次排序:[15, 36, 40, 57, 61, 64, 70, 81, 82, 89]
--------------------- 排序結束 ---------------------
排序後:[15, 36, 40, 57, 61, 64, 70, 81, 82, 89]

Process finished with exit code 0

快速排序

    快速排序由 C. A. R. Hoare 在1962年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。

借一張大佬的圖,傳送門:http://blog.csdn.net/it_zjyang/article/details/53406764
初始數組:[3, 7, 2, 9, 1, 4, 6, 8, 10, 5]
這裏寫圖片描述
流程模擬:
初始數組 arr:[3, 7, 2, 9, 1, 4, 6, 8, 10, 5],期望結果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  • 選定數組的最後一個元素 5 作爲基準值,也就是 最終排序結果 應該是以 5 爲界限劃分爲左右兩邊且有序

    1. 從左邊開始,尋找比 5 大的值,然後與 5 進行調換(比 5小的值保持原狀,排在 5 前面,比 5 大的值與 5 調換位置排 5 的後面),此輪找到 7,將 75 調換,結束此次遍歷。

    2. 從右邊開始,由於 7 已經是上一輪排好序的便不再動它,此輪從 10開始,向左遍歷,尋找比 5 小的值,然後與 5 進行調換(比 5 大的值保持原狀,排在 5 後面,比 5 小的值與 5 調換位置排 5 的後前面),此輪找到 4,將 45 調換,結束此次遍歷。

    3. 從左邊開始,由於 34 都是前兩輪已經排好序的便不再動它,從 2 開始,向右遍歷,尋找比 5 大的值,然後與 5 進行調換(道理同步驟 1),此輪找到 9,將 95 調換位置,結束此次遍歷。

    4. 從右邊開始,從 1 開始,向右遍歷,尋找比5 小的值,然後與 5 進行調換(道理同步驟 2),此輪找到 1,將 15 調換,結束此次遍歷。

    5. 這個時候,5 的左右兩側符合左側值 <=5,右側值 >=5,所以結束此輪排序。

    6. 5 的左右兩側繼續抽出來各自進行下一輪的排序,規則同上,直到無法再拆分下去,即完成了整體的快速排序。

快速排序核心之一:

public static int divide(int[] arr, int start, int end) {
    int base = arr[end];
    while (start < end) {

        while (start < end && arr[start] <= base) {
            start++;
        }
        if (start < end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            end--;
        }

        while (start < end && arr[end] >= base) {
            end--;
        }
        if (start < end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
        }

    }
    int borderPostion = start == end ? start : -1;
    return borderPostion;
}

快速排序核心之二:

public static void sort(int[] arr, int start, int end) {
    if (start > end) {
        return;
    } else {
        int borderPostion = divide(arr, start, end);
        sort(arr, start, borderPostion - 1);
        sort(arr, borderPostion + 1, end);
    }
}

完整代碼如下:

package com.ggsddu.domain.sort;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = new int[]{3, 7, 2, 9, 1, 4, 6, 8, 10, 5};
        System.out.println("排序前:" + Arrays.toString(arr));
        sort(arr, 0, arr.length - 1);
        System.out.println("排序後:" + Arrays.toString(arr));
    }

    /**
     * 快速排序
     *
     * @param arr
     * @param start
     * @param end
     */
    public static void sort(int[] arr, int start, int end) {
        if (start > end) {
            return;
        } else {
            // 如果不止一個元素,繼續劃分兩邊遞歸排序下去  
            int borderPostion = divide(arr, start, end);
            System.out.print(borderPostion + " ");// 以某個基準值爲界每一次劃分元素排序後,此基準值所在位置
            System.out.println(Arrays.toString(arr));
            sort(arr, start, borderPostion - 1);// 基準值左側元素遞歸排序
            sort(arr, borderPostion + 1, end);// 基準值右側元素遞歸排序
        }
    }

    /**
     * 對數組某一段元素進行劃分
     * 以基準值爲界,左側數都大於基準值,右側數都大於基準值
     * 形象表示爲:[{A|num<=基準值} 基準值 {B|num>=基準值}]
     *
     * @param arr
     * @param start 開始遍歷的位置
     * @param end   基準值的初始位置
     * @return 每一次劃分元素,基準值所在位置
     */
    public static int divide(int[] arr, int start, int end) {
        int base = arr[end];// 默認以最右邊的元素作爲基準值
        // 一旦start等於end,就說明左右兩個指針合併到了同一位置,可以結束此輪循環
        while (start < end) {

            while (start < end && arr[start] <= base) {
                start++;// 從左邊開始向右遍歷,如果如果遍歷的值比基準值小,下標start就繼續向右走  
            }
            // 上面的while循環結束時,就說明當前的arr[start]的值比基準值大,應與基準值位置進行交換
            if (start < end) {
                //交換  
                int temp = arr[start];
                arr[start] = arr[end];
                arr[end] = temp;
                // 此值與基準值交換位置後,被“排序”,並且“佔領”了原end的位置,排序範圍縮小,end向前移動一位  
                end--;
            }

            while (start < end && arr[end] >= base) {
                end--;// 從右邊開始遍歷,如果遍歷的值比基準值大,下標end就繼續向左走
            }
            // 上面的while循環結束時,就說明當前的arr[end]的值比基準值小,應與基準值進行交換
            if (start < end) {
                // 交換  
                int temp = arr[start];
                arr[start] = arr[end];
                arr[end] = temp;
                // 此值與基準值交換後,被“排序”,並且“佔領”原start的位置,排序範圍縮小,所以start同時向後移動一位  
                start++;
            }

        }
        // 返回start或end皆可,因爲此時start和end都爲基準值所在的位置 
        int borderPostion = start == end ? start : -1;
        return borderPostion;
    }

}

控制檯:

排序前:[3, 7, 2, 9, 1, 4, 6, 8, 10, 5]
4 [3, 4, 2, 1, 5, 9, 6, 8, 10, 7]
0 [1, 4, 2, 3, 5, 9, 6, 8, 10, 7]
2 [1, 2, 3, 4, 5, 9, 6, 8, 10, 7]
1 [1, 2, 3, 4, 5, 9, 6, 8, 10, 7]
3 [1, 2, 3, 4, 5, 9, 6, 8, 10, 7]
6 [1, 2, 3, 4, 5, 6, 7, 8, 10, 9]
5 [1, 2, 3, 4, 5, 6, 7, 8, 10, 9]
8 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
7 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
9 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
排序後:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Process finished with exit code 0

算法優缺點
    快速排序最“快”的地方在於左右兩邊能夠快速同時遞歸排序下去,所以最優的情況是基準值剛好取在無序區的中間,這樣能夠最大效率地讓兩邊排序,同時最大地減少遞歸劃分的次數。此時的時間複雜度僅爲 O(NlogN)
    快速排序也有存在不足的情況,當每次劃分基準值時,得到的基準值總是當前無序區域裏最大或最小的那個元素,這種情況下基準值的一邊爲空,另一邊則依然存在着很多元素(僅僅比排序前少了一個),此時時間複雜度爲 O(N*N)


選擇排序

動圖:
這裏寫圖片描述


插入排序

動圖:
這裏寫圖片描述

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