排序

145 阅读2分钟

冒泡排序

N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数

比较两个相邻的项,如果前一项大于后一项则交换他们的位置
每一轮比较,最大的项就会被依次交换(冒泡)到最后

function bubbleSort(arr) {
    let len = arr.length;
    for (let i = 0; i < len; i++) {
        for (let j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j+1]) {//相邻元素两两对比,前一项大于后一项则交换他们的位置
                [arr[j + 1], arr[j]] = [arr[j], arr[j + 1]];
            }
        }
    }
    return arr;
}

选择排序

选择排序跟冒泡排序非常类似,唯一的区别就是选择排序每次遍历时,将各个元素比较,将最大值或最小值的索引存放在一个变量中,全部比较完了以后,再将该索引上的元素进行就交换。

简单来说就是选择排序是每次遍历交换一次,而冒泡排序每次遍历需要交换多次,因此选择排序一般来说是要比冒泡排序效率高一点的。

const selectSort = function(arr) {
  for(let i = 0; i < arr.length - 1; i++) {
    let min = i;
    for(let j = min; j < arr.length; j++) {
      if(arr[j] < arr[min]) {
        // 发现比min小的,则重新赋值min
        min = j
      }
      // 将得到的最小值的索引min上的元素与我们初始遍历的位置上的元素交换
      const temp = arr[i];
      arr[i] = arr[min];
      arr[min] = temp;
    }
  }
  // 返回排序后的数组
  return arr
}

let arr = [45, 66, 1, 19, 34, 80, 2]
console.log(selectSort(arr));

插入排序

  1. 先把第一个元素做为有序数组 

  2. 当前索引i指定的元素依次和 [0,i-1] 的有序数组内的元素 比较:比当前元素大的则向后移动其位置,直到找到比当前元素小的元素,插到其后面,得到新的有序数组

  3. 开始下一轮循环,直到循环完成,返回排序后的数组

    const insertSort = (arr) => { // 1. 从索引为1的元素开始向后遍历数组 for(let i = 1; i < arr.length; i++) { let temp = arr[i] // 当前索引i指定的元素(即当前要插入的元素) let j = i // 3. 从右往左将有序区域内的元素与temp比较 while(arr[j - 1] > temp && j > 0) { arr[j] = arr[j - 1] j-- } // 4. 将temp插入到合适的位置 arr[j] = temp } // 返回排序后的数组 return arr }

归并排序

// 归并(合并两个有序数组)
const merge = function(left, right) {
  let result = []
  while(left.length && right.length) {
    if(left[0] < right[0]){
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }
  while(left.length) {
    result.push(left.shift())
  }
  while(right.length) {
    result.push(right.shift())
  }
  console.log('result', result)
  return result
}
// 采用自上而下的递归方法拆分再自下而上合并
const mergeSort = function(arr) {
  if(arr.length < 2) {
    return arr
  }
  const middle = Math.floor(arr.length/2)
  const left = arr.slice(0,middle);
  const right = arr.slice(middle);
  return merge(mergeSort(left), mergeSort(right))
}

快速排序

方法一:

  1. 选择数组中间项作为基数,并从数组中取出此基数;
  2. 定义两个空数组,遍历数组,逐个与基数比对,较小的放左边容器,较大的放右边容器;
  3. 递归处理两个数组的元素,并将处理后的数据与基数按大小合并成一个数组,返回

改进之处:1)使用splice方法取基准点元素时间复杂度高;2)定义两个数组存储数据空间复杂度更高

const quickSort = function (arr) {
  if(arr.length < 2) {
    return arr
  }
  // 抽出基准点元素
  const mask = arr.splice(Math.floor(arr.length/2), 1)[0]
  const left = []
  const right = []
  
  for(let i = 0; i < arr.length; i++) {
    if(arr[i] < mask) {
      left.push(arr[i])//比基准点小的放在左边数组
    } else {
      right.push(arr[i])//比基准点大的放在右边数组
    }
  }
  //递归执行以上操作,对左右两个数组进行操作,直到数组长度为 <= 1
  return [...quickSort(left), mask, ...quickSort(right)]
}

方法二:

  1. 通过下标取数组中间项作为基准点

  2. 创建两个指针,左边的指向数组第一个项,右边的指向最后一个项,移动左指针 i,直到找到一个比基准点大的项,接着,移动右边的指针 j,直到找到一个比基准点小的项,然后交换它们。重复这个过程,直到 i >= j。这个使比基准点小的都在左侧,比基准点大的都在右侧。这一步叫划分操作

  3. 接着,算法对划分后的小数组(较基准点小的值组成的的小数组, 以及较基准点大的值组成的小数组)重复之前的两个步骤,直到排序完成