堆排序

使用优先队列-最小/最大堆可实现。

优先队列

优先队列是一种能完成以下任务的队列:插入一个数值,取出最小的数值(获取数值,并且删除)。优先队列可以用二叉树来实现,我们称这种为二叉堆。

最小堆

最小堆是二叉堆的一种,是一颗完全二叉树(一种平衡树), 其特点是父节点的键值总是小于或者等于子节点。

  1. 实现细节(两个操作):
  2. push:向堆中插入数据时,首先在堆的末尾插入数据,然后不断向上提升,直到没有大小颠倒时。
  3. pop:从堆中删除最小值时首先把最后一个值复制到根节点上,并且删除最后一个数值。然后不断向下交换, 直到没有大小颠倒为止。在向下交换过程中,如果有两个子儿子都小于自己,就选择较小的
  4. 插入时间复杂度O(logN),删除时间复杂度O(logN),两个二叉堆合并时间复杂性O(NlogN).

最大堆同理。可用此结构实现堆排序算法。

  1. /*
  2. 最小堆
  3. */
  4. package main
  5. import "fmt"
  6. type Heap struct {
  7. Size int
  8. Elems []int
  9. }
  10. func NewHeap(MaxSize int) *Heap {
  11. h := new(Heap)
  12. h.Elems = make([]int, MaxSize, MaxSize)
  13. return h
  14. }
  15. func (h *Heap) Push(x int) {
  16. h.Size++
  17. // i是要插入节点的下标
  18. i := h.Size
  19. for {
  20. if i <= 0 {
  21. break
  22. }
  23. // parent为父亲节点的下标
  24. parent := (i - 1) / 2
  25. // 如果父亲节点小于等于插入的值,则说明大小没有跌倒,可以退出
  26. if h.Elems[parent] <= x {
  27. break
  28. }
  29. // 互换当前父亲节点与要插入的值
  30. h.Elems[i] = h.Elems[parent]
  31. i = parent
  32. }
  33. h.Elems[i] = x
  34. }
  35. func (h *Heap) Pop() int {
  36. if h.Size == 0 {
  37. return 0
  38. }
  39. // 取出根节点
  40. ret := h.Elems[0]
  41. // 将最后一个节点的值提到根节点上
  42. h.Size--
  43. x := h.Elems[h.Size]
  44. i := 0
  45. for {
  46. // a,b为左右两个子节点的下标
  47. a := 2*i + 1
  48. b := 2*i + 2
  49. // 没有左子树
  50. if a >= h.Size {
  51. break
  52. }
  53. // 有右子树,找两个子节点中较小的值
  54. if b < h.Size && h.Elems[b] < h.Elems[a] {
  55. a = b
  56. }
  57. // 父亲小直接退出
  58. if h.Elems[a] >= x {
  59. break
  60. }
  61. // 交换
  62. h.Elems[i] = h.Elems[a]
  63. i = a
  64. }
  65. h.Elems[i] = x
  66. return ret
  67. }
  68. func (h *Heap) Display() {
  69. fmt.Printf("Size:%d,Elems:%#v\n", h.Size, h.Elems[0:h.Size])
  70. }
  71. func main() {
  72. h := NewHeap(100)
  73. h.Display()
  74. h.Push(3)
  75. h.Push(6)
  76. h.Push(7)
  77. h.Push(27)
  78. h.Push(1)
  79. h.Push(2)
  80. h.Push(3)
  81. h.Display()
  82. fmt.Println(h.Pop())
  83. h.Display()
  84. fmt.Println(h.Pop())
  85. h.Display()
  86. fmt.Println(h.Pop())
  87. h.Display()
  88. fmt.Println(h.Pop())
  89. h.Display()
  90. fmt.Println(h.Pop())
  91. h.Display()
  92. }

左偏树

最小堆/最大堆如果两个堆进行合并,时间复杂度较高,左偏树是可合并的二叉堆,首先满足所有的堆的性质,其外,各种操作时间复杂度都是O(logN)。

  1. 左偏树的树节点需要保存的信息有:
  2. 1.左右子树节点编号
  3. 2.此节点到有空子结点的最短距离len(空子节点的节点,就是子节点数不足2个的节点)
  4. 3.自身权值
  5. 左偏就是每个节点的左子节点的len不小于右子节点的len(但并不代表左子节点数一定不小于右子节点数),那么可知左偏树中一个节点的距离就是右儿子距离+1(或没有右儿子),且左右子树都是左偏树。
  6. 合并树A和树B的操作方法如下:
  7. 1.如果AB有一个是空树,返回另一个。
  8. 2.如果A的优先级比B低,交换A,B。(确保左堆根节点小于右堆根节点)
  9. 3.递归处理,将BA的右子树合并。(B,Right(A)递归处理)
  10. 4.如果合并过后A的右儿子距离大于A的左儿子,交换A的左右儿子。(确保左儿子距离大于右儿子)
  11. 5.更新A的距离。

