js 数组排序

702 阅读4分钟
排序全部以升序为例     (n表示数组长度, i表示第几趟排序,从0开始)

冒泡排序

基本思想:

遍历数组,进行n - 1趟排序,并在每趟排序中比较相邻两个元素,若满足条件则交换数据。而第i趟排序会将在n-i个元素中的最大值交换至n-i-1的位置

function bubbleSort(arr) {
  if (Object.prototype.toString.call(arr) !== '[object Array]') return ;
  let len = arr.length;
  for (let i = 0; i < len - 1; i++) {  // 进行len - 1趟排序
    for (let j = 0; j < len - i - 1; j++) {  // 将len - i 个元素中的最大值交换至len - i - 1的位置
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];  // 数据交换
      }
    }
  }

  return arr;
}
let arr = [3, 6, 4, 2, 11, 10, 6];
bubbleSort(arr);    // [2, 3, 4, 6, 6, 10, 11]


我们可以从上图中看出,在第二趟排序之后,只进行了比较,但却没有进行数据交换,所以我们可以进一步优化。

优化

我们设置一个标志变量flag,用于表示本趟排序中是否有数据交换操作。当flag为1则说明本趟排序仍有数据交换操作,并在进行完每趟排序后置0;而flag为0则表示本趟没有数据交换操作。

function bubbleSort(arr) {
  if (Object.prototype.toString.call(arr) !== '[object Array]') return ;
  let len = arr.length;
  // 设置标志变量flag,当flag = 1时说明本趟排序仍有元素交换;
  // flag = 0时说明本趟排序没有元素交换,进行下一趟排序
  let flag = 1;
  for (let i = 0; i < len - 1 && flag === 1; i++) {  // 进行len - 1趟排序
    flag = 0;  // 每执行一趟排序,flag 置0
    for (let j = 0; j < len - i - 1; j++) {  // 将len - i 个元素中最大的交换至len - i - 1的位置
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];  // 数据交换
        flag = 1;  // 发生交换,flag置1
      }
    }
  }

  return arr;
}
let arr = [3, 6, 4, 2, 11, 10, 6];
bubbleSort(arr);    // [2, 3, 4, 6, 6, 10, 11]

再优化(双向数据扫描)

以上冒泡排序在一趟排序中只能找到一个最大值(或者最小值),我们利用在每趟排序中正向冒泡和反向冒泡的方法一次找到两个最值,从而减少排序趟数。

function bubbleSort(arr) {
  let [low, high] = [0, arr.length - 1];
  let j;

  while (low < high) {
    for (j = low; j < high; j++) {  // 从左往右扫描寻找最大值
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
    --high;    // 修改high值,前移以为

    for (j = high; j > low; --j) {  // 从右往左扫描寻找最小值
      if (arr[j] < arr[j - 1]) {
        [arr[j], arr[j - 1]] = [arr[j - 1], arr[j]];
      }
    }
    ++low;    // 修改ow值,后移一位
  }
  
  return arr;
}

选择排序

选择排序是一种原地排序算法,即不需要额外的存储空间

基本思想

遍历数组,进行n-1趟排序,在第i趟排序中,在n-i-1个元素中找到最小值。若最小值不位于n-i个元素中的第一个,则将最小值与第i个元素互换

function selectSort(arr) {
  if (Object.prototype.toString.call(arr) !== '[object Array]') return ;
  let len = arr.length;
  let minIndex;  // 最小值索引
  for (let i = 0; i < len - 1; i++) {  // 进行len - 1趟排序
    minIndex = i;  
    for (let j = i + 1; j < len; j++) {  // 在len - i - 1个元素中中找到最小值
      if (arr[j] < arr[minIndex]) {
        minIndex = j;  // 记录最小值索引
      }
    }
    if (minIndex !== i) { // 如果最小的元素不位于len - i个元素的第一个 => 也就是最小值的索引不是i
      [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];  // 数据交换
    }
  }

  return arr;
}

let arr = [3, 6, 4, 2, 11, 10, 6];
selectSort(arr);    // [2, 3, 4, 6, 6, 10, 11]

插入排序

基本思想

通过构建有序序列,对于未排序数据,每次选择一个未排序数据中的第一个元素,在已排序序列中从后向前扫描,找到对应位置插入。

function insertSort(arr) {
  if (Object.prototype.toString.call(arr) !== '[object Array]') return ;
  let len = arr.length;
  let temp, j;
  for (let i = 1; i < len; i++) {  // 进行len - 1趟排序
    temp = arr[i];  // 将arr[i]保存在临时变量中
    j = i - 1;
    while (j >= 0 && temp < arr[j]) {  // 将临时变量中的值在已排序中进行比较
      // 将arr[j]后移,再将j-1
      arr[j + 1] = arr[j--]; // arr[j + 1] = arr[j]; j--;
    }
    arr[j + 1] = temp;  // 将临时变量中的值插入指定位置
  }

  return arr;
}

let arr = [5, 6, 4, 2, 11, 10, 6];
insertSort(arr);    // [2, 3, 4, 6, 6, 10, 11]