js數值排序中冒泡演算法的4種簡單實現

changli發表於2019-02-16

實現陣列排序的演算法很多,其中冒泡演算法是比較簡單的
冒泡的基本原理是相鄰的兩個數進行比較,按照排序的條件進行互換,例如對數值從小到大排序,
隨著不斷的互換,最大的那個值會慢慢冒泡到陣列的末端
基於這個原理我們就可以寫氣泡排序了

為了簡單起見下面的例子都是對數值陣列進行從小到大排序,先模擬一個20個字元的陣列

function getRandomArr(n) {
  let arr = [];
  for (let i = 0; i < n; i++) {
    arr.push(~~(Math.random() * 100));
  }
  return arr
}
let randomArr = getRandomArr(20);

第一種冒泡演算法
從原理可知,冒泡演算法最少是需要2層迴圈的,當其中一個數值冒泡到末端時,這個數值下次就不需要參與迴圈了,這樣迴圈的範圍就會慢慢縮小,最後陣列完成排序

function bubbleSort(arr) {
  let len = arr.length;
  let temp;
  let i = len - 1;
  while(i > 0) {
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    i--;//不斷縮小範圍
  }
  return arr;
}
console.log(randomArr)//[ 93, 72, 29, 17, 82, 26, 56, 71, 35, 48, 37, 42, 3, 11, 33, 66, 81, 53, 59, 53 ]
console.log(`bubbleSort`, bubbleSort(randomArr.concat()));//bubbleSort [ 3, 11, 17, 26, 29, 33, 35, 37, 42, 48, 53, 53, 56, 59, 66, 71, 72, 81, 82, 93 ]

在冒泡的過程中,我們可以發現,如果陣列後面部分已經排好序了,也就是不用再交換雙方的位置時,只要記錄好最後一次交換的位置,就有很大的可能縮小下次迴圈的範圍,這樣就能提高冒泡的效能(這只是猜想)
第二種冒泡演算法

function bubbleSort2(arr) { 
  let len = arr.length;
  let i = len - 1;
  let temp;
  let pos;//用來記錄位置的
  while (i > 0) {
    pos = 0;//初始為0如果陣列一開始已經排好序了,那麼就可以很快終止冒泡
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[j + 1]) {
        pos = j;
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    i = pos;
  }
  return arr;
}
console.log(randomArr)//[47, 31, 85, 65, 44, 56, 54, 5, 67, 44, 76, 13, 90, 12, 83, 72, 2, 69, 58, 60]
console.log(`bubbleSort2`, bubbleSort2(randomArr.concat()));//bubbleSort2 [2, 5, 12, 13, 31, 44, 44, 47, 54, 56, 58, 60, 65, 67, 69, 72, 76, 83, 85, 90]

其實對於第一種迴圈,是從左到右進行冒泡,我們也可以從右到左冒泡,但是從右到左的方法和第一種基本就一樣了,但是我們可以在內層迴圈中實現先向左冒泡,再向右冒泡
第三種冒泡方法

function bubbleSort3(arr) {
  let len = arr.length;
  let low = 0;
  let higth = len - 1;
  let temp;
  while (low < higth) {
    for (let j = low; j < higth; j++) {
      if (arr[j] > arr[j + 1]) {
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    higth--;
    for (let j = higth; j > low; j--) {
      if (arr[j] < arr[j - 1]) {
        temp = arr[j];
        arr[j] = arr[j - 1];
        arr[j - 1] = temp;
      }
    }
    low++;
  }
  return arr;
}
console.log(randomArr)//[40, 78, 16, 97, 38, 27, 66, 44, 45, 31, 12, 1, 99, 68, 36, 42, 40, 54, 6, 42]
console.log(`bubbleSort3`, bubbleSort3(randomArr.concat()));//bubbleSort3 [1, 6, 12, 16, 27, 31, 36, 38, 40, 40, 42, 42, 44, 45, 54, 66, 68, 78, 97, 99]

最後可以結合第三種和第二種方法
第四種冒泡的方法

function bubbleSort4(arr) { 
  let len = arr.length;
  let low = 0;
  let higth = len - 1;
  let temp;
  while (low < higth) {
    let hPos = 0;
    let lPos = higth;
    for (let j = low; j < higth; j++) {
      if (arr[j] > arr[j + 1]) {
        hpos = j;
        temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    heigth = hPos;
    for (let j = higth; j > low; j--) {
      if (arr[j] < arr[j - 1]) {
        lPos = j;
        temp = arr[j];
        arr[j] = arr[j - 1];
        arr[j - 1] = temp;
      }
    }
    low = lPos;
  }
  return arr;
}
console.log(randomArr)//[40, 78, 16, 97, 38, 27, 66, 44, 45, 31, 12, 1, 99, 68, 36, 42, 40, 54, 6, 42]
console.log(`bubbleSort4`, bubbleSort4(randomArr.concat()));//[1, 6, 12, 16, 27, 31, 36, 38, 40, 40, 42, 42, 44, 45, 54, 66, 68, 78, 97, 99]

下面對這4種方法在chrome控制檯下進行一個簡單的效能測試

var randomArr = getRandomArr(10000);
console.time(`1`);
bubbleSort(randomArr.concat());
console.timeEnd(`1`);
console.time(`2`);
bubbleSort2(randomArr.concat());
console.timeEnd(`2`);
console.time(`3`);
bubbleSort3(randomArr.concat());
console.timeEnd(`3`);
console.time(`4`);
bubbleSort4(randomArr.concat());
console.timeEnd(`4`);
VM371:4 1: 329.705ms
VM371:7 2: 379.501ms
VM371:10 3: 310.843ms
VM371:13 4: 306.847ms

在經過多次測試發現一個有趣的現象執行最快的是第4種方法,最慢的是第2種,沒錯最慢的是我認為可以提高效能的第2種方法,這就相當尷尬了,不知道有哪位小夥伴可以解釋一下

相關文章