go 的书写很像C
然后以前没有弄明白的事情终于弄明白了
这一次是树的重新学习和深入学习
树的基本方法
- 创建
- 遍历
- 插入
- 删除
- 深度
- 广度
- 查找
树的进阶用法
- B树
- 二叉搜索树
- 红黑树
- 扩展和平衡
- 动态规划
- 贪心算法
- 摊还分析
- 斐波那契堆
基本二叉树
树的基本创建
这个有两种创建方法
第一种是仿照插入的方法
第二种是仿照层序遍历的方法
方法一
插入就是仿照二叉树的查找。
二叉搜索树:
一棵二叉树,可以为空;如果不为空,满足以下性质:
1非空左子树的所有键值小于其根结点的键值。
2.非空右子树的所有键值大于其根结点的键值
3.左、右子树都是二叉搜索树。
- 创建root
- 然后申请一个新的节点
- 先看左边再看右边
- 如果是空的就插入
func CreateTreeInsert[N Number](TestData []N) (root *Node[N]) {
root = new(Node[N])
root.V = TestData[0]
root.left = nil
root.right = nil
var tem = root
for i := 1; i < len(TestData); i++ {
var newNode = new(Node[N])
newNode.V = TestData[i]
newNode.right = nil
newNode.left = nil
tem = root
for {
if TestData[i] > tem.V {
if tem.right != nil {
tem = tem.right
} else if tem.right == nil {
tem.right = newNode
break
}
} else if TestData[i] < tem.V {
if tem.left != nil {
tem = tem.left
} else if tem.left == nil {
tem.left = newNode
break
}
}
}
}
return root
}
方法二
仿照层序遍历的方法
使用队列每一个元素进入后进行层序创建。这样就是一个完全二叉树
(主要插入的时候是先左后右就是。如果先右后左就不是了)
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树
//层序创建
- 申请一个队列
- 创建root
- 将root入队列
- 开始循环处理
- 弹出一个节点
- 申请一个节点
- 先填左边
- 把节点入队列
- 又申请一个节点
- 后填右边
- 把节点放入队列
- 退出是数组越界
func CreateTree[N Number](TestData []N) (root *Node[N]) {
var myQueue = new(queue[N])
myQueue.length = 0
myQueue.Q = make([]*Node[N], 20)
var j = 0
var length = len(TestData)
root = new(Node[N])
root.V = TestData[j]
root.left = nil
root.right = nil
myQueue.In(root)
for {
var tem, ErrorQ = myQueue.Out()
if ErrorQ != nil {
fmt.Printf("%v \n", myQueue)
}
for {
if tem.left == nil {
j = j + 1
if j >= length {
break
}
var newNode = new(Node[N])
newNode.V = TestData[j]
newNode.right = nil
newNode.left = nil
myQueue.In(newNode)
tem.left = newNode
} else if tem.right == nil {
j = j + 1
if j >= length {
break
}
var newNode = new(Node[N])
newNode.V = TestData[j]
newNode.right = nil
newNode.left = nil
myQueue.In(newNode)
tem.right = newNode
} else {
break
}
}
if j >= length {
break
}
}
return root
}
树的遍历
树的遍历是经典数据结构的用法
先序,中序,后序,层序
首先是递归用法
首先明白为什么要有先序中序后序
func PrintTree[N Number](root *Node[N]) {
if root == nil {
return
}
fmt.Printf("%v\n", root.V)//先序
PrintTree(root.left)
fmt.Printf("%v\n", root.V)//中序
PrintTree(root.right)
fmt.Printf("%v\n", root.V)//后序
}
其实是三个输出位置的不一样导致的输出顺序不一样。
然后就是非递归的用法了
为什么要有非递归
因为有时候要去理解原理。自己去实现压栈的原理
首先是
先序遍历非递归
先说结论
先序是压栈的时候的顺序
中序是出栈的时候的顺序
后序是先序的出栈的时候的排序
首先是先序遍历非递归实现
- 申请一个栈和一个队列
- 首先将root压进栈
- 把root入队列
- 开启循环
- 把Node.Lift全部压进栈
- (只要是压栈操作,都要入队列)
- 弹出一个node
- 如果Right节点有
- right节点压入栈
- right节点进队列
- 然后把右节点LIFT全部压栈
- 重复
- 最后在队列的就是完整的先序顺序
这样到栈空的时候退出
就是模拟人栈的情况
先压左边,如果左边没有了
就查看右边。右边有左边又把左边全部压进去
没有了就看右边了。
这样循环压左边
func PushLiftIn[N Number](node *Node[N], stack *Stack[N], Queue *queue[N]) {
for {
if node.left != nil {
stack.Push(node.left)
Queue.In(node.left)
node = node.left
} else if node.left == nil {
break
}
}
}
然后弹出一个元素检查右边有没有
func PopOut[N Number](stack *Stack[N], Queue *queue[N]) (PopError error) {
var tem, ErrorPop = stack.Pop()
if ErrorPop != nil {
fmt.Printf("%v\n", ErrorPop)
return ErrorPop
}
if tem.right != nil {
stack.Push(tem.right)
Queue.In(tem.right)
tem = tem.right
PushLiftIn(tem, stack, Queue)
}
return nil
}
完整的代码
一个栈
一个队列
func (n *Node[Number]) PreorderTraversal() {
// 从左边开始返回输出
//先申请一个栈
var tem = n
var myStack = new(Stack[Number])
myStack.length = 0
myStack.S = make([]*Node[Number], 20)
var myQueue = new(queue[Number])
myQueue.length = 0
myQueue.Q = make([]*Node[Number], 20)
myStack.Push(tem)
myQueue.In(tem)
PushLiftIn(tem, myStack, myQueue)
for {
var PopError = PopOut(myStack, myQueue)
if PopError != nil {
break
}
}
for {
var Node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
break
} else {
fmt.Printf("%v\n", Node.V)
}
}
}
中序遍历非递归
先序遍历和中序遍历唯一的区别
就是入队列的时候
先序是入栈的时候入队列
中序是出栈的时候入队列
func PopInOrderOut[N Number](stack *Stack[N], Queue *queue[N]) (PopError error) {
var tem, ErrorPop = stack.Pop()
if ErrorPop != nil {
fmt.Printf("%v\n", ErrorPop)
return ErrorPop
} else {
Queue.In(tem)
}
if tem.right != nil {
stack.Push(tem.right)
tem = tem.right
PushInOrderLiftIn(tem, stack)
}
return nil
}
只关心弹出的时候入队列
func PushInOrderLiftIn[N Number](node *Node[N], stack *Stack[N]) {
for {
if node.left != nil {
stack.Push(node.left)
node = node.left
} else if node.left == nil {
break
}
}
}
func PopInOrderOut[N Number](stack *Stack[N], Queue *queue[N]) (PopError error) {
var tem, ErrorPop = stack.Pop()
if ErrorPop != nil {
fmt.Printf("%v\n", ErrorPop)
return ErrorPop
} else {
Queue.In(tem)
}
if tem.right != nil {
stack.Push(tem.right)
tem = tem.right
PushInOrderLiftIn(tem, stack)
}
return nil
}
func (n *Node[Number]) InorderTraversal() {
// 从根节点开始返回输出
// 从左边开始返回输出
//先申请一个栈
var tem = n
var myStack = new(Stack[Number])
myStack.length = 0
myStack.S = make([]*Node[Number], 20)
var myQueue = new(queue[Number])
myQueue.length = 0
myQueue.Q = make([]*Node[Number], 20)
myStack.Push(tem)
PushInOrderLiftIn(tem, myStack)
for {
var PopError = PopInOrderOut(myStack, myQueue)
if PopError != nil {
break
}
}
for {
var Node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
break
} else {
fmt.Printf("%v\n", Node.V)
}
}
}
后序遍历非递归
后序遍历是压栈出栈顺序
就是反向的先序遍历后压入栈然后进行弹出
反向先序就是先右后左
func PushRightPost[N Number](node *Node[N], stack *Stack[N], stackTwo *Stack[N]) {
for {
if node.right != nil {
stack.Push(node.right)
stackTwo.Push(node.right)
node = node.right
} else if node.right == nil {
break
}
}
}
func PopOutPost[N Number](stack *Stack[N], stackTwo *Stack[N]) (PopError error) {
var tem, ErrorPop = stack.Pop()
if ErrorPop != nil {
fmt.Printf("%v\n", ErrorPop)
return ErrorPop
}
if tem.left != nil {
stack.Push(tem.left)
stackTwo.Push(tem.left)
tem = tem.left
PushRightPost(tem, stack, stackTwo)
}
return nil
}
然后最后输出另一个栈
func (n *Node[Number]) PostOrderTraversal() {
var tem = n
var myStack = new(Stack[Number])
myStack.length = 0
myStack.S = make([]*Node[Number], 20)
var myStackTwo = new(Stack[Number])
myStackTwo.length = 0
myStackTwo.S = make([]*Node[Number], 20)
myStack.Push(tem)
myStackTwo.Push(tem)
PushRightPost(tem, myStack, myStackTwo)
for {
var PopError = PopOutPost(myStack, myStackTwo)
if PopError != nil {
break
}
}
for {
var Value, ErrorPop = myStackTwo.Pop()
if ErrorPop != nil {
break
} else {
fmt.Printf("%v\n", Value.V)
}
}
}
层序遍历
没有什么好说的。
- 开一个队列
- 先入root
- 每次把左右节点入队列
- 但队列为空的时候退出
func (n *Node[Number]) LevelOrderTraversal() {
// 从右边开始返回输出
var tem = n
var myQueue = new(queue[Number])
myQueue.length = 0
myQueue.Q = make([]*Node[Number], 20)
myQueue.In(tem)
for {
var node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
fmt.Printf("myQueueOne %v\n", ErrorOut)
break
} else {
fmt.Printf("%v\n", node.V)
}
if node.left != nil {
myQueue.In(node.left)
}
if node.right != nil {
myQueue.In(node.right)
}
}
}
树的深度
这里就有两个算法DFs BFS
深度优先
DFS
func maxDepthDepthFirstSearch[N Number](root *Node[N]) int {
//如果root为nil,直接返回0
if root == nil {
return 0
}
//分别统计左节点深度、右节点深度,取最大值最后加上+1
return max(maxDepthDepthFirstSearch(root.left), maxDepthDepthFirstSearch(root.right)) + 1
}
//获取最大值
func max(a, b int) int {
if a > b {
return a
}
return b
}
BFS
利用层序遍历
开两个队列
把下一层的放在另一個队列里面
//层序遍历出来的深度
func (n *Node[Number]) deepBreadthFirstSearch() int {
var tem = n
//开两个队列
var myQueue = new(queue[Number])
myQueue.length = 0
myQueue.Q = make([]*Node[Number], 20)
//先压入root在第一个队列里面
myQueue.In(tem)
var myQueueTwo = new(queue[Number])
myQueueTwo.length = 0
myQueueTwo.Q = make([]*Node[Number], 20)
var depth = 0
for {
var node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
depth = depth + 1
fmt.Printf("myQueueOne %v\n", ErrorOut)
//空了就看第二个。把第二个压进去
for {
var node, ErrorOut = myQueueTwo.Out()
if ErrorOut != nil {
break
}
myQueue.In(node)
}
var tem, _ = myQueue.Out()
if tem != nil {
node = tem
} else {
break
}
}
if node.left != nil {
myQueueTwo.In(node.left)
}
if node.right != nil {
myQueueTwo.In(node.right)
}
}
return depth
}
下面是搜索二叉树
搜索二叉树
搜索二叉树
搜索二叉树
中序排列(左中右),那么会得到一个从小到大的序列
1非空左子树的所有键值小于其根结点的键值。
2.非空右子树的所有键值大于其根结点的键值
3.左、右子树都是二叉搜索树。
当时如果把一个顺序的数组插入进去
搜索二叉树会直接退化为数组,没有查找的意义了
然后出现了平衡二叉树AVL二叉树
AVL
左子树与右子树高度之差的绝对值不超过1
树的每个左子树和右子树都是AVL树
每一个节点都有一个平衡因子(balance factor),任一节点的平衡因子是-1、0、1
(每一个节点的平衡因子 = 右子树高度 - 左子树高度)
AVL节点
(请不要关注把 left 敲成lift 起飞)
value 我设置的是int64 这一次并没有用泛型
type Value int64
type NodeOfAVL struct {
V Value
Lift *NodeOfAVL
Right *NodeOfAVL
Parent *NodeOfAVL
BalanceFactor int //(每一个节点的平衡因子 = 右子树高度 - 左子树高度)
}
队列的基本操作
type queue struct {
length int
Q []*NodeOfAVL
}
func (q *queue) In(value *NodeOfAVL) {
q.Q[q.length] = value
q.length = q.length + 1
}
func (q *queue) Out() (value *NodeOfAVL, err error) {
if q.length != 0 {
value = q.Q[0]
q.Q = q.Q[1:]
q.length = q.length - 1
err = nil
} else {
err = errors.New("this Queue is Empty")
value = nil
}
return value, err
}
AVL基本方法
type AVLTree interface {
create(data []Value)
insert(insertValue Value) (ErrorEqual error)
find(findValue Value) (NodeLocation *NodeOfAVL, ErrorFind error)
LevelOrderTraversal()
deepBreadthFirstSearch() int
SetBalanceFactor()
}
AVL-create 创建
这个准确的来说是搜索二叉树的创建
都是仿照插入的方法。
- 创建一个root
- 如果更小向左边
- 如果更大向右边
func (root *NodeOfAVL) create(data []Value) {
var tem = root
for _, datum := range data {
var newNode = new(NodeOfAVL)
newNode.V = datum
newNode.Lift = nil
newNode.Right = nil
newNode.Parent = nil
newNode.BalanceFactor = 0
tem = root
for {
if datum > tem.V {
if tem.Right != nil {
tem = tem.Right
} else if tem.Right == nil {
newNode.Parent = tem
tem.Right = newNode
break
}
} else if datum < tem.V {
if tem.Lift != nil {
tem = tem.Lift
} else if tem.Lift == nil {
newNode.Parent = tem
tem.Lift = newNode
break
}
}
}
}
}
AVL深度
DFS
func maxDepthDepthFirstSearch(root *NodeOfAVL) int {
//如果root为nil,直接返回0
if root == nil {
return 0
}
//分别统计左节点深度、右节点深度,取最大值最后加上+1
return max(maxDepthDepthFirstSearch(root.Lift), maxDepthDepthFirstSearch(root.Right)) + 1
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
BFS
func (root *NodeOfAVL) deepBreadthFirstSearch() int {
// 从右边开始返回输出
var tem = root
var myQueue = new(queue)
myQueue.length = 0
myQueue.Q = make([]*NodeOfAVL, 20)
myQueue.In(tem)
var myQueueTwo = new(queue)
myQueueTwo.length = 0
myQueueTwo.Q = make([]*NodeOfAVL, 20)
var depth = 0
for {
var node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
depth = depth + 1
fmt.Printf("\n")
for {
var node, ErrorOut = myQueueTwo.Out()
if ErrorOut != nil {
break
}
myQueue.In(node)
}
var tem, _ = myQueue.Out()
if tem != nil {
node = tem
} else {
break
}
}
fmt.Printf("%v ", node.V)
if node.Lift != nil {
myQueueTwo.In(node.Lift)
}
if node.Right != nil {
myQueueTwo.In(node.Right)
}
}
return depth
}
AVL设置平衡因子
每一个节点都有一个平衡因子(balance factor),任一节点的平衡因子是-1、0、1
(每一个节点的平衡因子 = 右子树高度 - 左子树高度)
所以每一个节点弄左边右边
node.BalanceFactor = maxDepthDepthFirstSearch(node.Right) - maxDepthDepthFirstSearch(node.Lift)
用什么遍历都是可以的
func (root *NodeOfAVL) SetBalanceFactor() {
var tem = root
var myQueue = new(queue)
myQueue.length = 0
myQueue.Q = make([]*NodeOfAVL, 20)
myQueue.In(tem)
for {
var node, ErrorOut = myQueue.Out()
if ErrorOut != nil {
fmt.Printf("myQueueOne %v\n", ErrorOut)
break
} else {
node.BalanceFactor = maxDepthDepthFirstSearch(node.Right) - maxDepthDepthFirstSearch(node.Lift)
fmt.Printf("%v Banlence %d\n", node.V, node.BalanceFactor)
}
if node.Lift != nil {
myQueue.In(node.Lift)
}
if node.Right != nil {
myQueue.In(node.Right)
}
}
}
AVL的插入和删除平衡-左旋和右旋
在插入和删除的时候都存在让AVL不在平衡的情况
首先平衡因子只会存在-1 0 1
如果插入一个节点或者删除一个节点后
在删除一个节点或插入一个节点后
更新平衡因子
平衡因子 = 右子树高度 - 左子树高度
如果是左边的树多了 root的平衡因子为 -2
然后看左树的节点
如果是1 左树是右倾斜的
如果是-1 左树是左倾斜的
如果是右边的树多了 root的平衡因子为2
同理
每次动头结点的时候。保证是同样倾斜的就是了
- 旋转
- 看旋转的另一边有节点没有
- 没有就直接旋转
- 有就保存另一边节点
- 变成旋转节点父亲的另一边节点
左旋和右旋
func rotateToTheLeft(root *NodeOfAVL) (newRoot *NodeOfAVL) {
var leftNode = root.Lift
if leftNode.Right != nil {
root.Lift = nil
root.Parent = leftNode
leftNode.Parent = nil
leftNode.Right = root
} else if leftNode.Right == nil {
var leftNodeRight = leftNode.Right
//保存另一边的节点
root.Lift = leftNodeRight
root.Parent = leftNode
leftNodeRight.Parent = root
leftNode.Parent = nil
leftNode.Right = root
}
newRoot = leftNode
return newRoot
}
func rotateToTheRight(root *NodeOfAVL) (newRoot *NodeOfAVL) {
var RightNode = root.Right
if RightNode.Lift == nil {
root.Parent = RightNode
root.Right = nil
RightNode.Parent = nil
RightNode.Lift = root
} else if RightNode.Lift != nil {
var RightNodeLeft = RightNode.Lift
root.Parent = RightNode
root.Lift = RightNodeLeft
RightNodeLeft.Parent=root
RightNode.Parent = nil
RightNode.Lift = root
}
newRoot = RightNode
return newRoot
}
然后就是完整的插入过程了
- tem用来进行插入,左右找
- temtwo用来平衡
- 项目3
func insertWithCheck(root *NodeOfAVL, insertValue Value) (NewRoot *NodeOfAVL, ErrorEqual error) {
var tem = root
var temTwo = root
for {
if insertValue > tem.V {
if tem.Right != nil {
tem = tem.Right
} else if tem.Right == nil {
var newNode = new(NodeOfAVL)
newNode.V = insertValue
newNode.Lift = nil
newNode.Right = nil
newNode.Parent = nil
newNode.BalanceFactor = 0
tem.Right = newNode
newNode.Parent = tem
break
}
} else if insertValue < tem.V {
if tem.Lift != nil {
tem = tem.Lift
} else if tem.Lift == nil {
var newNode = new(NodeOfAVL)
newNode.V = insertValue
newNode.Lift = nil
newNode.Right = nil
newNode.Parent = nil
newNode.BalanceFactor = 0
newNode.Parent = tem
tem.Lift = newNode
break
}
} else if insertValue == tem.V {
ErrorEqual = errors.New("this is no equal value in AVL Tree")
break
}
}
//更新所有节点的平衡因素
temTwo.SetBalanceFactor()
if temTwo.BalanceFactor == -2 {
var leftNode = root.Lift
if leftNode.BalanceFactor == -1 {
//如果是-1这个就是左边节点偏左,直接左旋
NewRoot = rotateToTheLeft(temTwo)
} else if leftNode.BalanceFactor == 1 {
//这个就是左边节点偏右,需要先变成偏左,先这个节点进行右旋,然后再左旋
var newLeftNode = rotateToTheRight(leftNode)
temTwo.Lift = newLeftNode
newLeftNode.Parent = temTwo
NewRoot = rotateToTheLeft(temTwo)
}
} else if temTwo.BalanceFactor == 2 {
var RightNode = root.Right
if RightNode.BalanceFactor == 1 {
//如果是1这个就是右边偏右直接右旋
NewRoot = rotateToTheRight(temTwo)
} else if RightNode.BalanceFactor == -1 {
//如果是-1这个就是右边偏左,先左旋,再右旋
var newRightNode = rotateToTheLeft(RightNode)
temTwo.Right = newRightNode
newRightNode.Parent = temTwo
NewRoot = rotateToTheRight(temTwo)
}
} else {
NewRoot = temTwo
}
return NewRoot, ErrorEqual
}
如果是删除节点
对于非叶子节点的删除,最终都将转化为对叶子节点的删除。
- 中序遍历
- 找到删除节点的前驱或者后驱节点
- 值进行替换
- 重新设置平衡因子
- 进行左右旋转
首先先中序遍历
func PrintTree(root *NodeOfAVL) {
if root == nil {
return
}
PrintTree(root.Lift)
fmt.Printf("%v\n", root.V)
PrintTree(root.Right)
}
然后是找到删除节点的前驱或者后驱节点
就是小一个或者大一个的节点位置
func (root *NodeOfAVL) find(findValue Value) (NodeLocation *NodeOfAVL, ErrorFind error) {
var tem = root
for {
if findValue > tem.V {
if tem.Right != nil {
tem = tem.Right
} else if tem.Right == nil {
ErrorFind = errors.New("this is no findValue")
NodeLocation = nil
break
}
} else if findValue < tem.V {
if tem.Lift != nil {
tem = tem.Lift
} else if tem.Lift == nil {
ErrorFind = errors.New("this is no findValue")
NodeLocation = nil
break
}
} else if findValue == tem.V {
NodeLocation = tem
ErrorFind = nil
break
}
}
return NodeLocation, ErrorFind
}
然后将值进行替换
移除前驱节点的情况或者后驱节点的情况都是在叶子上
比较好处理
然后就是和插入同样的步骤
重新看平衡因子
进行左右旋转