本文所有代码都是go语言编写
基础知识
基本介绍
- root 根
- child 儿子
- parent 父节点
- path 路径 定义为节点n1,n2...nk的一个序列,使得对于1 <= i < k,节点ni是ni+1的父亲。这个路径的长为该路径上的边的条数,即k-1。从每个节点到他自己有一条长为0的路径,在一棵树中从跟到每个节点恰好存在一条路径
- depth 深度 对任一节点ni,ni的深度定义为从根到ni的唯一路径的长。
- height 高 高是从ni到一片树叶的最长路径的长,树的高就是根节点的高。
- degree 度 节点拥有的子树的个数,树的度就是各节点度的最大值
树的实现 第一儿子下一兄弟表示法
type TreeNode struct{
val int
FirstChild *TreeNode
NestSibling *TreeNode
}
A(root)
|
B--->C--->D dep=1
| |
E F---->G dep=2
二叉树
二叉树是一棵树,其中每个节点都不能有多于两个儿子。
-
满二叉树 一颗深度为k且有2^k-1个结点的二叉树称为满二叉树
-
完全二叉树 当且仅当其每一个结点都与深度为k的满二叉树中编号为1至n的结点一一对应时,称为完全二叉树。
二叉树的性质
- 在二叉树的第i层上至多有2^(i-1)个结点
- 深度为k的二叉树上至多有2^(k-1)个结点
- 对任意一颗二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1
- 具有n个结点的完全二叉树的深度为不大于log2 n的最大整数+1
- 求二叉树的最大深度 (DFS)
func maxDepth(root *TreeNode) int {
if root==nil{
return 0
}
l:=maxDepth(root.Left)
r:=maxDepth(root.Right)
if l>r{
return l+1
}else {
return r+1
}
}
- 求二叉树的路径 (同理递归DFS)
func binaryTreePaths(root *TreeNode) []string {
path := make([]string, 0)
if root == nil {
return path
}
var DFS func(root *TreeNode, str string)
DFS = func(root *TreeNode, str string) {
if root == nil {
return
}
str = fmt.Sprintf("%s%d->", str,root.Val)
if root.Left == nil && root.Right == nil {
path = append(path, str[:len(str)-2])
}
DFS(root.Left, str)
DFS(root.Right, str)
}
DFS(root, "")
return path
}
二叉树的存储结构
- 顺序存储结构 通过一个一维数组表示,最好适用于完全二叉树
- 链式存储结构
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
二叉树的遍历
二叉树的先序遍历
先遍历根节点 在遍历左子树 右子树
- 递归解法
时间复杂度:O(n)
空间复杂度:O(n)
func preorderTraversal(root *TreeNode) []int {
ret := make([]int, 0)
if root == nil {
return ret
}
var PT func(root *TreeNode)
PT = func(root *TreeNode) {
ret = append(ret, root.Val)
if root.Left != nil {
PT(root.Left)
}
if root.Right != nil {
PT(root.Right)
}
}
PT(root)
return ret
}
- 迭代解法
通过一个栈,每次将当前节点的右儿子先入栈,随后左儿子入栈,循环遍历时将当前节点出栈放入数组即可。
时间复杂度:O(n)
空间复杂度:O(n)最坏情况
func preorderTraversal2(root *TreeNode) []int {
ret := make([]int, 0)
if root == nil {
return ret
}
stack := []*TreeNode{root}
for len(stack) > 0 {
node := stack[len(stack)-1]
stack = stack[:len(stack)-1]
ret = append(ret, node.Val)
if node.Right != nil {
stack = append(stack, node.Right)
}
if node.Left != nil {
stack = append(stack, node.Left)
}
}
return ret
}
二叉树的中序遍历
先遍历左子树,在遍历根节点,然后遍历右子树。
二叉树的后序遍历
先遍历左子树,在遍历右子树,最后遍历根节点
二叉树的层序遍历(DFS,BFS)
-
迭代实现 通过广度优先遍历,借用队列作为辅助结构
首先将根节点放入队列,不断遍历队列
如果根节点做右子树不为空,根节点出队,将左右子树节点放入队列
我们把每层遍历到的节点都放入到一个结果集中,最后返回这个结果集就可以了。时间复杂度: O(n)
空间复杂度:O(n)
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
func levelOrder(root *TreeNode) [][]int {
ret := [][]int{}
if root == nil {
return ret
}
queue := []*TreeNode{root}
for len(queue) > 0 {
l := len(queue)
ans := make([]int, 0)
for i := 0; i < l; i++ {
node := queue[i]
ans = append(ans, node.Val)
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
ret = append(ret, ans)
queue = queue[l:]
}
return ret
}
-
递归实现
按照深搜的方法,每次递归代入一个index表示当前层数,当遍历到一个新的深度而最终结果 res 中还没有创建当前对应的列表时,应该在 res 中新建一个列表用来保存该层的所有节点。
时间复杂度: O(n)
空间复杂度: O(h) h为二叉树的高度
func levelOrderDFS(root *TreeNode) [][]int {
ret := [][]int{}
if root == nil {
return ret
}
var SerchRoot func(root *TreeNode, index int)
SerchRoot = func(root *TreeNode, index int) {
if root == nil {
return
}
if len(ret) == index {
ret = append(ret, []int{})
}
ret[index] = append(ret[index], root.Val)
SerchRoot(root.Left, index+1)
SerchRoot(root.Right, index+1)
}
SerchRoot(root, 0)
return ret
}
二叉树的展开(单链表)
方法
- 先序遍历 然后通过获得的顺序进行展开
- 先序遍历和展开同时进行
func flatten(root *TreeNode) {
if root == nil {
return
}
stack := []*TreeNode{root}
var prev *TreeNode
for len(stack) > 0 {
curr := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if prev != nil {
prev.Left, prev.Right = nil, curr
}
left, right := curr.Left, curr.Right
if right != nil {
stack = append(stack, right)
}
if left != nil {
stack = append(stack, left)
}
prev = curr
}
}
- 寻找前驱节点 按照前序遍历的顺序,左子树的最右边节点一定是在当前节点右子树的根节点之前
【1】
【2】 【5】
【3】 【4】 【6】
例如 当前根节点的左儿子节点存在,那么左子树的最右节点4一定在5之前,4就称为5的前驱节点。
对于当前节点,如果其左子节点不为空,则在其左子树中找到最右边的节点,作为前驱节点,将当前节点的右子节点赋给前驱节点的右子节点,然后将当前节点的左子节点赋给当前节点的右子节点,并将当前节点的左子节点设为空。对当前节点处理结束后,继续处理链表中的下一个节点,直到所有节点都处理结束。
func flatten3(root *TreeNode) {
if root == nil {
return
}
curr := root
for curr != nil {
if curr.Left != nil {
next := curr.Left
per := next
for per.Right != nil {
per = per.Right
}
per.Right = curr.Right
curr.Left = nil
curr.Right = next
}
curr = curr.Right
}
}
时间复杂度:O(n)
空间复杂度:O(1)