排序算法

138 阅读2分钟

冒泡排序

var bubbleSort = function (array) {
  for (let i = 0; i < array.length; i++) {
    for (let j = 0; j < array.length - i - 1; j++) {
      if (array[j] > array[j + 1]) {
        let tmp = array[j];
        array[j] = array[j + 1]
        array[j + 1] = tmp;
      }
    }
  }
  return array;
}
//改进1 记录最后一次交换的位置
var bubbleSort1 = function (array) {
  let i = array.length - 1;
  while (i > 0) {
    let pos = 0;
    for (let j = 0; j < i; j++) {    //最后一次交换的位置加一 之后有序  减少趟数
      if (array[j] > array[j + 1]) {
        pos = j;
        let tmp = array[j];
        array[j] = array[j + 1]
        array[j + 1] = tmp;
      }
    }
    i = pos;
  }
  return array
}
//改进2 正反两个方向冒泡 减少趟数
var bubbleSort2 = function (array) {
  let low = 0;
  let high = array.length - 1;
  while (low < high) {
    for (let j = low; j < high; j++) {
      console.log(j)
      if (array[j] > array[j + 1]) {
        let tmp = array[j];
        array[j] = array[j + 1]
        array[j + 1] = tmp;
      }
    }
    --high; //一趟之后找到最大值 high前移一位
    for (let j = high; j > low; j--) {
      if (array[j] < array[j - 1]) {
        let tmp = array[j];
        array[j] = array[j - 1]
        array[j - 1] = tmp;
      }
    }
    ++low; //一趟之后找到最小值 low后移一位
  }
  return array
}
var arr = [2, 4, 5, 1, 7, 3]
var bubble = bubbleSort(arr)
console.log(bubble)

算法分析

交换排序 稳定

最佳情况:输入的数组已经是正序了 T(n) = O(n)

最坏情况:输入的数组是反序 T(n) = O(n2)

平均情况: T(n) = O(n2)

选择排序

var selectionSort = function (array) {
  let len = array.length;
  for (let i = 0; i < len - 1; i++) {  //n-1趟
    let minindex = i;   //该趟找到的最小值应该放置的位置
    for (let j = i + 1; j < len; j++) { 
      if (array[j] < array[minindex]) {  //找到最小值的索引
        minindex = j
      }
    }
    let tmp = array[i]  //交换
    array[i] = array[minindex]
    array[minindex] = tmp;
  }
  return array;
}

算法分析

选择排序 不稳定

最佳情况:T(n) = O(n2)

最差情况:T(n) = O(n2)

平均情况:T(n) = O(n2)

时间复杂度与输入序列无关

插入排序

var insertionSort = function (array) {
  let len = array.length;
  for (let i = 1; i < len; i++) {  //从下标1开始
    let key = array[i];           //记录下一个插入的值
    let j = i - 1;                 //和有序部分的最后一个比较
    while (j >= 0 && array[j] > key) {
      array[j + 1] = array[j];        //交换
      j--;
    }
    array[j + 1] = key;                 //空位j+1 就是插入位置
  }
  return array;
}

//改进 二分法 在有序的部分二分查找
var insertionSort = function (array) {
  let len = array.length;
  for (let i = 1; i < len; i++) {
    let left = 0;
    let right = i - 1;
    let key = array[i]
    while (left <= right) {           //有等号
      let middle = parseInt((left + right) / 2)  //取整数部分  
      if (key < array[middle]) {                 //当进行到中间部分 没有等号,否则不稳定
        right = middle - 1;     
      }
      else {
        left = middle + 1;
      }
    }
    for (let j = i - 1; j <= left; j++) {  //从left开始所有元素后移一位 统一移动
      array[j + 1] = array[j]
    }
    array[left] = key; //left就是元素插入的位置
  }
  return array;
}

算法分析

插入排序 稳定

最佳情况:T(N) = O(n)

最差情况:T(n) = O(n2)

平均情况:T(n) = O(n2)

希尔排序

