排序和查詢

CBD發表於2019-11-25

排序和查詢 {ignore}

排序演算法

排序演算法沒有優劣之分,在不同的場景中,不同的排序演算法執行效率不同。

  1. 選擇排序 Selection Sort

一次選擇排序,可以將某個區間的最小值排列到該區域的第一位,具體的方式是:

  1. 找出該區域的最小值
  2. 將該值與該區域第一個值交換
  3. 對下一個區域重複上述過程,直到排序完成
  1. 氣泡排序 Bubble Sort

一次氣泡排序,可以將某個區域序列的最大值排序到該區域的最後一位,具體的方式是:

  1. 將第1位和第2位比較,如果前者比後者大則交換
  2. 將第2位和第3位比較,如果前者比後者大則交換
  3. 依次類推,直到比較到該區域的最後兩位
  4. 重複上述過程,直到序列排序完成
  1. 插入排序 Insertion Sort

將序列分為兩個部分,一部分是有序的,一部分是無序的,現在要做的是,就是不斷的從無序的部分取出資料,加入到有序的部分,直到整個排序完成

例如:序列[5, 7, 2, 3, 6]

  1. 分為有序的序列和無序的序列 (5) (7 2 3 6)
  2. 不斷的擴充有序序列 (5 7) (2 3 6)
  3. 不斷的擴充有序序列 (2 5 7) (3 6)
  4. 不斷的擴充有序序列 (2 3 5 7) (6)
  5. 不斷的擴充有序序列 (2 3 5 6 7)
  6. 排序完成
  1. 快速排序 Quick Sort

選擇一個數(比如序列的最後一位)作為基準數,將整個序列排序成兩部分,一部分比該數小,另一部分比該數大,基準數在中間,然後對剩餘的序列做同樣的事情,直到排序完成

例如:序列[5, 7, 2, 3, 6, 4]

  1. 選擇4作為基準數,排序成為:(3, 2) 4 (7, 6, 5)
  2. 對於3,2, 繼續使用該方式排序,得到: (2, 3) 4 (7,6,5)
  3. 對於7,6,5,繼續使用該方式排序,得到: (2, 3) 4 (5,6,7)
  4. 排序完成

查詢演算法

  1. 順序查詢 Inorder Search

即普通的遍歷,屬於演算法的窮舉法,沒啥好解釋的

  1. 二分查詢 Binary Search

如果一個序列是一個排序好的序列,則使用二分查詢可以極大的縮短查詢時間

具體的做法是:

查詢該序列中間未知的資料

  1. 相等,找到
  2. 要找的資料較大,則對後續部分的資料做同樣的步驟
  3. 要找的資料較小,則對前面部分的資料做同樣的步驟
  1. 插值查詢 Interpolation Search

插值查詢是對二分查詢的進一步改進

如果序列不僅是一個排序好的序列,而且序列的步長大致相同,使用插值查詢會更快的找到目標。

插值查詢基於如下假設:下標之間的距離比和資料之間的距離比大致相同,即:

(目標下標-最小下標) / (最大下標 - 最小下標) ≈ (目標值 - 最小值) / (最大值 - 最小值)

因此可以算出大致的下標落點:

目標下標 ≈ (目標值 - 最小值) / (最大值 - 最小值) * (最大下標 - 最小下標) + 最小下標

這樣就可以計算出大致的下標落點,後續的比較和二分查詢一樣。

/**
 * 交換陣列中指定的位置
 * @param {*} arr 
 * @param {*} i1 
 * @param {*} i2 
 */
function swap(arr, i1, i2) {
    var temp = arr[i1]
    arr[i1] = arr[i2];
    arr[i2] = temp;
}

/**
 * 選擇排序
 * @param {*} arr 
 */
function selectionSort(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        //搞定 i ~ arr.length-1 區間
        //從該區間中找出最小值,和 第 i 位交換
        var min = arr[i]; //定義一個變數,為該區間的第一個數
        var index = i; //最小值所在的位置
        for (var j = i + 1; j < arr.length; j++) {
            if (arr[j] < min) {
                min = arr[j];
                index = j; //重新記錄最小值的位置
            }
        }
        //最小值已經找出
        //交換第i位和第index位
        swap(arr, i, index);
    }
}

/**
 * 氣泡排序
 * @param {*} arr 
 */
function bubbleSort(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
        //需要經過arr.length-1次的冒泡
        //i:0   迴圈:0~arr.length-1-i
        //i:1   迴圈:0~arr.length-1-i
        //i:2   迴圈: 0~arr.length-1-i
        for (var j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr, j, j + 1);
            }
        }
    }
}

/**
 * 插入排序
 * @param {*} arr 
 */
function insertionSort(arr) {
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] < arr[i - 1]) {
            //將第i位的值加入到前面有序佇列的正確位置
            var temp = arr[i];
            for (var j = i; j >= 0; j--) {
                if (j > 0 && arr[j - 1] > temp) {
                    arr[j] = arr[j - 1];
                }
                else {
                    arr[j] = temp;
                    break;
                }
            }
        }
    }
}

/**
 * 快速排序
 * @param {*} arr 
 */
function quickSort(arr) {
    /**
     * 對陣列的某個區域進行一次快速排序
     * @param {*} arr 
     * @param {*} start 區域的起始下標
     * @param {*} end 區域的結束下標
     */
    function _quickSort(arr, start, end) {
        if (start >= end || start > arr.length - 1) return;
        var low = start, high = end;
        var key = arr[end]; //基準值
        while (low < high) {
            while (low < high && arr[low] <= key) low++;
            arr[high] = arr[low];
            while (low < high && arr[high] >= key) high--;
            arr[low] = arr[high];
        }
        //low === high
        arr[low] = key;
        _quickSort(arr, start, low - 1);
        _quickSort(arr, low + 1, end);
    }
    _quickSort(arr, 0, arr.length - 1);
}

var arr = [5, 3, 1, 6, 7, 4];
console.log(arr)
quickSort(arr)
console.log(arr);
複製程式碼

相關文章