JavaScript實現常見排序及搜尋演算法
阿新 • • 發佈:2019-02-17
前言
在本部分將主要學習常用的排序和搜尋演算法: 如:氣泡排序、選擇排序、插入排序、歸併排序、 快速排序和堆排序等以及:順序搜尋和二分搜尋演算法。
//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));