09.二叉排序树和平衡二叉树

一、二叉排序树

1.什么是二叉排序树

二叉排序树要么是空二叉树 ,要么具有如下特点:

  • 二叉排序树中,如果其根结点有左子树,那么左子树上所有结点的值都小于根结点的值;
  • 二叉排序树中,如果其根结点有右子树,那么右子树上所有结点的值都大于根结点的值;
  • 二叉排序树的左右子树也要求都是二叉排序树;

如下图就是一个二叉排序树:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VsGloKcV-1642763317481)(C:\Users\86186\AppData\Roaming\Typora\typora-user-images\image-20220117113147858.png)]

2.二叉排序树,方便查找。

再二叉排序树中,进行查找,效率非常高,查找过程如下:

  • 如果相等,查找成功;
  • 如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
  • 如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;

查找的特点:

  1. 如果树的构造,非常平衡的话(注意这个平衡),查找效率为 O ( log ⁡ 2 n ) O(\log_{2}{n}) O(log2n)
  2. 中序遍历二叉排序树时,可以得到有关所有关键字的一个有序的序列
3.二叉排序树,插入操作

再搜索到,位置后,就可以直接插入,插入效率为查找效率。

4.二叉排序树,删除操作

假设要删除的为结点 p,则对于二叉排序树来说,需要根据结点 p 所在不同的位置作不同的操作,有以下 3 种可能:

1、结点 p 为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可;
2、结点 p 只有左子树或者只有右子树,如果 p 是其双亲节点的左孩子,则直接将 p 节点的左子树或右子树作为其双亲节点的左子树;反之也是如此,如果 p 是其双亲节点的右孩子,则直接将 p 节点的左子树或右子树作为其双亲节点的右子树;
3、结点 p 左右子树都有,此时有两种处理方式:

1)令结点 p 的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树,如图 3 所示;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TU3giAgM-1642763317484)(D:\文档\数据结构与算法\image\image-20220117114137245.png)]

2)用结点 p 的直接前驱(或直接后继)来代替结点 p,同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。如图 4 为使用直接前驱代替结点 p:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4yAdjbKN-1642763317485)(D:\文档\数据结构与算法\image\image-20220117114223810.png)]

5.二叉排序树的缺点

二叉树查找的时间复杂度,不是固定的,和构造时,关键字的顺序有很大关系。

最坏的情况,如果关键字都是拍好序的。如12,24,37,45,53,93,这样一个完全有序的集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2794VL8a-1642763317486)(D:\文档\数据结构与算法\image\image-20220117115112614.png)]

二叉树,就会被构造成一条线,这时时间复杂度就会变成,O(n).

有没有,既有二叉树的有点,添加元素时,有一直是平滑的数据结构呢?

二、平衡二叉树

平衡二叉树基于 二叉排序树。

大家知道如果,如果建立查找二叉树,的序列是随机的,二叉树的查找性能会接近 O(logn) ,但是如果,序列是顺序的如从小到大,建立的查找二叉树,就会变成一个所有节点只有右孩子的,链表。查找效率会退化成 O(n), 原因就是这棵树不平衡了。判断一个棵树平衡的标准是什么,如何构建和调整一棵树,使他平衡呢。答案就是 平衡二叉树(AVL)

1.如何判断一棵树是否平衡

(1)平衡因子

平衡因子:是一个数值,每个节点的平衡因子 = 左子树的高度 - 右子树的高度

(2)平衡二叉的标准是什么?

每个节点的平衡因子的绝对值都不超过1

2.如何保证平衡

只是在插入元素,和删除元素时,和二叉排序树不同

(1)插入平衡
  1. 从插入的节点依次往上找他的根节点入栈。找到第一个不平衡的节点结束
  2. 出栈2个节点,第一个不平衡的节点,加上其他出栈节点一共三个节点,排序
  3. 中间的,当做跟节点,小的当做左子树,大的当做右子树,左右子树的孩子不动
  4. 对于没有位置的节点,根据查找二叉树的添加原则添加到树中。
(2)删除平衡
  1. 按照查找二叉树的删除原则,删除节点,并保存真正被删除节点的位置。
  2. 从真正被删除节点,向上找到第一个不平衡的节点
  3. 其他同插入平衡

参考:https://www.bilibili.com/video/BV1Vy4y1M7cD

3.代码实现

avltree.go

package avltree

import (
	"fmt"
	"sort"
)

type AvlTree struct {
	Root *AvlNode
}

type AvlNode struct {
	Depth               int
	Data                int
	Left, Right, Parent *AvlNode
}

func (a *AvlNode) GetBalancer() int {
	if a == nil {
		return 0
	}
	if a.Left.GetDepth()-a.Right.GetDepth() > 0 {
		return a.Left.GetDepth() - a.Right.GetDepth()
	}
	return a.Right.GetDepth() - a.Left.GetDepth()
}

func (a *AvlNode) UpdateDepth() {
	if a == nil {
		return
	}
	max := a.Left.GetDepth()
	if max < a.Right.GetDepth() {
		max = a.Right.GetDepth()
	}
	a.Depth = max + 1
	if a.Parent != nil {
		a.Parent.UpdateDepth()
	}
}

func (a *AvlNode) GetDepth() int {
	if a == nil {
		return 0
	}
	return a.Depth
}

