JS排序算法 ᕦ(・ㅂ・)ᕤ走過路過不要錯過

在這裏插入圖片描述
在這裏插入圖片描述

冒泡排序(Bubble Sort)

通過相鄰元素的比較和交換,使得每一趟循環都能找到未排數組的最大值或最小值,把它放到後面;
在這裏插入圖片描述
普通冒泡排序

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    for(let i=0,len=arr.length; i<len-1;i++){
        // flag用來標誌每一輪是否有交換數據;如果沒有,則說明數組已有序
        let flag = true
        for(let j=0;j<len-i-1;j++){
            if(arr[j]>arr[j+1]){
                [arr[j],arr[j+1]] = [arr[j+1],arr[j]];
                flag = false;
            }
        }
        if(flag) return;
    }
}

sort(arr)
console.log(arr) // [ 2, 3, 8, 11, 13, 35, 44 ]

雙向冒泡排序

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    let low = 0;
    let high = arr.length -1;
    while(low < high){
        let flag = true;
        // 找到最大值放在右邊
        for(let i=low;i<high;i++){
            if(arr[i]>arr[i+1]){
                [arr[i],arr[i+1]] = [arr[i+1],arr[i]];
                flag = false;
            }
        }
        high--;
        // 找到最小值放在左邊
        for(let j=high;j>low;j--){
            if(arr[j]<arr[j-1]){
                [arr[j],arr[j-1]] = [arr[j-1],arr[j]];
                flag = false;
            }
        }
        low++;
        if(flag)return;
    }    
}

sort(arr)
console.log(arr) // [ 2, 3, 8, 11, 13, 35, 44 ]

快速排序

  1. 從數組中取出一個數作爲基準。
  2. 在原數組中進行移動,將大於基準的數放到基準右邊,小於基準的數放到基準左邊,在基準左右形成兩個子數組。
  3. 在左右子數組中反覆執行步驟1、2,直到所有子數組只剩下一個數。
let arr = [2,44,3,13,11,35,8]

function sort(arr){
    if(arr.length <=1) {
        return arr;
    }
    // 獲取基準的索引
    let pivotIndex = Math.floor(arr.length/2);
    // 找到基準,把基準從原數組中刪除
    let pivot = arr.splice(pivotIndex,1)[0];
    // 定義左右數組
    let left = [];
    let right = [];
    // 比基準小的放在left,比基準大的放在right
    arr.forEach(item => {
        if(item < pivot){
            left.push(item);
        }else {
            right.push(item);
        }
    });
    return sort(left).concat([pivot],sort(right));
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

直接插入排序

基本思想:
在已排序的數據序列從後向前掃描,找到對應的位置插入未排序的數據。
插入排序通常採用佔位的形式,空間複雜度爲O(1)。
因此,從後向前掃描的過程中,需要反覆的把已排序的元素逐步向後挪位,爲新插入元素提供插入的位置。

  1. 從第一個元素開始,該元素可以認爲已經被排序
  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描
  3. 如果該元素(已排序)大於新元素,將該元素移到下一位置
  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
  5. 將新元素插入到下一位置中
  6. 重複步驟2

在這裏插入圖片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    for(let i=1,len=arr.length;i<len;i++){
        let temp = arr[i]; // 需要排序的元素
        let j=i-1;         // 默認已排序元素的最後一位的索引
        while(j>=0 && arr[j]>temp){ // 在已排序好的隊列中從後向前掃描
            arr[j+1]=arr[j];  // 已排序的元素大於新元素,將該元素移到下一個位置
            j--;
        }
        arr[j+1]=temp; // 當已排序的元素<=新元素或到頭了,即可將新元素加到該元素後面或首位
    }
    return arr
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

希爾排序

希爾排序也是一種插入排序,它是簡單插入排序經過改進之後的一個更高的版本,也稱爲縮小增量排序,同時該算法是衝破O(n^2)的第一批算法之一。它與插入排序的不同之處在於,它會優先比較距離較遠的元素。

基本思路:
根據增量來分割數組,對每一組進行直接插入排序
在這裏插入圖片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
    let len = arr.length;
    // gap 即爲增量
    for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
      for (let i = gap; i < len; i++) {
        let j = i;
        let current = arr[i];
        while(j - gap >= 0 && current < arr[j - gap]) {
          arr[j] = arr[j - gap];
          j = j - gap;
        }
        arr[j] = current;
      }
    }
    return arr;
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

簡單選擇排序

選擇排序是一種簡單直觀的排序算法
基本思路:
首先在未排序序列中找到最小/最大 元素,存放在排序序列的起始位置;然後,再從剩餘未排序元素中繼續尋找最小/最大元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
在這裏插入圖片描述

let arr = [2,44,3,13,11,35,8]

