排序算法记录

1.我的目的

      很久没有写过基本的排序算法了,想实现一下;最近应该会把基本排序算法都回忆一遍。写一个记录一个吧(以下代码都在leetcode上跑通过所有数据且时间复杂度在预期范围内)

2.代码

归并排序—我的代码:

let s = [1,2,5,3,5,1,4];
let funcA = function (arr) {
  let l = parseInt(arr.length/2);
  if (l > 0) {
    let left = funcA(arr.slice(0,l));
    let right = funcA(arr.slice(l));
    let leftIndex = left.length;
    let rightIndex = right.length;
    let resp = [];
    let leftTemp = 0;
    let rightTemp = 0;
    while (leftTemp < leftIndex && rightTemp < rightIndex) {
      if (left[leftTemp] < right[rightTemp]) {
        resp.push(left[leftTemp]);
        leftTemp++;
      }
      else {
        resp.push(right[rightTemp]);
        rightTemp++;
      }
    };
    if (leftTemp === leftIndex) {
      resp = [...resp, ...right.slice(rightTemp)];
    }
    if (rightTemp === rightIndex) {
      resp = [...resp, ...left.slice(leftTemp)];
    }
    return resp;
  } else {
    return arr;
  }
}
console.log(funcA(s))

时间复杂度包括 拆解数组 + 合并排序数组
拆解数组时间为为 l o g 2 n log_2{n} log2n
合并排序的时候为 合并*排序 合并为 l o g 2 n log_2{n} log2n ,我们每比较一个值就会插入到返回数组中,所以最多比较n次;即时间复杂度为n l o g 2 n log_2{n} log2n


快速排序—我的代码:

let sortList = [2,34,3,6,1,21,53,10,21,6]
let quickSort = function (left, right, sortList) {
  let sign = sortList[right]
  let leftSign = left
  let rightSign = right
  while (leftSign < rightSign) {
    while (sortList[leftSign] <= sign && leftSign < rightSign) {
      leftSign++
    }
    sortList[rightSign] = sortList[leftSign]
    while (sortList[rightSign] >= sign && leftSign < rightSign) {
      rightSign--
    }
    sortList[leftSign] = sortList[rightSign]
  }
  sortList[leftSign] = sign
  if (left < leftSign-1) {
    quickSort(left, leftSign-1, sortList)
  }
  if (right > rightSign+1) {
    quickSort(rightSign+1, right, sortList)
  }
}
quickSort(0, sortList.length-1, sortList)
console.log(sortList)

时间复杂度包括 排列次数(层数)*每一层排列时间
容易想到 排列层数是变化的,所以快排也被称为不稳定的;
    在最优情况下是一个等比数列 也就是2(1- 2 k 2^k 2k)/(1-2) = n 得到 k = l o g 2 n log_2{n} log2n , 然后每层都需要排列n个数,所以是O(n l o g 2 n log_2{n} log2n)
    在最坏情况,也就是不能刚好二分的去排列了,层数就会变高,最坏情况就是每层都在最左边或者最右边,那么也就是要排列n层,事件复杂度就是O( n 2 n^2 n2)

注意事项:①while的时候带上leftSign < rightSign,因为可能直接一路比较都是符合条件使得leftSign大于rightSign了


归并排序----我的代码

let sortList = [-4, 0, 7, 4, 9, -5, -1, 0, -7, -1]
let lastLeaf = parseInt((sortList.length - 1) / 2)
let sortOnetime = function (arr, i, lenSign) {
  let sign = i
  if (arr[2 * i + 1] !== undefined && arr[2 * i + 1] > arr[sign] && (2 * i + 1) <= lenSign) {
    sign = 2 * i + 1
  }
  if (arr[2 * i + 2] !== undefined && arr[2 * i + 2] > arr[sign] && (2 * i + 2) <= lenSign) {
    sign = 2 * i + 2
  }
  if (sign !== i){
    let temp = arr[sign]
    arr[sign] = arr[i]
    arr[i] = temp
    sortOnetime(arr, sign, lenSign)
  }
}
// 初始化一个升序堆
for (let i = lastLeaf; i >= 0; i--) {
  sortOnetime(sortList, i, sortList.length - 1)
}
// 交换首尾并重新排序堆
for (let i = sortList.length - 1; i > 0; i--) {
  sortOnetime(sortList, 0, i)
  let temp = sortList[i]
  sortList[i] = sortList[0]
  sortList[0] = temp
}
console.log(sortList)

注意:
    一开始自己犯了两个错误:
    1.每次在排序堆的时候左边右边比较如果都大于父节点,我会对两个子节点自身继续进行排序, 而这样是不必要的,大大增加了时间复杂度 ,哭```; 因为父节点的子节点肯定是各自有序的,是调整之后可能对其有序造成影响,所以只需要取递归排序调换的那一边(也就是最大的那一边)就可以了
    2.在对换堆顶和堆尾元素之后,要记得把重排序的长度减1,被影响的节点排序时也应遵守这个长度限制

堆排序的讲解网上一大堆,就不赘述了,重点理解为什么要对改变顺序的子节点进行继续排序。 举一个比较实际的例子: 堆[11,9,7,7,8,6] 我们交换首尾=>[6,9,7,7,8,11]然后比较当前节点并替换 [9,6,7,7,8,11] ; 到这里,仔细观察堆 明显678的父子节点不符合堆定义,所以为了解决这种问题,我们还是需要对替换过的节点继续进行排序操作的。

时间复杂度:
初始化堆 + 排列每个节点
说下排列每个节点:每一次排列我们就想着是堆头排到叶子节点去,这样需要操作的次数是最多了,在这种情况下,叶子节点高度Log(n+1) ,从堆首操作下去,每一高度操作一次,也就是操作log(n+1)次了,所以随着需要排序的堆数量变少,时间复杂度为log(n+1) + log(n) + log(n-1) + ````log(2) 这个等式和nlog(n)同阶(这个网上也有很多大佬做了数学方面的证明,这里不记录了),所以我们一般也归为O(nlog(n))


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页