数据结构-树

本文介绍了树的基本概念,特别是二叉树的类型,如满二叉树和完全二叉树。阐述了二叉搜索树的特点及在查询、插入和删除操作上的时间复杂度。此外,还讨论了树的遍历方法,包括前序、中序、后续和层次遍历。示例代码展示了这些遍历方法的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树是什么

树是由结点和边组成的,不存在环的一种数据结构。树满足递归定义的特性。也就是说,如果一个数据结构是树结构,那么剔除掉根结点后,得到的若干个子结构也是树,通常称作子树。

二叉树是什么

在二叉树中,每个结点最多有两个分支,即每个结点最多有两个子结点,分别称作左子结点和右子结点

  • 满二叉树:定义为除了叶子结点外,所有结点都有2个子结点
  • 完全二叉树:定义为除了最后一层以外,其他层的结点个数都达到最大,并且最后一层的叶子结点都靠左排列

二叉树时间复杂度

  • 遍历 O(n)
  • 增删O(1)
  • 查询O(1)

树的存储结构

  • 基于指针的链式存储:像链表一样,每个结点有三个字段,一个存储数据,另外两个分别存放指向左右子结点的指针

  • 基于数组的顺序存储:按照规律把结点存放在数组里

    完全二叉树为什么必须结点都靠左排列?

    完全二叉树,是从存储空间利用效率的视角来看的。对于一棵完全二叉树而言,仅仅浪费了下标为 0 的存储位置。而如果是一棵非完全二叉树,则会浪费大量的存储空间

二叉搜索树

  • 在二叉查找树中的任意一个结点,其左子树中的每个结点的值,都要小于这个结点的值
  • 在二叉查找树中的任意一个结点,其右子树中每个结点的值,都要大于这个结点的值
  • 在二叉查找树中,会尽可能规避两个结点数值相等的情况
  • 对二叉查找树进行中序遍历,就可以输出一个从小到大的有序数据队列

二叉搜索树时间复杂度

  • 查询的时间复杂度为O(logn)
  • 插入时间复杂度为O(logn),但这并不意味着它比普通二叉树要复杂。原因在于这里的时间复杂度更多是消耗在了遍历数据去找到查找位置上,真正执行插入动作的时间复杂度仍然是 O(1)

二叉搜索树删除结点

  • 删除的结点是某个叶子结点,则直接删除,将其父结点指针指向 null 即可
  • 删除的结点只有一个子结点,只需要将其父结点指向的子结点的指针换成其子结点的指针即可
  • 删除的结点有两个子结点,则有两种可行的操作方式
    1. 第一种,找到这个结点的左子树中最大的结点,替换要删除的结点
    2. 第二种,找到这个结点的右子树中最小的结点,替换要删除的结点

树的遍历

package main

import (
	"fmt"
	"leetcode/common"
)

// 前序遍历
func PreOrderTraverse(node *common.TreeNode) {
	if node == nil {
		return
	}
	fmt.Println(node.Val, " ")
	PreOrderTraverse(node.Left)
	PreOrderTraverse(node.Right)
}

// 中序遍历
func InOrderTraverse(node *common.TreeNode) {
	if node == nil {
		return
	}
	InOrderTraverse(node.Left)
	fmt.Println(node.Val, " ")
	InOrderTraverse(node.Right)
}

// 后序遍历
func PostOrderTraverse(node *common.TreeNode) {
	if node == nil {
		return
	}
	PostOrderTraverse(node.Left)
	PostOrderTraverse(node.Right)
	fmt.Println(node.Val, " ")
}

// 层次遍历
func LevelOrderTraverse(root *common.TreeNode) {
	if root == nil {
		return
	}
	if root.Left == nil || root.Right == nil {
		fmt.Println(root.Val)
		return
	}
	stack := []*common.TreeNode{root}
	for {
		node := stack[0]
		fmt.Println("value", node.Val)
		if node.Left != nil {
			stack = append(stack, node.Left)
		}
		if node.Right != nil {
			stack = append(stack, node.Right)
		}
		stack = stack[1:]
		if len(stack) == 0 {
			break
		}
	}

}

func main() {
	nodeA := common.TreeNode{Val: "A", Left: nil, Right: nil,}
	nodeB := common.TreeNode{Val: "B", Left: nil, Right: nil,}
	nodeC := common.TreeNode{Val: "C", Left: nil, Right: nil,}
	nodeD := common.TreeNode{Val: "D", Left: nil, Right: nil,}
	nodeE := common.TreeNode{Val: "E", Left: nil, Right: nil,}
	nodeF := common.TreeNode{Val: "F", Left: nil, Right: nil,}
	nodeG := common.TreeNode{Val: "G", Left: nil, Right: nil,}

	nodeA.Left = &nodeB
	nodeA.Right = &nodeC

	nodeB.Left = &nodeD
	nodeB.Right = &nodeE

	nodeC.Left = &nodeF
	nodeC.Right = &nodeG

	//PreOrderTraverse(&nodeA)
	//InOrderTraverse(&nodeA)
	//PostOrderTraverse(&nodeA)
	LevelOrderTraverse(&nodeA)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值