左偏树合并操作合并的是两棵左偏树,对于堆的插入,就是合并一棵树和一个节点,对于堆的删除,就是合并根的两棵子树。

  1. /*
  2. 左偏树
  3. */
  4. package main
  5. import (
  6. "fmt"
  7. )
  8. type LeftistHeap struct {
  9. Root *Node
  10. }
  11. type Node struct {
  12. Data int
  13. Distance int
  14. LeftChild *Node
  15. RightChild *Node
  16. }
  17. func New() *LeftistHeap {
  18. h := new(LeftistHeap)
  19. return h
  20. }
  21. func (n *Node) Dist() int {
  22. if n == nil {
  23. return -1 // 空节点距离为-1
  24. }
  25. return n.Distance
  26. }
  27. func (h *LeftistHeap) Push(data int) {
  28. newNode := new(Node)
  29. newNode.Data = data
  30. h.Root = h.Root.Merge(newNode)
  31. }
  32. func (h *LeftistHeap) Pop() int {
  33. if h.Root == nil {
  34. return -1 // pop完
  35. }
  36. data := h.Root.Data
  37. h.Root = h.Root.LeftChild.Merge(h.Root.RightChild)
  38. return data
  39. }
  40. // 合并两棵左偏树
  41. func (A *Node) Merge(B *Node) *Node {
  42. // 一棵树为空返回另外一棵树
  43. if A == nil {
  44. return B
  45. }
  46. if B == nil {
  47. return A
  48. }
  49. leftHeap := A
  50. rightHeap := B
  51. // 使左堆做为合并后的根节点
  52. if A.Data > B.Data {
  53. leftHeap = B
  54. rightHeap = A
  55. }
  56. // 递归:左堆的右子树和右堆进行合并,作为左堆右子树
  57. leftHeap.RightChild = leftHeap.RightChild.Merge(rightHeap)
  58. // 树翻转左右,确保左儿子距离大于右子
  59. if leftHeap.RightChild.Dist() > leftHeap.LeftChild.Dist() {
  60. leftHeap.LeftChild, leftHeap.RightChild = leftHeap.RightChild, leftHeap.LeftChild
  61. }
  62. if leftHeap.RightChild == nil {
  63. leftHeap.Distance = 0
  64. } else {
  65. leftHeap.Distance = leftHeap.RightChild.Dist() + 1
  66. }
  67. return leftHeap
  68. }
  69. // 递归先序排序
  70. func (n *Node) Display() {
  71. if n == nil {
  72. fmt.Println("null")
  73. return
  74. }
  75. fmt.Println(n.Data)
  76. fmt.Printf("Node:%d,Left child:", n.Data)
  77. if n.LeftChild != nil {
  78. n.LeftChild.Display()
  79. } else {
  80. fmt.Print("null")
  81. }
  82. fmt.Println()
  83. fmt.Printf("Node:%d,Right child:", n.Data)
  84. if n.RightChild != nil {
  85. n.RightChild.Display()
  86. } else {
  87. fmt.Print("null")
  88. }
  89. fmt.Println()
  90. }
  91. func (h *LeftistHeap) Display() {
  92. h.Root.Display()
  93. }
  94. func main() {
  95. n := New()
  96. n.Display()
  97. fmt.Println("---")
  98. n.Push(3)
  99. n.Push(1)
  100. n.Push(5)
  101. n.Push(8)
  102. n.Display()
  103. fmt.Println(n.Pop())
  104. fmt.Println(n.Pop())
  105. fmt.Println(n.Pop())
  106. fmt.Println(n.Pop())
  107. fmt.Println(n.Pop())
  108. fmt.Println(n.Pop())
  109. }

转载请注明:http://www.lenggirl.com/algorithm/heap.html

