常見數據結構與算法-JavaScript版(排序篇)

1.排序的定義

排序就是使一串記錄,按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操做。
javascript

2.冒泡排序

冒泡排序的思路是:兩兩比較相鄰記錄的關鍵字,若是反序則交換,直到沒有反序位置。java

內層循環結束一次能夠獲得一個最大值或最小值。時間複雜度爲O(n²)。shell

function bubbleOrder(arr) {
        var temp = 0;
        for (var i = 0; i < arr.length; i++) {
            for (var j = i + 1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }複製代碼

3.簡單選擇排序

簡單選擇排序是將下標i做爲假定的最小值,再將i+1至n的記錄的關鍵字與i的關鍵字比較,選出其中的最小值,並和i的值交換。數組

簡單選擇排序與冒泡排序時間複雜度相同,但元素移動次數少。ui

function selectOrder(arr) {
        var min = 0;
        var temp = 0;
        for (var i = 0; i < arr.length; i++) {
            // 假定當前最小值的下標爲i
            min = i;
            for (var j = i + 1; j < arr.length; j++) {
                // 與假定的最小值比較,小的話則改變min的值
                if (arr[j] < arr[min]) {
                    min = j;
                }
            }
            // 若是min的值與當初假定值不等,則將最小值與當前值交換
            if (min != i) {
                temp = arr[i];
                arr[i] = arr[min];
                arr[min] = temp;
            }
        }
        return arr;
    }複製代碼

4.直接插入排序

直接插入排序是將一個記錄插入到已經排好序的有序表中,從而獲得一個新的,記錄數加1的新的有序表。spa

直接插入排序與冒泡排序時間複雜度相同,但元素移動次數少。
code

function insertOrder(arr) {
        // temp 用於存儲當前值
        var temp = 0;
        for (var i = 1; i < arr.length; i++) {
            temp = arr[i];
            j = i - 1;
            // arr[j]至關於哨兵,表示已經排序過的序列的最大值
            while (temp < arr[j] && j >= 0) {
                // 哨兵位以後的全部已排序序列(j到i-1)向後移動一位,即arr[0]變爲arr[1],arr[1]變爲arr[2]
                arr[j + 1] = arr[j];
                j--;
            }
            //把j-1減掉的1加回來,(試想若是不知足while條件,即arr[i]>arr[i-1],則不須要移動,而上邊j=i-1,須要再加回來)
            arr[j + 1] = temp;
        }
        return arr;
    }複製代碼

5.快速排序

快速排序的思路是:經過一趟排序將待排記錄分割成獨立兩部分,其中一部分記錄的關鍵字均比另外一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。排序

選取一個值做爲基準值,比基準值小的放左邊,比基準值大的放右邊,遞歸,直到左右兩邊都排好序,時間複雜度爲O(nlogn).
遞歸

function quickOrder(arr) {
        if (arr.length > 1) {
            var mid = parseInt(arr.length / 2);
            //不能夠直接讓arr[i]與arr[mid]比較,當arr[mid]恰好爲最小值時,沒法劃分爲左右兩部分,只有右邊部分,會出現死循環
            var pivot = arr.splice(mid, 1);
            var left = [];
            var right = [];
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] < pivot) {
                    left.push(arr[i]);
                } else {
                    right.push(arr[i]);
                }
            }
            return quickOrder(left).concat(pivot, quickOrder(right));
        } else {
            return arr;
        }
    }複製代碼

6.希爾排序

希爾排序的原理與直接插入排序相同,步長從1變爲increase。ip

希爾排序的時間複雜度不穩定,最差的狀況是O(n²)。

function shellOrder(arr) {
        var increase = arr.length;
        var temp;
        while (increase > 0) {
            increase = parseInt(increase / 2);
            for (var i = increase; i < arr.length; i++) {
                temp = arr[i];
                var j = i - increase;
                while (temp < arr[j] && j >= 0) {
                    arr[j + increase] = arr[j];
                    j = j - increase;
                }
                arr[j + increase] = temp;
            }
        }
        return arr;
    }複製代碼

7.堆排序

堆排序利用的是二叉樹中的大頂堆結構,即根節點大於左右子樹 。

二叉樹中存在 根節點i的兩個子節點分別是2i和2i+1 ,利用數組表示二叉樹時,二叉樹從1開始,數組從0開始 ,故數組中存在根節點i的兩個子節點分別是2i+1 和2i+1+1

function heapOrder(arr) {
        var j = arr.length;
        var tempArr = [];
        while (j > 0) {
            // 返回arr的第一項爲數組的最大值
            // 數組長度或者說二叉樹長度爲arr.length,則最大葉子節點爲aarr.length-1項,則此葉子節點的根節點爲(arr.length-1)/2
            // 故i從(arr.length-1)/2開始,一直向下遍歷到0,即根節點結束
            for (var i = parseInt(arr.length / 2 - 1); i >= 0; i--) {
                maxNode(arr, i)
            }
            // 第一項爲最大值,彈出後,將數組剩餘項再排列爲大頂堆
            tempArr.push(arr.shift());
            j--;
        }
        return tempArr;
        function maxNode(arr, i) {
            var temp = arr[i];
            for (var j = i * 2 + 1; j < arr.length; j = j * 2 + 1) {
                // 找到根的左右節點中較大的一個再與根節點比較
                if (j + 1 < arr.length && arr[j] < arr[j + 1]) {
                    // 若是右節點大,則j+1,此時arr[j]爲右節點
                    j++;
                }
                // arr[j]爲左右節點中較大的一個
                if (temp < arr[j]) {
                    // 若是根節點小於左右節點的較大值,則交換位置
                    arr[i] = arr[j];
                    arr[j] = temp;
                    i = j;
                } else {
                    break;
                }

            }
        }
    }複製代碼

8.歸併排序

歸併排序是將數組先劃分紅2個2個一組的子數組,進行排序,再合併已經有序的子數組,平均時間複雜度爲O(nlogn)

function mergeSort(arr) {
        // 若是數組長度大於1,再進行分組,不然直接返回
        if (arr.length > 1) {
            //將數組從中間部分分爲兩部分
            var len = parseInt(arr.length / 2);
            // 數組的前半部分
            var arr1 = arr.slice(0, len);
            // 數組的後半部分
            var arr2 = arr.slice(len);
            // 最開始寫的是:mergeSort(arr1) 沒有前邊的賦值操做,則雖然分組進行了排序,但並無改變arr
            // 將arr1的子數組的排序結果賦值給arr1
            arr1 = mergeSort(arr1);
            arr2 = mergeSort(arr2);
            // 返回數組的兩個子數組的排序結果
            return merge(arr1, arr2);
        } else {
            return arr;
        }
   // 對兩個參數數組進行shift()操做,可以使得代碼更簡潔
    function merge(arr1, arr2) {
        var temp = [];
        while (arr1.length && arr2.length) {
            if (arr1[0] < arr2[0]) {
                temp.push(arr1.shift());
            } else {
                temp.push(arr2.push());
            }
        }
        temp.concat(arr1, arr2);
    }
複製代碼
相關文章
相關標籤/搜索