树是什么
树是由结点和边组成的,不存在环的一种数据结构。树满足递归定义的特性。也就是说,如果一个数据结构是树结构,那么剔除掉根结点后,得到的若干个子结构也是树,通常称作子树。
二叉树是什么
在二叉树中,每个结点最多有两个分支,即每个结点最多有两个子结点,分别称作左子结点和右子结点
- 满二叉树:定义为除了叶子结点外,所有结点都有2个子结点
- 完全二叉树:定义为除了最后一层以外,其他层的结点个数都达到最大,并且最后一层的叶子结点都靠左排列
二叉树时间复杂度
- 遍历
O(n)
- 增删
O(1)
- 查询
O(1)
树的存储结构
-
基于指针的链式存储:像链表一样,每个结点有三个字段,一个存储数据,另外两个分别存放指向左右子结点的指针
-
基于数组的顺序存储:按照规律把结点存放在数组里
完全二叉树为什么必须结点都靠左排列?
完全二叉树,是从存储空间利用效率的视角来看的。对于一棵完全二叉树而言,仅仅浪费了下标为 0 的存储位置。而如果是一棵非完全二叉树,则会浪费大量的存储空间
二叉搜索树
- 在二叉查找树中的任意一个结点,其左子树中的每个结点的值,都要小于这个结点的值
- 在二叉查找树中的任意一个结点,其右子树中每个结点的值,都要大于这个结点的值
- 在二叉查找树中,会尽可能规避两个结点数值相等的情况
- 对二叉查找树进行中序遍历,就可以输出一个从小到大的有序数据队列
二叉搜索树时间复杂度
- 查询的时间复杂度为
O(logn)
- 插入时间复杂度为
O(logn)
,但这并不意味着它比普通二叉树要复杂。原因在于这里的时间复杂度更多是消耗在了遍历数据去找到查找位置上,真正执行插入动作的时间复杂度仍然是O(1)
二叉搜索树删除结点
- 删除的结点是某个叶子结点,则直接删除,将其父结点指针指向 null 即可
- 删除的结点只有一个子结点,只需要将其父结点指向的子结点的指针换成其子结点的指针即可
- 删除的结点有两个子结点,则有两种可行的操作方式
- 第一种,找到这个结点的左子树中最大的结点,替换要删除的结点
- 第二种,找到这个结点的右子树中最小的结点,替换要删除的结点
树的遍历
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)
}