面试经典算法:优先队列,最大堆,堆排序,左偏树Golang实现
堆排序
使用优先队列-最小/最大堆可实现。
优先队列
优先队列是一种能完成以下任务的队列:插入一个数值,取出最小的数值(获取数值,并且删除)。优先队列可以用二叉树来实现,我们称这种为二叉堆。
最小堆
最小堆是二叉堆的一种,是一颗完全二叉树(一种平衡树), 其特点是父节点的键值总是小于或者等于子节点。
实现细节(两个操作):
push:向堆中插入数据时,首先在堆的末尾插入数据,然后不断向上提升,直到没有大小颠倒时。
pop:从堆中删除最小值时首先把最后一个值复制到根节点上,并且删除最后一个数值。然后不断向下交换, 直到没有大小颠倒为止。在向下交换过程中,如果有两个子儿子都小于自己,就选择较小的
插入时间复杂度O(logN),删除时间复杂度O(logN),两个二叉堆合并时间复杂性O(NlogN).
最大堆同理。可用此结构实现堆排序算法。
/*
最小堆
*/
package main
import "fmt"
type Heap struct {
Size int
Elems []int
}
func NewHeap(MaxSize int) *Heap {
h := new(Heap)
h.Elems = make([]int, MaxSize, MaxSize)
return h
}
func (h *Heap) Push(x int) {
h.Size++
// i是要插入节点的下标
i := h.Size
for {
if i <= 0 {
break
}
// parent为父亲节点的下标
parent := (i - 1) / 2
// 如果父亲节点小于等于插入的值,则说明大小没有跌倒,可以退出
if h.Elems[parent] <= x {
break
}
// 互换当前父亲节点与要插入的值
h.Elems[i] = h.Elems[parent]
i = parent
}
h.Elems[i] = x
}
func (h *Heap) Pop() int {
if h.Size == 0 {
return 0
}
// 取出根节点
ret := h.Elems[0]
// 将最后一个节点的值提到根节点上
h.Size--
x := h.Elems[h.Size]
i := 0
for {
// a,b为左右两个子节点的下标
a := 2*i + 1
b := 2*i + 2
// 没有左子树
if a >= h.Size {
break
}
// 有右子树,找两个子节点中较小的值
if b < h.Size && h.Elems[b] < h.Elems[a] {
a = b
}
// 父亲小直接退出
if h.Elems[a] >= x {
break
}
// 交换
h.Elems[i] = h.Elems[a]
i = a
}
h.Elems[i] = x
return ret
}
func (h *Heap) Display() {
fmt.Printf("Size:%d,Elems:%#v\n", h.Size, h.Elems[0:h.Size])
}
func main() {
h := NewHeap(100)
h.Display()
h.Push(3)
h.Push(6)
h.Push(7)
h.Push(27)
h.Push(1)
h.Push(2)
h.Push(3)
h.Display()
fmt.Println(h.Pop())
h.Display()
fmt.Println(h.Pop())
h.Display()
fmt.Println(h.Pop())
h.Display()
fmt.Println(h.Pop())
h.Display()
fmt.Println(h.Pop())
h.Display()
}
左偏树
最小堆/最大堆如果两个堆进行合并,时间复杂度较高,左偏树是可合并的二叉堆,首先满足所有的堆的性质,其外,各种操作时间复杂度都是O(logN)。
左偏树的树节点需要保存的信息有:
1.左右子树节点编号
2.此节点到有空子结点的最短距离len(空子节点的节点,就是子节点数不足2个的节点)
3.自身权值
左偏就是每个节点的左子节点的len不小于右子节点的len(但并不代表左子节点数一定不小于右子节点数),那么可知左偏树中一个节点的距离就是右儿子距离+1(或没有右儿子),且左右子树都是左偏树。
合并树A和树B的操作方法如下:
1.如果A或B有一个是空树,返回另一个。
2.如果A的优先级比B低,交换A,B。(确保左堆根节点小于右堆根节点)
3.递归处理,将B和A的右子树合并。(B,Right(A)递归处理)
4.如果合并过后A的右儿子距离大于A的左儿子,交换A的左右儿子。(确保左儿子距离大于右儿子)
5.更新A的距离。
左偏树合并操作合并的是两棵左偏树,对于堆的插入,就是合并一棵树和一个节点,对于堆的删除,就是合并根的两棵子树。
/*
左偏树
*/
package main
import (
"fmt"
)
type LeftistHeap struct {
Root *Node
}
type Node struct {
Data int
Distance int
LeftChild *Node
RightChild *Node
}
func New() *LeftistHeap {
h := new(LeftistHeap)
return h
}
func (n *Node) Dist() int {
if n == nil {
return -1 // 空节点距离为-1
}
return n.Distance
}
func (h *LeftistHeap) Push(data int) {
newNode := new(Node)
newNode.Data = data
h.Root = h.Root.Merge(newNode)
}
func (h *LeftistHeap) Pop() int {
if h.Root == nil {
return -1 // pop完
}
data := h.Root.Data
h.Root = h.Root.LeftChild.Merge(h.Root.RightChild)
return data
}
// 合并两棵左偏树
func (A *Node) Merge(B *Node) *Node {
// 一棵树为空返回另外一棵树
if A == nil {
return B
}
if B == nil {
return A
}
leftHeap := A
rightHeap := B
// 使左堆做为合并后的根节点
if A.Data > B.Data {
leftHeap = B
rightHeap = A
}
// 递归:左堆的右子树和右堆进行合并,作为左堆右子树
leftHeap.RightChild = leftHeap.RightChild.Merge(rightHeap)
// 树翻转左右,确保左儿子距离大于右子
if leftHeap.RightChild.Dist() > leftHeap.LeftChild.Dist() {
leftHeap.LeftChild, leftHeap.RightChild = leftHeap.RightChild, leftHeap.LeftChild
}
if leftHeap.RightChild == nil {
leftHeap.Distance = 0
} else {
leftHeap.Distance = leftHeap.RightChild.Dist() + 1
}
return leftHeap
}
// 递归先序排序
func (n *Node) Display() {
if n == nil {
fmt.Println("null")
return
}
fmt.Println(n.Data)
fmt.Printf("Node:%d,Left child:", n.Data)
if n.LeftChild != nil {
n.LeftChild.Display()
} else {
fmt.Print("null")
}
fmt.Println()
fmt.Printf("Node:%d,Right child:", n.Data)
if n.RightChild != nil {
n.RightChild.Display()
} else {
fmt.Print("null")
}
fmt.Println()
}
func (h *LeftistHeap) Display() {
h.Root.Display()
}
func main() {
n := New()
n.Display()
fmt.Println("---")
n.Push(3)
n.Push(1)
n.Push(5)
n.Push(8)
n.Display()
fmt.Println(n.Pop())
fmt.Println(n.Pop())
fmt.Println(n.Pop())
fmt.Println(n.Pop())
fmt.Println(n.Pop())
fmt.Println(n.Pop())
}
转载请注明:http://www.lenggirl.com/algorithm/heap.html
面试经典算法:优先队列,最大堆,堆排序,左偏树Golang实现的更多相关文章
- HDU 1512 Monkey King(左偏树+并查集)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1512 [题目大意] 现在有 一群互不认识的猴子,每个猴子有一个能力值,每次选择两个猴子,挑出他们所 ...
- 黄源河《左偏树的应用》——数字序列(Baltic 2004)
这道题哪里都找不到. [问题描述] 给定一个整数序列a1, a2, … , an,求一个不下降序列b1 ≤ b2 ≤ … ≤ bn,使得数列{ai}和{bi}的各项之差的绝对值之和 |a1 - b1| ...
- 【BZOJ 1367】 1367: [Baltic2004]sequence (可并堆-左偏树)
1367: [Baltic2004]sequence Description Input Output 一个整数R Sample Input 7 9 4 8 20 14 15 18 Sample Ou ...
- 洛谷P4331 [BOI2004] Sequence 数字序列 [左偏树]
题目传送门 数字序列 题目描述 给定一个整数序列 a1,a2,⋅⋅⋅,an ,求出一个递增序列 b1<b2<⋅⋅⋅<bn ,使得序列 ai 和 bi 的各项之差的绝对 ...
- Monkey King(左偏树 可并堆)
我们知道如果要我们给一个序列排序,按照某种大小顺序关系,我们很容易想到优先队列,的确很方便,但是优先队列也有解决不了的问题,当题目要求你把两个优先队列合并的时候,这就实现不了了 优先队列只有插入 删除 ...
- 左偏树(p3377)
题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆 ...
- 左偏树(Leftist Heap/Tree)简介及代码
左偏树是一种常用的优先队列(堆)结构.与二叉堆相比,左偏树可以高效的实现两个堆的合并操作. 左偏树实现方便,编程复杂度低,而且有着不俗的效率表现. 它的一个常见应用就是与并查集结合使用.利用并查集确定 ...
- luogu【P3377】 【模板】左偏树
左偏树 顾名思义 向左偏的树 (原题入口) 它有啥子用呢??? 当然是进行堆的合并啦2333普通堆的合并其实是有点慢的(用优先队列的话 只能 一个pop 一个push 来操作 复杂度就是O(n log ...
- 『左偏树 Leftist Tree』
新增一道例题 左偏树 Leftist Tree 这是一个由堆(优先队列)推广而来的神奇数据结构,我们先来了解一下它. 简单的来说,左偏树可以实现一般堆的所有功能,如查询最值,删除堆顶元素,加入新元素等 ...
随机推荐
- oracle之按表名查询表字段结构
工作中查看oracle表结构, 1,pl/sql或其他开发工具可以输入表名然后ctr+点击表名就可以看见表结构: 2,表字段过多,如果给第三方截图看比较麻烦,得截好几次,容易看眼花,可以查询如下sql ...
- OpenResty之 limit.count 模块
原文: lua-resty-limit-traffic/lib/resty/limit/count.md 1. 示例 http { lua_shared_dict my_limit_count_sto ...
- (转)MitmProxy+APPnium安装使用
MitmProxy+APPnium安装使用 2019年08月19日 11:09:48 jiageibuuuyi 阅读数 61更多 分类专栏: python学习笔记 版权声明:本文为博主原创文章,遵 ...
- HearthBuddy 调试肯瑞托法师寒冰屏障的配合
35疯狂的科学家 63肯瑞托法师 13过期货物专卖商 64对面的英雄术士 比较好的出牌策略是,肯瑞托法师+寒冰屏障 ailoop1 startEnemyTurnSimThread1start prin ...
- 有关Python的import...和from...import...的区别
1.语法分析:首先from A import a1 是从A模块导入a1工具(可以是某个 函数,全局变量,类),import A是导入整个A模块的全部内容(包括全部的函数,全局变量,类). 2.内存分析 ...
- 骑行川藏--新都桥&塔公草原
新都桥 塔公草原 新都桥,位于四川省甘孜藏族自治州康定市西部地区,距市区81公里: 别名:东俄罗,一个镇名.海拔约3300米,没有突出的标志性景观,沿线有10余公里被称为“摄影家走廊”. 神奇光线,无 ...
- Angular 开发工具介绍
1.Webstorm2.Visual Studio Code (推荐) 记得 安装angular插件
- web前端之es6对象的扩展
1.属性的简洁表示法 2.属性名表达式 表达式作为对象的属性名 3.方法的 name 属性 例如:函数的name 属性,返回函数名. 4.Object.is() ES 比较两个值是否相等,只有两个运算 ...
- 自定义string类
#include <iostream> #include <cstring> using namespace std; class String; class Data{ // ...
- Linux -- Proactor(及其与Reactor的比较)
高并发服务器常由多线程+IO复用服务器(one event loop per thread) 两种I/O多路复用模式:Reactor和Proactor 一般地,I/O多路复用机制都依赖于一个事件多路分 ...