func (a *AvlTree) AddNode(val int) bool {
	// 查找二叉树插入
	node := AvlNode{Depth: 1, Data: val}
	if a.Root == nil {
		a.Root = &node
		return true
	}
	current := a.Root
	for i := 0; i < 100; i++ {
		if current.Data > val {
			if current.Left == nil {
				current.Left = &node
				node.Parent = current
				node.UpdateDepth()
				a.ReBalance(&node)
				return true
			} else {
				current = current.Left
			}
		} else {
			if current.Right == nil {
				current.Right = &node
				node.Parent = current
				node.UpdateDepth()
				a.ReBalance(&node)
				return true
			} else {
				current = current.Right
			}
		}
	}
	return false
}

func (a *AvlTree) ReBalance(no *AvlNode) {
	// 获取不平衡的元素开始,前三个元素。
	current := no
	//s := linear.NewStack()
	//s.Push(current)
	for {
		if current.GetBalancer() > 1 {
			break
		}
		current = current.Parent
		if current == nil {
			return
		}
	}
	var list []*AvlNode
	list = append(list, current)
	for i := 1; i < 3; i++ {
		ele := current.Left
		if ele.GetDepth() < current.Right.GetDepth() {
			ele = current.Right
		}
		list = append(list, ele)
		current = ele
	}

	//记录父元素,和父元素分支
	if list[0] == a.Root {
		a.Root = nil
	}

	parent := list[0].Parent
	isLeft := false
	if parent != nil {
		isLeft = (parent.Left == list[0])
	}

	// 擦除三个元素的相互指向
	for _, v := range list {
		for _, vv := range list {
			if v.Left == vv {
				v.Left = nil
			}
			if v.Right == vv {
				v.Right = nil
			}
			v.Parent = nil
		}
	}
	// 排序
	sort.SliceStable(list, func(i, j int) bool {
		return list[i].Data < list[j].Data
	})

	//记录多余元素
	var appenList []*AvlNode
	if list[1].Left != nil {
		appenList = append(appenList, list[1].Left)
	}
	if list[1].Right != nil {
		appenList = append(appenList, list[1].Right)
	}
	//处理父结点
	list[1].Parent = parent
	if parent != nil {
		if isLeft {
			parent.Left = list[1]
		} else {
			parent.Right = list[1]
		}
	}
	//处理左右结点
	list[1].Left = list[0]
	list[0].Parent = list[1]
	list[1].Right = list[2]
	list[2].Parent = list[1]

	if a.Root == nil {
		a.Root = list[1]
	}

	list[0].UpdateDepth()
	list[2].UpdateDepth()

	for _, v := range appenList {
		a.AddNode(v.Data)
	}

}

func (a *AvlTree) DelNode(ele int) bool {
	node := a.FindNode(ele)

	fmt.Println("find node ", node)
	if node == nil {
		return false
	}
	// 1.没有子节点,直接删除,ReBalance父结点
	if node.Left == nil && node.Right == nil {
		if node.Parent.Left == node {
			node.Parent.Left = nil
		} else {
			node.Parent.Right = nil
		}
		node.Parent.UpdateDepth()
		a.ReBalance(node.Parent)
		node.Parent = nil
		return true
	}
	// 2.没有右子树子树,左子树代替自己原来的位置,ReBalance父结点
	if node.Left != nil && node.Right == nil {
		if node.Parent.Left == node {
			node.Parent.Left = node.Left
		} else {
			node.Parent.Right = node.Left
		}
		node.Parent.UpdateDepth()
		a.ReBalance(node.Parent)
		node.Parent = nil
		return true
	}
	// 3.没有左子树,右子树代替自己原来的位置,ReBalance父结点
	if node.Left == nil && node.Right != nil {
		if node.Parent.Left == node {
			node.Parent.Left = node.Right
		} else {
			node.Parent.Right = node.Right
		}
		node.Parent.UpdateDepth()
		a.ReBalance(node.Parent)
		node.Parent = nil
		return true
	}
	// 4.左右子树都有,左子树的最右结点。代替,自己原来位置,原来右结点,变为null,rebalance 最右结点的父结点。
	mostRightNode := node.Left
	for mostRightNode.Right != nil {
		mostRightNode = mostRightNode.Right
	}
	node.Data = mostRightNode.Data
	mostRightNode.Parent.Right = nil
	mostRightNode.Parent.UpdateDepth()
	a.ReBalance(mostRightNode.Parent)
	mostRightNode.Parent = nil

	return true
}

func (a *AvlTree) FindNode(ele int) *AvlNode {
	current := a.Root
	for i := 0; i < 100; i++ {
		if current.Data == ele {
			return current
		}
		if current.Data > ele {
			if current.Left == nil {
				return nil
			}
			current = current.Left
		} else {
			if current.Right == nil {
				return nil
			}
			current = current.Right
		}
	}
	return nil
}

func (a *AvlTree) PreOrder() {
	a.Root.PreOrder()
}

func (a *AvlNode) PreOrder() {
	if a == nil {
		return
	}
	fmt.Printf(" %+v \n", a)
	a.Left.PreOrder()
	a.Right.PreOrder()
}

avltree_test.go

package avltree

import (
	"fmt"
	"testing"
)

func TestAvlNode(t *testing.T) {
	var tree AvlTree
	tree.AddNode(16)
	tree.AddNode(3)
	tree.AddNode(7)
	tree.AddNode(11)
	tree.AddNode(9)
	tree.AddNode(26)
	tree.AddNode(18)
	tree.AddNode(14)
	tree.AddNode(15)

	tree.PreOrder()

	aa := tree.FindNode(8)
	if aa != nil{
		t.Fail()
	}
	bb := tree.FindNode(11)
	if bb == nil {
		t.Fail()
	}
	if tree.Root.GetBalancer() > 1 {
		t.Fail()
	}

	tree.DelNode(26)
	fmt.Println("===================================")

	tree.PreOrder()

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值