var shellSort = function (array) {
  let len = array.length;
  let gap = Math.floor(len / 2);
  //动态定义间隔???  这个没搞懂
  while (gap < len / 5) {
    gap = gap * 5 + 1;
  }
  for (gap; gap > 0; gap = Math.floor(len / 2)) {   //直到gap=1
    for (let i = gap; i < len; i++) {  // 从第一个gap开始  相当于插入排序 
      let j = i - gap;                 //向前找到和i一组的
      let key = array[i]               //标记一下下标为i的值
      while (j >= 0 && array[j] > key) {  //如果前边的那个大
        array[j + gap] = array[j];       //就往后挪一个
        j = j - gap;                     //找到在前面一个 和i一组的那个元素
      }
      array[j + gap] = key;
    }
  }
  return array
}

算法分析

不稳定 (相同的元素不在一组,可能被排到前边)

最佳情况:玄学

最坏情况:

平均情况:

归并排序

//分支的思想
var mergeSort = function (array) {
  if (array.length < 2) {   //最底层的情况
    return arr
  }
  let middle = Math.floor(array.length / 2);
  let left = array.slice(0, middle)
  let right = array.slice(middle, array.length)
  return merge(mergeSort(left), mergeSort(right))  
}
function merge (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())
  }
  return result
}

算法分析

归并排序 稳定 分治的思想

最佳情况:T(n) = O(nlogn)

最坏情况:T(n) = O(nlogn)

平均情况:T(n) = O(nlogn)

归并排序的时间复杂度和选择排序一样,不受输入数组序列影响

时间复杂度降低,代价是需要额外的空间,最大需要O(n)的空间。

快速排序

//方法1
var quickSort = function (array, left, right) {
  if (left < right) {
    let pivot = array[right];    //基准为最后一个数
    let i = left - 1;            //i指向left之前
    for (let j = left; j <= right; j++) {  //从left开始遍历到right
      if (array[j] <= pivot) {   //当遍历到的数小于等于pivot时 
        i++;                      //挪到i的位置  把这个数和array[i]交换 这样i的位置上的数是上一次找到的比pivot小的数
        let temp = array[j];        //最后j=right时,满足array[j]=pivot 就把pivot的位置找到了,放在i的位置上
        array[j] = array[i];        //此时iz左面的数都比pivot小,i右边的数都比pivot大
        array[i] = temp;
      }
    }
    quickSort(array, left, i - 1)   //再对左边和右边分别快排
    quickSort(array, i + 1, right)
  }
  return array;
}
//方法2
var quickSort = function (array) {
  if (array.length < 2) {          //出口
    return array
  }
  let pivotindex = Math.floor(array.length / 2)
  let pivot = array.splice(pivotindex, 1)[0];        //把基准从数组里面取出来
  let left = [];                       
  let right = [];
  for (let i = 0; i < array.length; i++) {   //遍历每一个元素 把小的放left,大的放右边right
    if (array[i] < pivot) {
      left.push(array[i])
    }
    else {
      right.push(array[i])
    }
  }
  return quickSort(left).concat([pivot], quickSort(right))  //左 pivot 右相连
}

算法分析

不稳定

最佳情况:T(n) = O(nlogn) 分成logn组

最坏情况:T(n) = O(n2) 数组已经有序 分成n组

平均情况:T(n) = O(nlogn)

堆排序

var heapSort = function (array) {
  var heapsize = array.length;
  //建堆
  for (let i = Math.floor(heapsize / 2) - 1; i >= 0; i--) {  //从第一个非叶子节点开始
    heapify(array, i, heapsize);
  }
  for (let j = heapsize - 1; j >= 0; j--) {
    let temp = array[0]
    array[0] = array[j]
    array[j] = temp;
    heapify(array, 0, --heapsize) //从顶部开始维护堆的性质 同时堆的大小自减一
  }
  return array;
}

function heapify (array, x, len) {
  let l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;  //找到左右孩子
  if (l < len && array[l] > array[largest]) {   //将较大的孩子设为largest
    largest = l;
  }
  if (r < len && array[r] > array[l]) {
    largest = r
  }
  if (x != largest) {  //如果找到了更大的 ,交换
    temp = array[x];
    array[x] = array[largest];
    array[largest] = temp;
    heapify(arr, largest, len)  //交换后继续向下维护  如果进行到了叶子节点,就维护完了,什么也不会做
  }
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(heapSort(arr))

算法分析

不稳定

最佳情况:T(n) = O(nlogn)

最差情况:T(n) = O(nlogn)

平均情况:T(n) = O(nlogn)

二叉排序树