function sort(arr){
   let len = arr.length;
   let minIndex,temp;
   for(let i=0;i<len-1;i++){
       minIndex=i;
       for(let j=i+1;j<len;j++){ // 循環尋找最小的數
           if(arr[j]<arr[minIndex]){
               minIndex=j;  // 保存最小數的索引
           }
       }
       [arr[i],arr[minIndex]] = [arr[minIndex],arr[i]];
   }
   return arr;
}

console.log(sort(arr)) // [ 2, 3, 8, 11, 13, 35, 44 ]

堆排序

參考網站
堆排序可以說是一種利用堆的概念來排序的選擇排序,分爲兩種方法:

  1. 大頂堆:每個節點的值都大於或等於其子節點的值,在堆排序算法中用於升序排列
  2. 小頂堆:每個節點的值都小於或等於其子節點的值,在堆排序算法中用於降序排列
    在這裏插入圖片描述
    在這裏插入圖片描述

我們用簡單的公式來描述一下堆的定義就是:
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

// 交換兩個節點
function swap(A, i, j) {
    let temp = A[i];
    A[i] = A[j];
    A[j] = temp; 
  }
  
  // 將 i 結點以下的堆整理爲大頂堆,注意這一步實現的基礎實際上是:
  // 假設 結點 i 以下的子堆已經是一個大頂堆,shiftDown函數實現的
  // 功能是實際上是:找到 結點 i 在包括結點 i 的堆中的正確位置。後面
  // 將寫一個 for 循環,從第一個非葉子結點開始,對每一個非葉子結點
  // 都執行 shiftDown操作,所以就滿足了結點 i 以下的子堆已經是一大
  //頂堆
  function shiftDown(A, i, length) {
    let temp = A[i]; // 當前父節點
  // j<length 的目的是對結點 i 以下的結點全部做順序調整
    for(let j = 2*i+1; j<length; j = 2*j+1) {
      temp = A[i];  // 將 A[i] 取出,整個過程相當於找到 A[i] 應處於的位置
      if(j+1 < length && A[j] < A[j+1]) { 
        j++;   // 找到兩個孩子中較大的一個,再與父節點比較
      }
      if(temp < A[j]) {
        swap(A, i, j) // 如果父節點小於子節點:交換;否則跳出
        i = j;  // 交換後,temp 的下標變爲 j
      } else {
        break;
      }
    }
  }
  
  // 堆排序
  function heapSort(A) {
    // 初始化大頂堆,從第一個非葉子結點開始
    for(let i = Math.floor(A.length/2-1); i>=0; i--) {
      shiftDown(A, i, A.length);
    }
    // 排序,每一次for循環找出一個當前最大值,數組長度減一
    for(let i = Math.floor(A.length-1); i>0; i--) {
      swap(A, 0, i); // 根節點與最後一個節點交換
      shiftDown(A, 0, i); // 從根節點開始調整,並且最後一個結點已經爲當
                           // 前最大值,不需要再參與比較,所以第三個參數
                           // 爲 i,即比較到最後一個結點前一個即可
    }
  }
  
  let arr = [50,45,40,20,25,35,30,10,15]
  heapSort(arr);
  console.log(arr) // [ 10, 15, 20, 25, 30, 35, 40, 45, 50 ]
 

二路歸併排序

參考鏈接
歸併排序採用的是分治的思想,首先是“分”,將一個數組反覆二分爲兩個小數組,直到每個數組只有一個元素;其次是“治”,從最小數組開始,兩兩按大小順序合併,直到併爲原始數組大小,下面是圖解:
在這裏插入圖片描述

// 融合兩個有序數組,這裏實際上是將數組 arr 分爲兩個數組
function mergeArray(arr, first, mid, last, temp) {
    let i = first; 
    let m = mid;
    let j = mid+1;
    let n = last;
    let k = 0;
    while(i<=m && j<=n) {
        if(arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while(i<=m) {
        temp[k++] = arr[i++];
    }
    while(j<=n) {
        temp[k++] = arr[j++];
    } 
    for(let l=0; l<k; l++) {
        arr[first+l] = temp[l];
    }
    return arr;
}
// 遞歸實現歸併排序
function mergeSort(arr, first, last, temp) {
    if(first<last) {
        let mid = Math.floor((first+last)/2);
        mergeSort(arr, first, mid, temp);    // 左子數組有序
        mergeSort(arr, mid+1, last, temp);   // 右子數組有序
        arr = mergeArray(arr, first, mid, last, temp);  
    }
    return arr;
}
  
// example
let arr = [2,44,3,13,11,35,8]
let temp = new Array();
console.log(mergeSort(arr, 0 ,arr.length-1, temp)); // [ 2, 3, 8, 11, 13, 35, 44 ]
  

在這裏插入圖片描述
持續更新中。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章