數據結構與算法---常見排序

以前看了一點關於數據結構和算法的文章,這是個充滿魅力的領域,想簡單總結分享一下java

冒泡排序

從小到大:算法

  1. 初始化兩個指針,分別指向第一個元素(索引0)和第二個元素(索引1);
  2. 比較相鄰兩個元素的大小,若右側的元素(index+1)小於左側(index),則交換位置,不然不變;
  3. 指針同時右移一個單位;
  4. 重複第二步,直到到達數組末尾;
  5. 末尾索引減一(已經是最大),重複1,2,3,直到無須交換;

冒泡的特色:每一次輪迴後(步驟4),末排序的值都會「冒」到正確的位置,而後繼續輪迴,繼續冒泡;數組

冒泡實戰:數據結構

public static int[] bubbleSort(int[] arr) {
    // 初始化第一次最終冒泡的位置,及最後一個索引
    int lastSortedIndex = arr.length-1;
    // 初始化設定爲排序
    boolean sorted = false;

    while (!sorted) {
        sorted = true;
        for (int i = 0; i < lastSortedIndex; i++) {
            if (arr[i] > arr[i + 1]) {
                sorted = false;
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
        lastSortedIndex--;
    }
    return arr;
}

public static void main(String[] args) {
    int[] arr = {2, 3, 1, 5, 4};
    System.out.println(Arrays.toString(bubbleSort(arr))); // 1,2,3,4,5
}
複製代碼

冒泡的效率:數據結構和算法

比較:(N-1)+(N-2)+(N-3)+...+1ui

交換:最壞:(N-1)+(N-2)+(N-3)+...+1,最好:0spa

時間複雜度:O(N²)指針

選擇排序

從小到大:code

  1. 從左日後,記錄最小值的索引,最小值初始爲索引0的值,若是當前索引的值小於最小值,則更新最小的索引爲當前索引,直到數組的最後一位;
  2. 將最小值與本次檢查的起點交換;
  3. 初始最小值的索引進一位,重複前兩步,直到排序完成(起點索引爲最大索引)

選擇特色:選擇最小值索引。比較時,只記錄索引值,不作置位操做,直到本次數組比較完畢,再進行至多一次交換,最小值將依次向後排列排序

選擇實戰:

public static int[] selectionSort(int[] arr) {
    // 從小到大排序,依次最小的值
    for (int i = 0; i < arr.length; i++) {
        // 初始化最小值的索引
        int minIdenx = i;
        for (int j = i+1; j < arr.length; j++) {
            // 當前索引值小於當前最小值,更新索引
            if (arr[j] < arr[minIdenx]) {
                minIdenx = j;
            }
        }
        // 當前最小值的索引不在起點
        if (minIdenx != i) {
            int temp = arr[i];
            arr[i] = arr[minIdenx];
            arr[minIdenx] = temp;
        }
    }

    return arr;
}

public static void main(String[] args) {
    int[] arr = {2, 3, 1, 5, 4};
    System.out.println(Arrays.toString(selectionSort(arr)));
}
複製代碼

選擇效率:

比較:(N-1)+(N-2)+(N-3)+...+1

交換:最多:N-1,最少:0(每輪1或0次)

時間複雜度:O(N²/2) ——>忽略常數——>O(N²)

選擇排序的步數大概只有冒泡的一半

插入排序

從小到大:

  1. 移出:第一輪,先將第二個元素(索引1)的值移出,保存到臨時變量,並記錄當前空隙位置的指針,且當前數組中第二個元素處於空隙狀態;
  2. 比較並平移:臨時變量與空隙左側的值挨個比較,若空隙左側的值大於臨時變量,則該值向右移動一位,空隙自動左移(指針減一),若當前值比臨時變量小,或空隙已到達最左則,平移結束;
  3. 插入:將臨時變量填入空隙;
  4. 重複前三步,直到數組有序

插入特色:每輪四個步驟,移出,比較,平移,插入

插入實戰:

public static int[] insertionSort(int[] arr) {
    // 從第二個元素開始移出
    for (int i = 1; i < arr.length; i++) {
        // 初始空隙所在位置的指針
        int position = i;
        // 當前移出值保存到臨時變量
        int tempValue = arr[i];

        // 直到空隙移到最左側,或當前值小於臨時變量,則將臨時變量插入空隙
        while (position > 0 && arr[position - 1] > tempValue) {
            // 不符合條件,將左側值右移一位,即:將左側的值賦給右側,指針減一
            arr[position] = arr[--position];
        }
        // 臨時變量插入當前空隙的指針
        arr[position] = tempValue;
    }
    return arr;
}


public static void main(String[] args) {
    int[] arr = {2, 3, 1, 5, 4};
    System.out.println(Arrays.toString(insertionSort(arr)));
}
複製代碼

插入效率:

移出:N-1

比較:最多:1+2+3+...+N-1=N²/2,最少:N-1

平移:最多:N²/2,最少:0(有序)

插入:N-1

時間複雜度:O(N²+2N-2)——>簡化——>O(N²),最壞、平均、最好狀況:N二、N2/二、N步

快速排序

分區:

  1. 從數組從隨機選取一個值,以其爲軸,將比它小的值放在左邊,比它大的之放到右邊
  2. 放置兩個指針,分別指向除軸元素的數組最左和最又的元素
  3. 左指針逐個索引向右移動,當遇到大於等於軸的值就停下來;
  4. 右指針逐個索引向左移動,當遇到小於等於軸的值就停下來;
  5. 將兩個指針所指的值交換位置;
  6. 重複3,4,5,直到兩個指針重合,或左指針移到右指針的右邊;
  7. 將軸與左指針所在的位置交換;
  8. 當分區完成時,在軸左側的那些值確定比軸要小,在軸右側的那些值確定比軸要大

從小到大:

  1. 將數組分區,使軸到正確的位置;
  2. 對軸左右的兩個子數組遞歸地重複一、2步,也就是說,兩個子數組都各自分區,並造成各自的軸以及由軸分隔的更小的子數組。而後也對這些子數組分區,依次類推;
  3. 當分出的子數組長度爲0或1時,即達到基準情形,無需進一步操做

快速特色:一次分區至少有N次比較,及數組的每一個值都要與軸作比較;每次分區,左右指針豆花從兩端開始靠近,直到相遇

實戰:

public class TestQuickSort {
    public static int partition(int[] arr, int leftPointer, int rightPointer) {
        // 老是取最右的值做爲軸
        int pivotPosition = rightPointer;
        int pivot = arr[pivotPosition];

        // 將右指針指向軸左邊的一格
        rightPointer -= 1;

        while (true) {
            // 左指針只要小於軸,右移,不能超過軸
            while (arr[leftPointer] < pivot && leftPointer < pivotPosition) {
                leftPointer += 1;
            }

            // 右指針只要小於軸,左移
            while (arr[rightPointer] > pivot && rightPointer > 0) {
                rightPointer -= 1;
            }

            if (leftPointer >= rightPointer) {
                break;
            } else {
                swap(arr, leftPointer, rightPointer);
            }
        }

        // 將左指針的值與軸交換
        swap(arr, leftPointer, pivotPosition);
        // 返回左指針
        return leftPointer;
    }

    public static void swap(int[] arr, int pointer1, int pointer2) {
        int tempValue = arr[pointer1];
        arr[pointer1] = arr[pointer2];
        arr[pointer2] = tempValue;
    }

    public static int[] quickSort(int[] arr, int leftPointer, int rightPointer) {
        // 基準情形:分出的子數組長度爲0或1
        if (rightPointer - leftPointer <= 0) {
            return arr;
        }

        // 將數組分紅兩部分,並返回分隔所用的軸的索引
        int pivotPosition = partition(arr, leftPointer, rightPointer);

        // 對軸左側的部分遞歸調用quicksort
        quickSort(arr, leftPointer, pivotPosition - 1);

        // 對軸右側的部分遞歸調用quicksort
        quickSort(arr, pivotPosition + 1, rightPointer);

        return arr;

    }

    public static void main(String[] args) {
        // int[] arr = {0, 5, 2, 1, 6, 4};
        int[] arr = {5, 6, 0, 4, 3, 2, 1, 4};
        int[] sort = quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(sort));
    }
}
複製代碼

快速效率:

比較:每一個值都要與軸比較

交換:在適當時候將左右指針所指的兩個值交換位置

時間複雜度:一次分區,最少交換1次,最多N/2次,分區——>O(N);總共——>O(NlogN) 最好:O(NlogN),平均O(NlogN),最壞O(N²)(每次分區都是軸落在數組的開頭或結尾,如已升序或降序)


參考書籍:數據結構與算法圖解-【美】傑伊·溫格羅

相關文章
相關標籤/搜索