1. 程式人生 > >JavaScript實現常見排序及搜尋演算法

JavaScript實現常見排序及搜尋演算法

前言

在本部分將主要學習常用的排序和搜尋演算法: 如:氣泡排序、選擇排序、插入排序、歸併排序、 快速排序和堆排序等以及:順序搜尋和二分搜尋演算法。

//1、在開始排序演算法之前,先建立一個數組(列表)來表示待排序和搜尋的資料結構。

function ArrayList() {
    var array = []; //1.1定義一個數組來存放資料
    this.insert = function (item) {  //1.2定義一個插入方法來向資料結構中新增元素
        array.push(item);
    };
    this.toString = function () { //使用js原生的join方法,來拼接陣列中的所有元素
        return array.join('-');
    };
 //未改進時的氣泡排序:
    /***
     * 氣泡排序比較任何兩個相鄰的項,如果第一個比第二個大,則交換它們。
     *  時間複雜度為 O(n^2)
     */
    this.bubbleSort = function () {
        var length = array.length;
        for (var i = 0; i < length; i++) { //控制迴圈次數
            for (var j = 0; j < length - 1; j++) { //不斷比較交換相鄰位置的資料
                if (array[j] > array[j + 1]) {
                    swap(array, j, j + 1);
                }
            }
        }
    };
    //改進後的氣泡排序
    /**
     *  如果從內迴圈減去外迴圈中已經跑過的次數,就可以避免內迴圈中所有不必要的比較
     * */
    this.modifiedBubbleSort = function () {
        var length = array.length;
        for (var i = 0; i < length; i++) {
            for (var j = 0; j < length - 1 - i; j++) {
                if (array[j] > array[i]) {
                    swap(j, j + 1);
                }
            }
        }
    };


    /**
     * 選擇排序:選擇排序演算法是一種原址比較排序演算法。選擇排序大致的思路是找到資料結構中最小值並將其放在第一位,
     * 接著找到第二小的值並將其放在第二位,以此類推。
     *  其時間複雜度也為O(n^2)
     *
     * */
    //選擇排序演算法的原始碼:
    this.selectionSort = function () {
        var length = array.length;
        var indexMin;
        for (var i = 0; i < length - 1; i++) {//外迴圈迭代陣列,並控制迭代輪次(陣列的第n個值====下一個最小值)
            indexMin = i; //假設本輪迭代的最小值
            for (var j = i; j < length; j++) {
                //從當前的i值開始至陣列結束,比較是否位置j的值比當前最小值小,如果是
                if (array[indexMin] > array[j]) {
                    indexMin = j;//則改變最小值至新最小值
                }
            }//當內迴圈結束,得出陣列第n小的值

            if (i !== indexMin) {//如果該最小值和原最小值不同,則交換其值
                [array[i], array[indexMin]] = [array[indexMin], array[i]];
            }
        }
    };

    /**
     * 插入排序:插入排序每次排一個整數項,以此構建最後的排序陣列。
     *
     * */
    this.insertionSort = function () {
        var length = array.length;
        var j, temp;
        for (var i = 1; i < length; i++) {//迭代陣列來給第i項找到正確的位置
            j = i;
            temp = array[i];//用i的值來初始化一個輔助變數並將其值亦儲存於--臨時變數
            while (j > 0 && array[j - 1] > temp) {//查詢正確的位置插入
                array[j] = array[j - 1];//如果陣列中前面的值比待比較的值大,把前面的值移到當前位置,J--
                j--;
            }
            array[j] = temp;  //直到前面的值部不大於temp時,把temp放入當前的位置中。然後迭代下一項繼續排序
        }
    };

    /**
     * 歸併排序:歸併排序是第一個可以被實際使用的演算法
     *前面幾個演算法的效能不好,歸併排序不錯,其複雜度為
     * 時間複雜度:O(nlog^n)
     * 歸併排序是一種分治演算法。其思想是將原始陣列切分成較小的陣列
     * 直到每個小陣列只有一個位置,接著小陣列歸併成較大的陣列,直到最後只有一個排序完畢的大陣列
     * */
     this.mergeSort = function(){
         array = mergeSortRec(array);
     };


     /**
      *  快速排序:最常用的演算法,它的複雜度為O(nlog^n)
      * (1)首先,從陣列中選擇中間一項作為主元。
      * (2)建立兩個指標,左邊一個指向陣列第一項,右邊一個指向陣列最後一項。
      * 移動左指標直到我們找到一個比主元大的元素,接著,移動右指標直到找到一個比主元小的元素。
      * 然後交換它們,重複這個過程,直到左指標超過了右指標。
      * 這個過程使得比主元小的值都排在主元之前,而比主元大的值都排在主元之後
      *(3)接著,演算法對劃分後的小陣列(較主元小的值組成的子陣列,以及較主元大的值組成的子陣列)
      * 重複之前的兩個步驟,直至陣列已經完全排序。
      **/
     this.quickSort = function(){
         quick(array, 0, array.length - 1);
     };

    //搜尋演算法
    /**
     *  順序搜尋:順序或線醒搜尋是最基本的搜尋演算法。它的機制是:
     *  將每一個數據結構中的元素和我們要找的元素做比較。順序搜尋是最低效的
     *  一種搜尋演算法。
     * */
    this.sequentialSearch = function(item){
        for(var i=0;i<array.length;i++){
            if(item === array[i]){
                return i;//true或者array[i]
            }
        }
        return -1;
    }

    /**
     *  二分搜尋:該演算法要求被搜尋的資料結構已經排序
     *  步驟:
     *  (1)選擇陣列的中間值
     *  (2)如果選中值是待搜尋值,那麼演算法執行完畢
     *  (3)如果待搜尋值比選中值要小,則返回步驟1並在選中值左邊的子陣列中尋找
     *  (4)如果待搜尋值比選中值要大,則返回步驟1並在選中值右邊的子陣列中尋找
     */

    this.binarySeach = function(item){
        this.quickSort();  //在查詢之前對陣列進行排序
        var low = 0;
        var high = array.length -1;
        var mid,element;
        while(low<= high){
            mid = Math.floor((low + high) /2);
            element = array[mid];
            if(element <item)
            {
                low = mid +1;
            } else if(element >item){
                high = mid -1;
            }else{
                return mid;  //返回的是索引
            }
        }
        return -1;
    };

}

   //快速排序劃分過程:
    var partition = function(array,left,right){
       var pivot = array[Math.floor((right + left)/2)];//選擇中間項作為主元
        var i= left; //初始化為陣列第一個元素
        var j = right; //初始化為陣列最後一個元素
        while(i<= j){
            while(array[i] < pivot){ //移動指標直到找到一個元素比主元大
                i++;
            }
            while(array[j] > pivot){ //對於right指標做同樣的事,移動找到比主元小的
                j--;
            }
            if(i<= j){ //當左指標指向的元素比主元大且右指標指向的元素比主元小,且此時左指標索引沒有右指標索引大,則交換它們
                swap(array,i,j);
                i++;
                j--;
            }
        }
        return i;
    };

      // 宣告方法來呼叫遞迴函式,傳遞待排序陣列,以及索引0及其最末的位置作為引數。
    var quick = function(array,left,right){
        var index;  //定義該變數,幫助我們將子陣列分離為較小的陣列和較大的陣列
        if(array.length >1){
            index = partition(array,left,right);
            if(left < index -1){
                quick(array,left,index-1);
            }
            if(index < right){
                quick(array,index,right);
            }
        }
    };



    //以下為歸併排序
   // 實際被執行的輔助函式
    var  mergeSortRec = function(array){
     var length = array.length;
     if(length ===1){   //結束條件
         return array;
     }
     var  mid = Math.floor(length/2);//獲得陣列的中間位
     var left = array.slice(0,mid); //陣列將其分成兩個小陣列
     var right = array.slice(mid,length);
     return merge(mergeSortRec(left),mergeSortRec(right));
    };
    //定義merge函式,負責合併和排序小陣列來產生大陣列
    //直到回到原始陣列並已經排序完成。
    var merge = function(left,right){
        var result = [];
        var iL = 0;
        var iR = 0;
        while(iL < left.length && ir < right.length) { // {8}
            if (left[iL] < right[iR]) {
                result.push(left[iL++]); // {9}
            } else {
                result.push(right[iR++]);
            }
        }
        while(iL <left.length){
            result.push(left[iL++]);
        }
        while(iR <right.length){
            result.push(right[iR++]);
        }
        return result;
    };



    //交換方法
    var swap= function(array,index1,index2){
       /* var aux = array[index1];
         array[index1]= array[index2];
         array[index2]=aux;*/
    //可以利用es6中的方式來交換
       [array[index1],array[index2]]=[array[index2],array[index1]];
   };


測試:


//測試氣泡排序演算法
//定義一個函式自動地建立一個未排序的陣列,陣列的長度由函式的引數指定
function createNonSortedArray(size){
    var array = new ArrayList();
    for(var i = size;i>0;i--){
       array.insert(i);
    }
    return array;
}
//測試冒泡結果
var array = createNonSortedArray(5);
console.log("氣泡排序前:",array.toString());
array.bubbleSort();
console.log("氣泡排序後:",array.toString());

//測試選擇排序演算法
var selectionArry = createNonSortedArray(5);
console.log("選擇排序前:",selectionArry.toString());
selectionArry.selectionSort();
console.log("選擇排序後:",selectionArry.toString());


//測試插入排序演算法
var  insertArry = createNonSortedArray(5);
console.log("插入排序前:",insertArry.toString());
insertArry.selectionSort();
console.log("插入排序後:",insertArry.toString());

console.log("二分查詢後的資料",insertArry.binarySeach(3));

執行結果