第2章 堆

161 阅读2分钟

基本概念

必须是完全二叉树,任一节点的值是其子树所有结点的最大值或最小值

image.png

时间空间复杂度

名称最好平均最坏内存稳定性备注
归并排序nlog(n)nlog(n)nlog(n)nYes...
快速排序nlog(n)nlog(n)n2log(n)No在 in-place 版本下,内存复杂度通常是 O(log(n))
希尔排序nlog(n)取决于差距序列n(log(n))21No...
堆排序nlog(n)nlog(n)nlog(n)1No...

引用: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
    }
  }
}

堆排序

leetcode 451

image.png 思路:

  • 将问题转换为熟悉的领域,对于字符串的问题,首先考虑转为数组,然后题目中要求排序,考虑将字符串转换为数字
  • 在经过转换之后,选择堆排序
  • 排序完成后,将排序结果转为字符串
/**
 * @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()

};

image.png

es6 map

学习视频
map 的存储结构

image.png

    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中的所有元素删除

遍历
image.png

// 还可以同时获得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,有了这个方法就可以很方便的实现上面的操作。