二叉树

  • 线性表:1:1
  • 树:1:N
    • 根节点:只有后继,没有前驱
    • 子结点: 既有前驱,又有后继
    • 叶子结点:只有前驱,没有后继

树的定义:

  1. 互不相交的有限结点集合
  2. 只有一个根结点

树的特性:

  1. 只有一个前驱,可以有多个后继
  2. 树中可以有树(子树)
  3. 树可以为空,结点0

相关概念:

  • 结点的度:
    • 结点的直接后继个数
  • 树的度:
    • 先求每个结点分支数, 这些数中取max, 为“树”的度。
  • 树的深度(高度):
    • 树的层数。

二叉树:

  1. 每个结点最多两颗子树,即结点的度,不能大于 2 。可以0、1。
  2. 左子树、右子树 不能颠倒。(有序)

满二叉树:

  • 每个结点都有 左子结点、右子结点 的 二叉树。【要求会画】

完全二叉树:

  • 除最后一层外,每一层的结点数都达到最大值。(左子、右子都不缺)
  • “满二叉树” 是“完全二叉树”的特例!

二叉树数据存储结构:

在这里插入图片描述

将一颗多叉树转化为一颗二叉树——左子右兄法:

  1. 兄弟连:将同层所有兄弟连成一条线 (2 兄弟的也连)
  2. 右子断:将所有右儿子,与父亲的边断掉。
  3. 45°转:将刚才连成的那条线,顺时针旋转 45°

【结论】:“兄弟”被线连起来的,都在它们的大哥右子树中,且根结点一定没有右儿子,因为根没有兄弟。

转化之前:

在这里插入图片描述
转化之后:

在这里插入图片描述

二叉树操作:

方法列表:

  1. 创建二叉树 Create()
  2. 二叉树的遍历:大前提:先左后右
    • 先序遍历:根、左、右 PreOrder()
    • 中序遍历:左、根、右 MideOrder()
    • 后序遍历:左、右、根 PostOrder()
  3. 获取二叉树深度(高度):TreeHeight()
  4. 获取二叉树叶子结点个数:LeafNum()
  5. 二叉树的数据查找 Search()
  6. 销毁二叉树 Destroy()
  7. 二叉树的翻转 Reverse()
  8. 二叉树的拷贝 Copy()

创建二叉树 Create()

  1. 依据 图 创建二叉树的 各个结点。 并且初始化
  2. 根据图,组织结点和结点之间的层次关系。
// 创建二叉树 -- 根据图
func (node *BinTreeNode) Create() {

	// 创建二叉树子节点
	node1 := BinTreeNode{1, nil, nil}
	node2 := BinTreeNode{2, nil, nil}
	node3 := BinTreeNode{3, nil, nil}
	node4 := BinTreeNode{4, nil, nil}
	node5 := BinTreeNode{5, nil, nil}
	node6 := BinTreeNode{6, nil, nil}
	node7 := BinTreeNode{7, nil, nil}

	node.Data = 0
	node.Lchild = &node1
	node.Rchild = &node2
	node1.Lchild = &node3
	node1.Rchild = &node4
	node2.Lchild = &node5
	node2.Rchild = &node6
	node3.Lchild = &node7
}

遍历二叉树的方法

先序遍历
  • DLR:先中,再左, 再右。
    • 先中: 打印数据
    • 再左: 左子树递归调用本函数
    • 再右: 右子树递归调用本函数
  • 先打印 根结点, 判断左子树是否二叉树,如果是,递归打印该二叉树的 根, 再判断左子树是否是二叉树。。。。。一直到 左子树已经是一个叶子结点。逐层返回。
// 打印二叉树 -- 先序遍历:DLR : 先中,再左,再右
func (node *BinTreeNode) PreOrder()  {
	if node == nil {		// 递归出口
		return
	}
	// 先中,先打印Data数据
	fmt.Print(node.Data, " ")

	// 再左,左子树递归调用本函数
	node.Lchild.PreOrder()

	// 再右,右子树递归调用本函数
	node.Rchild.PreOrder()
}
中序遍历
  • LDR: 先左, 再中,再右
    • 先左: 左子树递归调用本函数
    • 再中: 打印数据
    • 再右:右子树递归调用本函数
  • 先判断左子树是否可以递归进入, 如果可以递归进入,递归到叶子结点,打印左子树的叶子结点。打印中间数据,再打印右子树。
// 打印二叉树 -- 中序遍历:LDR : 先左,再中,再右
func (node *BinTreeNode) MidOrder() {
	if node == nil {		// 递归出口
		return
	}
	// 先左, 左子树递归调用本数据
	node.Lchild.MidOrder()

	// 再中, 打印Data数据
	fmt.Print(node.Data, " ")

	// 再右, 右子树递归调用本函数
	node.Rchild.MidOrder()
}
后序遍历
  • LRD:先左, 再右, 再中
    • 先左: 左子树递归调用本函数
    • 再右:右子树递归调用本函数
    • 再中:打印数据
  • 先判断左子树是否可以递归进入, 递归到叶子结点,打印,判断右子树是否可以递归进入, 递归到叶子结点打印,最后再打印中间数据。逐层向上返回。
