基本概念
必须是完全二叉树,任一节点的值是其子树所有结点的最大值或最小值
时间空间复杂度
| 名称 | 最好 | 平均 | 最坏 | 内存 | 稳定性 | 备注 |
|---|---|---|---|---|---|---|
| 归并排序 | nlog(n) | nlog(n) | nlog(n) | n | Yes | ... |
| 快速排序 | nlog(n) | nlog(n) | n2 | log(n) | No | 在 in-place 版本下,内存复杂度通常是 O(log(n)) |
| 希尔排序 | nlog(n) | 取决于差距序列 | n(log(n))2 | 1 | No | ... |
| 堆排序 | nlog(n) | nlog(n) | nlog(n) | 1 | No | ... |
引用:juejin.cn/post/684490…
堆排序相比于快排来说,最坏的情况下时间复杂度也为nlog(n)
前提
满二叉树性质:如果父元素为i,则左子树为2i,右子树为2i+1。由于在js中索引从零开始,所以左子树为2i+1,右子树为2i+2。
构建最大堆
思路从最后一个父节点开始,父节点分别和左右节点比较大小,如果发现子节点比父节点大则两两交换,交换完之后,由于结构破坏,所以交换后最下面的节点再次递归。完成一次完整的交换。依次从最后一个父节点遍历到第一个节点,完成最大堆构建,把最上面的节点和最后一个节点交换,然后令构建堆的范围减一,以此往复直到size<=0
class Heap {
constructor (data) {
this.data = data
}
// 交互两个数
swap (arr, a, b) {
if (a === b) {
return ''
}
let c = arr[a]
arr[a] = arr[b]
arr[b] = c
}
// 构建最大堆
mapxHeapify (Arr, i, size) {
// 左节点
let l = i * 2 + 1
// 右节点
let r = i * 2 + 2
let largest = i
if (l <= size && Arr[l] > Arr[largest]) {
largest = l
}
if (r <= size && Arr[r] > Arr[largest]) {
largest = r
}
if (largest !== i) {
this.swap(Arr, i, largest)
this.mapxHeapify(Arr, largest, size)
}
}
sort () {
let iArr = this.data
let n = iArr.length
if (n <= 1) {
return iArr
} else {
for (let i = Math.floor(n / 2); i >= 0; i--) {
this.mapxHeapify(iArr, i - 1, n)
}
for (let j = 0; j < n; j++) {
this.swap(iArr, 0, n - 1 - j)
this.mapxHeapify(iArr, 0, n - 1 - j - 1)
}
return iArr
}
}
}
堆排序
思路:
- 将问题转换为熟悉的领域,对于字符串的问题,首先考虑转为数组,然后题目中要求排序,考虑将字符串转换为数字
- 在经过转换之后,选择堆排序
- 排序完成后,将排序结果转为字符串
/**
* @param {string} s
* @return {string}
*/
class Heap {
constructor (str) {
let map = new Map()
str.split('').forEach(item => {
if(map.has(item)) {
map.set(item, map.get(item) + 1)
} else {
map.set(item, 1)
}
})
this.map = map
this.data = Array.from(map.values())
}
swap (arr, a, b) {
if (a === b) {
return ''
}
let c = arr[a]
arr[a] = arr[b]
arr[b] = c
}
// 构建最大堆
mapxHeapify (Arr, i, size) {
// 左节点
let l = i * 2 + 1
// 右节点
let r = i * 2 + 2
let largest = i
if (l <= size && Arr[l] > Arr[largest]) {
largest = l
}
if (r <= size && Arr[r] > Arr[largest]) {
largest = r
}
if (largest !== i) {
this.swap(Arr, i, largest)
this.mapxHeapify(Arr, largest, size)
}
}
sort () {
let iArr = this.data
let n = iArr.length
if (n <= 1) {
return iArr
} else {
for (let i = Math.floor(n / 2); i >= 0; i--) {
this.mapxHeapify(iArr, i - 1, n)
}
for (let j = 0; j < n; j++) {
this.swap(iArr, 0, n - 1 - j)
this.mapxHeapify(iArr, 0, n - 1 - j - 1)
}
return iArr
}
}
toString () {
let arr = this.sort()
let str = []
while (arr.length) {
let top = arr.pop()
for (let [k,v] of this.map) {
if (v === top) {
str.push(k.repeat(v))
this.map.delete(k)
break
}
}
}
return str.join('')
}
}
var frequencySort = function(s) {
let heap = new Heap(s)
return heap.toString()
};
es6 map
学习视频
map 的存储结构
const map = new Map()
map.set(1,'one') // 通过set方法可以保存键值对
map.get(1) //输出为'one', 通过get方法,根据存储的key来取得value
map.size //返回为包含键值对个数
map.has(key) //判断Map对象中是否存在key,有则返回true,否则返回false
map.delete(key) //通过键值从Map中移除对应的数据
map.clear() //将这个map中的所有元素删除
遍历
// 还可以同时获得key和value,第一个为key,第二个为vlaue
for (let [k,v] of this.map) {
console.log(k,v) // 1,one 2,two 3,three
}
总结 es6中的map适用于对一个数组的元素个数进行记录,把元素作为key,元素个数作为value,有了这个方法就可以很方便的实现上面的操作。