数据结构-常见的排序算法

187 阅读4分钟

排序算法时间复杂度

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
冒泡排序O(n*n)O(n*n)O(n*n)O(1)In-place稳定
选择排序O(n*n)O(n*n)O(n*n)O(1)In-place不稳定
插入排序O(n*n)O(n)O(n*n)O(1)In-place稳定
快速排序O(n*log n)O(n*log n)O(n*n)O(n*log n)In-place不稳定
归并排序O(n*log n)O(n*log n)O(n*log n)O(n)Out-place稳定
希尔排序O(n*log n)O(n)O(n*n)O(1)In-place不稳定
堆排序O(n*log n)O(n)O(n*log n)O(1)In-place不稳定

注释:

  • In-place: 占用常数内存,不占用额外内存
  • Out-place: 占用额外内存

冒泡排序

定义

就是将大的数组往上冒(我这里上指的是数组尾端) , 所以冒一次就可以将一个最大的数冒的最上面, 所以不需要每次都全部冒

代码实现

function bubbleSort(arr) {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for(let j = 1; j < len - i; j ++) {
      if (arr[j-1] > arr[j]) {
        let temp = arr[j-1];
        arr[j-1] = arr[j];
        arr[j] = temp;
      }
    }
  }
  return arr;
}

选择排序

定义

选择排序 , 从开始位置每次找出最小那个数字, 放到起始位置 ,然后起始位置迭代重复操作

代码实现

function selectionSort(arr) {
  for (let i = 0; i < arr.length; i++) {
    let minIndex = i;
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    const temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp
  }
  return arr
}

插入排序

定义

插入排序 比如 2,3,1 数组 , 假如此时到1了进行插入排序, 1先拿出来, 3和1比较大,就向后移动一下,然后2和1比较还是大就继续向后移动, 此时就可以找到合适位置插入了

代码实现

function insertSort(arr) {
  for (let i = 1; i < arr.length; i++) {
    let j = i - 1;
    let temp = arr[i];
    while(j >= 0 && arr[j] > temp) {
      arr[j + 1] = arr[j];
      j --;
    }
    arr[j + 1] = temp;
  }
  return arr;
}

快速排序

定义

1、在数组中找一个元素(节点),比它小的放在节点的左边,比它大的放在节点右边。一趟下来,比节点小的在左边,比节点大的在右边; 2、不断执行这个操作....

实现

function quickSort(arr = []) {
  if (arr.length <= 1) {
    return arr;
  }
  // 取第一个元素作为判断的依据
  let left = [], right = [];
  for(let i = 1; i < arr.length; i ++) {
    if (arr[i] < arr[0]) {
      left.push(arr[i])
    } else {
      right.push(arr[i])
    }
  }
  return quickSort(left).concat(arr[0], quickSort(right));
}

归并排序

定义

  1. 将两个已排好序的数组合并成一个有序的数组
  2. 将元素分隔开来,看成是有序的数组,进行比较合并
  3. 断拆分和合并,直到只有一个元素

实现

function merge(left, right) {
  let res = [];
  while(left.length > 0 && right.length > 0) {
    if (left[0] < right[0]) {
      res.push(left.shift());
    } else {
      res.push(right.shift());
    }
  }
  return res.concat(left, right);
}
function mergeSort(arr) {
  if (arr.length <= 1) {
    return arr;
  }
  // arr.length >> 1 相当于 Math.floor(arr.length/2) 也可以这样 arr.length / 2 | 0
  let mid = arr.length >> 1;
  let left = arr.slice(0, mid);
  let right = arr.slice(mid);
  return merge(mergeSort(left), mergeSort(right));
}

希尔排序

定义

  1. 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。
  2. 所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。
  3. 取第二个增量d2小于d1重复上述的分组和排序,直至所取的增量dt=1(dt小于dt-l小于…小于d2小于d1),即所有记录放在同一组中进行直接插入排序为止。

代码实现

function shellSort(arr) {
  const len = arr.length;
  let gap = len >> 1;
  for (gap; gap > 0; gap = gap >> 1) {
    for (let i = gap; i < len; i++) {
      let j = i - gap;
      const temp = arr[i];
      while(j >= 0 && arr[j] > temp) {
        arr[j + gap] = arr[j];
        j -= gap;
      }
      arr[j + gap] = temp;
    }
  }
  return arr;
}

堆排序

定义

利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

  1. 将待排序的序列构造成一个最大堆,此时序列的最大值为根节点
  2. 依次将根节点与待排序序列的最后一个元素交换
  3. 再维护从根节点到该元素的前一个节点为最大堆,如此往复,最终得到一个递增序列

代码实现

function heapSort(arr) {
  // 初始化大顶堆,从第一个非叶子节点开始
  let len = arr.length;
  let i = Math.floor(len / 2 - 1);
  for (i; i >= 0; i --) {
    adjustHeap(arr, i, len);
  }
  // 排序,每一次for循环找出一个当前最大值,数组长度减一
  for (let j = len - 1; j > 0; j --) {
    // 根节点与最后一个节点交换
    const temp = arr[0];
    arr[0] = arr[j];
    arr[j] = temp;
    adjustHeap(arr, 0, j);
  }
  return arr;
}
function adjustHeap(arr, index, len) {
  let j = (index << 1) + 1; //左子节点索引
  // 左子节点索引超出计算范围,直接返回。
  if (j >= len) {
    return;
  }
  // 找到两个孩子中较大的一个,再与父节点比较
  if (j + 1 < len && arr[j] < arr[j + 1]) {
    j ++;
  }
  // 如果父节点小于子节点则交换;否则跳出
  if (arr[index] < arr[j]) {
    const temp = arr[index];
    arr[index] = arr[j];
    arr[j] = temp;
    adjustHeap(arr, j, len);
  }
}