// 打印二叉树 -- 后序遍历:LRD: 先左,再右,再中
func (node *BinTreeNode) PostOrder() {
	if node == nil {
		return
	}
	// 先左, 左子树递归调用本函数
	node.Lchild.PostOrder()

	// 再右, 右子树递归调用本函数
	node.Rchild.PostOrder()

	// 再中, 打印data数据
	fmt.Print(node.Data, " ")
}

求取二叉树高度TreeHeight()

  1. 容错。 递归出口,不能返回 -1.
  2. 左子树、右子树各自递归进入。保存各自的返回值
  3. 比较 左子树的返回值和 右子树的返回值。最终返回比较结果的大值。
// 获取二叉树的树高(深度)
func (node *BinTreeNode) TreeHeight() int  {
	if node == nil {		// 递归出口
		return 0			// 不能返回 -1
	}
	// 左子树,递归进入
	lh := node.Lchild.TreeHeight()

	// 右子树,递归进入
	rh := node.Rchild.TreeHeight()

	// 累加,比较左子树和右子树 高度.
	if lh > rh {
		lh++
		return lh
	} else {
		rh++
		return rh
	}
}

获取二叉树的叶子结点个数 LeafNum()

  • 使用指针传参

    1. 容错
    2. 判断结点的左子树 和 右子树同时为 nil 表示找到叶子结点。将 指针参数 ++
    3. 左子树递归进入
    4. 右子树递归进入
    // 获取二叉树的叶子结点数 --- 指针传参
    func (node *BinTreeNode) LeafNum(num *int) {
    	if node == nil {
    		return
    	}
    	// 判断是否为叶子结点. 左子树和右子树均为 nil
    	if node.Lchild == nil && node.Rchild == nil {
    		(*num)++
    	}
    
    	// 左子树/右子树 各自递归进入
    	node.Lchild.LeafNum(num)
    	node.Rchild.LeafNum(num)
    }
    
  • 使用全局变量

    1. 容错
    2. 判断结点的左子树 和 右子树同时为 nil 表示找到叶子结点。将 全局变量 ++
    3. 左子树递归进入
    4. 右子树递归进入
    // 获取二叉树的叶子结点数 --- 全局变量
    var num2 = 0
    func (node *BinTreeNode) LeafNum2() {
    	if node == nil {
    		return
    	}
    	if node.Lchild == nil && node.Rchild == nil {
    		num2++
    	}
    	// 左子树/右子树 各自递归进入
    	node.Lchild.LeafNum2()
    	node.Rchild.LeafNum2()
    }
    

查找二叉树数据

  1. 容错
  2. 比较node.Data 和传递的数据值 类型、数值是否一致。
    1. reflect.TypeOf()
    2. reflect.DeepEqual()
  3. 左子树递归进入
  4. 右子树递归进入
var bl = false
func (node *BinTreeNode) Search(Data interface{}) {
	if node == nil {
		return
	}
	// 比较数据值是否相同,同时数据类型一致
	if reflect.TypeOf(node.Data) == reflect.TypeOf(Data) && reflect.DeepEqual(node.Data, Data) {
		fmt.Println("找到数据:", node.Data)
		bl = true
		return
	}
	// 左子树和右子树各自递归进入
	node.Lchild.Search(Data)
	node.Rchild.Search(Data)
}

二叉树翻转

  1. 容错
  2. 利用多重赋值,交互左右子树
  3. 左子树递归进入
  4. 右子树递归进入
// 二叉树翻转
func (node *BinTreeNode) Reverse() {
	if node == nil {
		return
	}
	// 左子树和右子树交换. 依赖 Go语言特有的 多重赋值
	node.Lchild, node.Rchild = node.Rchild, node.Lchild

	node.Lchild.Reverse()
	node.Rchild.Reverse()
}

二叉树拷贝Copy()

  1. 容错
  2. 左子树递归调用copy, 保存返回值,作为左子结点数据
  3. 右子树递归调用copy, 保存返回值,作为右子结点数据
  4. 创建新结点, 初始化
    1. Data 域 使用当前结点 的 data赋值。 node.Data
    2. lChild 域 使用 上左子递归返回值 赋值。
    3. RChild 域 使用上 右子递归返回值 赋值。
  5. 返回 新结点(新树的根结点。)
// 拷贝二叉树
func (node *BinTreeNode) Copy() *BinTreeNode {
	if node == nil {
		return nil
	}
	// 左子树递归进入
	lChild := node.Lchild.Copy()
	// 右子树递归进入
	rChild := node.Rchild.Copy()

	// 创建新结点, 赋值
	newNode := new(BinTreeNode)
	newNode.Data = node.Data
	newNode.Lchild = lChild
	newNode.Rchild = rChild

	return newNode
}

的根结点。)

// 拷贝二叉树
func (node *BinTreeNode) Copy() *BinTreeNode {
	if node == nil {
		return nil
	}
	// 左子树递归进入
	lChild := node.Lchild.Copy()
	// 右子树递归进入
	rChild := node.Rchild.Copy()

	// 创建新结点, 赋值
	newNode := new(BinTreeNode)
	newNode.Data = node.Data
	newNode.Lchild = lChild
	newNode.Rchild = rChild

	return newNode
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值