面试经典算法:优先队列,最大堆,堆排序,左偏树Golang实现的更多相关文章

  1. HDU 1512 Monkey King(左偏树+并查集)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1512 [题目大意] 现在有 一群互不认识的猴子,每个猴子有一个能力值,每次选择两个猴子,挑出他们所 ...

  2. 黄源河《左偏树的应用》——数字序列(Baltic 2004)

    这道题哪里都找不到. [问题描述] 给定一个整数序列a1, a2, … , an,求一个不下降序列b1 ≤ b2 ≤ … ≤ bn,使得数列{ai}和{bi}的各项之差的绝对值之和 |a1 - b1| ...

  3. 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)

    1367: [Baltic2004]sequence Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Ou ...

  4. 洛谷P4331 [BOI2004] Sequence 数字序列 [左偏树]

    题目传送门 数字序列 题目描述 给定一个整数序列 a1​,a2​,⋅⋅⋅,an​ ,求出一个递增序列 b1​<b2​<⋅⋅⋅<bn​ ,使得序列 ai​ 和 bi​ 的各项之差的绝对 ...

  5. Monkey King(左偏树 可并堆)

    我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...

  6. 左偏树(p3377)

    题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆 ...

  7. 左偏树(Leftist Heap/Tree)简介及代码

    左偏树是一种常用的优先队列(堆)结构.与二叉堆相比,左偏树可以高效的实现两个堆的合并操作. 左偏树实现方便,编程复杂度低,而且有着不俗的效率表现. 它的一个常见应用就是与并查集结合使用.利用并查集确定 ...

  8. luogu【P3377】 【模板】左偏树

    左偏树 顾名思义 向左偏的树 (原题入口) 它有啥子用呢??? 当然是进行堆的合并啦2333普通堆的合并其实是有点慢的(用优先队列的话 只能 一个pop 一个push 来操作 复杂度就是O(n log ...

  9. 『左偏树 Leftist Tree』

    新增一道例题 左偏树 Leftist Tree 这是一个由堆(优先队列)推广而来的神奇数据结构,我们先来了解一下它. 简单的来说,左偏树可以实现一般堆的所有功能,如查询最值,删除堆顶元素,加入新元素等 ...

随机推荐

  1. case设计及验证:入口+页面+展示

    测试个性CB问题, 功能整体结构为:入口+页面+展示 总结: 1. 产品文档为主,其次是服务端接口返回.数据结构及字段值确认.结合实际场景检查是否有遗漏或不合理. 2. 以字段为维度,每个字段的检查点 ...

  2. PHP try catch 如何使用

      <?php   try { if (file_exists('test_try_catch.php')) { require ('test_try_catch.php'); } else { ...

  3. python 简化数据结构的初始化一

    写在前面 在it行业混了差不多十年了,懊悔自己开始写博客的时间太晚.有那么一天就开始写了,也不知道自己会不会坚持写下去.不知不觉日子过到了今天,回头看看,也算是坚持写了.现在写博客的感觉好奇怪,一天天 ...

  4. 015-命令行下载安装brew

    一.brew 1.安装Homebrew 安装命令: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/inst ...

  5. 重装GUI前备份GUI配置

    sap系统要重装, gui配置 想要保存,这个要怎么弄? SAP菜单  选项-> 本地数据 -> 历史记录 里的地址 C:\Users\Administrator\AppData\Roam ...

  6. python使用退格键时出现^H解决方法

    Linux 使用退格键时出现^H解决方法 1.临时解决 按ctrl 2.永久解决 基本现象 进入 Python shell,按下 Delete/Backspace 键: Python 3.5.2 (d ...

  7. 使用 ServiceStack.Text 序列化 json的实现代码

    相信做 .net 开发的朋友经常会遇到 json 序列化这样的需要,今天发篇文章总结下自己使用ServiceStack.Text 来序列化 json.它的速度比 Newtonsoft.Json 快很多 ...

  8. 【Leetcode_easy】783. Minimum Distance Between BST Nodes

    problem 783. Minimum Distance Between BST Nodes 参考 1. Leetcode_easy_783. Minimum Distance Between BS ...

  9. iOS-UIAlertView与UIActionSheet

    UIAlertView与UIActionSheet 6.11.1 常规调用 UIAlertView:调出一个模态对话框,屏幕居中显示 UIActionSheet:非模态对话框,屏幕下方弹出 Alert ...

  10. 超详细的RNN代码实现(tensorflow)

    一.学习单步的RNN:RNNCell 如果要学习TensorFlow中的RNN,第一站应该就是去了解“RNNCell”,它是TensorFlow中实现RNN的基本单元,每个RNNCell都有一个cal ...