.什么是二叉树?
树的数据结构就像图中的树一样,这里面每个元素称为结点, 结点直接的关系是“父子关系”
比如下面这幅图,A 节点就是 B 节点的父节点,B 节点是 A 节点的子节点。B、C、D 这三个节点的父节点是同一个节点,所以它们之间互称为兄弟节点。我们把没有父节点的节点叫做根节点,也就是图中的节点 E。我们把没有子节点的节点叫做叶子节点或者叶节点,比如图中的 G、H、I、J、K、L 都是叶子节点
关于“树”,还有三个比较相似的概念:高度(Height)、深度(Depth)、层(Level)。它们的定义是这样的
高度是从下往上,从0开始计数。深度是从上往下,也是从0开始计数。
“层数”跟深度的计算类似,不过,计数起点是 1,也就是说根节点位于第 1 层。
二叉树(Binary Tree),满二叉树,完全二叉树,平衡二叉树,二叉查找树
划重点了,数组二叉树的存储方式:
1)节点X存储在数组中下标为i的位置,
2)则其左子节点下标为2×i,
3)右子节点下标为2×i+1,
4)父节点为i/2
二叉树的遍历
前序遍历是指,对于树中的任意节点来说,先打印这个节点,然后再打印它的左子树,最后打印它的右子树。
中序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它本身,最后打印它的右子树。
后序遍历是指,对于树中的任意节点来说,先打印它的左子树,然后再打印它的右子树,最后打印这个节点本身。
实际上,二叉树的前、中、后序遍历就是一个递归的过程。
递归非常重要的一点就是要写出推导公式!!!
void preOrder(Node* root) {
if (root == null) return;
print root // 此处为伪代码,表示打印root节点
preOrder(root->left);
preOrder(root->right);
}
void inOrder(Node* root) {
if (root == null) return;
inOrder(root->left);
print root // 此处为伪代码,表示打印root节点
inOrder(root->right);
}
void postOrder(Node* root) {
if (root == null) return;
postOrder(root->left);
postOrder(root->right);
print root // 此处为伪代码,表示打印root节点
}
二叉树遍历的时间复杂度是 O(n)。
这里帮助大家确定下来递归算法的三个要素。每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!
-
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
-
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
-
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
练习题目:
144. 二叉树前序遍历
func preorderTraversal(root *TreeNode) []int {
vals := []int{}
if root == nil {
return vals
}
var preorder func(node *TreeNode)
preorder = func(node *TreeNode){
//返回条件
if node == nil { //当前node为空,就return到上一层
return
}
fmt.Println(node)
vals = append(vals,node.Val) //node的值存储到vals
preorder(node.Left) //递归遍历左结点
preorder(node.Right) //递归遍历右结点
}
preorder(root)
return vals
}
145. 二叉树的后序遍历
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func postorderTraversal(root *TreeNode) []int{
vals := []int{}
if root == nil {
return vals
}
var postorder func(node *TreeNode)
postorder = func(node *TreeNode){
if node == nil {
return
}
postorder(node.Left) //递归遍历左结点
postorder(node.Right) //递归遍历右结点
vals = append(vals,node.Val)
}
postorder(root)
return vals
}
94.二叉树的中序遍历
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func inorderTraversal(root *TreeNode) []int {
vals := []int{}
if root == nil {
return vals
}
var inorder func(node *TreeNode)
inorder = func(node *TreeNode){
if node == nil {
return
}
inorder(node.Left)
vals = append(vals,node.Val)
inorder(node.Right)
}
inorder(root)
return vals
}
二叉树的迭代遍历
前序遍历(迭代法)
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。
func preorderTraversal(root *TreeNode) []int {
vals := []int{}
if root == nil {
return vals
}
stack := list.New()
stack.PushBack(root)
for stack.Len() > 0 {
node:=stack.Remove(stack.Back()).(*TreeNode)
vals = append(vals,node.Val)
if node.Right != nil {
stack.PushBack(node.Right)
}
if node.Left != nil{
stack.PushBack(node.Left)
}
}
return vals
}
中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点
func inorderTraversal(root *TreeNode) []int {
ans := []int{}
if root == nil {
return ans
}
st := list.New()
cur := root
for cur != nil || st.Len() > 0 {
if cur != nil {
st.PushBack(cur)
cur = cur.Left
} else {
cur = st.Remove(st.Back()).(*TreeNode)
ans = append(ans, cur.Val)
cur = cur.Right
}
}
return ans
}
后序遍历(迭代法)
后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序
func postorderTraversal(root *TreeNode) []int {
ans := []int{}
if root == nil {
return ans
}
st := list.New()
st.PushBack(root)
for st.Len() > 0 {
node := st.Remove(st.Back()).(*TreeNode)
ans = append(ans, node.Val)
if node.Left != nil {
st.PushBack(node.Left)
}
if node.Right != nil {
st.PushBack(node.Right)
}
}
reverse(ans)
return ans
}
func reverse(a []int) {
l, r := 0, len(a) - 1
for l < r {
a[l], a[r] = a[r], a[l]
l, r = l+1, r-1
}
}
二叉树的统一迭代法
前序遍历
/**
type Element struct {
// 元素保管的值
Value interface{}
// 内含隐藏或非导出字段
}
func (l *List) Back() *Element
前序遍历:中左右
压栈顺序:右左中
**/
func preorderTraversal(root *TreeNode) []int {
vals := []int{}
if root == nil {
return vals
}
stack := list.New()
stack.PushBack(root)
var node *TreeNode
for stack.Len() > 0 {
e:=stack.Back()
stack.Remove(e) //弹出元素
if e.Value == nil { //如果为空,则表明是需要处理中间节点
e=stack.Back()
stack.Remove(e)
node=e.Value.(*TreeNode)
vals = append(vals,node.Val)
continue
}
node=e.Value.(*TreeNode)
//压栈顺序:右左中
if node.Right != nil{
stack.PushBack(node.Right)
}
if node.Left != nil {
stack.PushBack(node.Left)
}
stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符
stack.PushBack(nil)
}
return vals
}
中序遍历
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//中序遍历:左中右
//压栈顺序:右中左
func inorderTraversal(root *TreeNode) []int {
if root==nil{
return nil
}
stack:=list.New()//栈
res:=[]int{}//结果集
stack.PushBack(root)
var node *TreeNode
for stack.Len()>0{
e := stack.Back()
stack.Remove(e)
if e.Value==nil{// 如果为空,则表明是需要处理中间节点
e=stack.Back()//弹出元素(即中间节点)
stack.Remove(e)//删除中间节点
node=e.Value.(*TreeNode)
res=append(res,node.Val)//将中间节点加入到结果集中
continue//继续弹出栈中下一个节点
}
node = e.Value.(*TreeNode)
//压栈顺序:右中左
if node.Right!=nil{
stack.PushBack(node.Right)
}
stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符
stack.PushBack(nil)
if node.Left!=nil{
stack.PushBack(node.Left)
}
}
return res
}
后序
//后续遍历:左右中
//压栈顺序:中右左
func postorderTraversal(root *TreeNode) []int {
if root == nil {
return nil
}
var stack = list.New()//栈
res:=[]int{}//结果集
stack.PushBack(root)
var node *TreeNode
for stack.Len()>0{
e := stack.Back()
stack.Remove(e)
if e.Value==nil{// 如果为空,则表明是需要处理中间节点
e=stack.Back()//弹出元素(即中间节点)
stack.Remove(e)//删除中间节点
node=e.Value.(*TreeNode)
res=append(res,node.Val)//将中间节点加入到结果集中
continue//继续弹出栈中下一个节点
}
node = e.Value.(*TreeNode)
//压栈顺序:中右左
stack.PushBack(node)//中间节点压栈后再压入nil作为中间节点的标志符
stack.PushBack(nil)
if node.Right!=nil{
stack.PushBack(node.Right)
}
if node.Left!=nil{
stack.PushBack(node.Left)
}
}
return res
}
二叉树层序遍历登场!
序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func levelOrder(root *TreeNode) [][]int {
res := [][]int{}
if root == nil {
return res
}
stack := list.New()
stack.PushBack(root)
var tmpArr []int
for stack.Len() > 0 {
length:=stack.Len()
for i:=0;i<length;i++ {
node:=stack.Remove(stack.Front()).(*TreeNode)
if node.Left != nil {
stack.PushBack(node.Left)
}
if node.Right != nil {
stack.PushBack(node.Right)
}
tmpArr = append(tmpArr,node.Val)
}
res = append(res,tmpArr)
tmpArr = []int{}
}
return res
}
226.翻转二叉树
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func invertTree(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
root.Left,root.Right=root.Right,root.Left
invertTree(root.Left)
invertTree(root.Right)
return root
}
101. 对称二叉树
力扣题目链接(opens new window)
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isSymmetric(root *TreeNode) bool {
return dfs(root.Left,root.Right)
}
func dfs(left *TreeNode,right *TreeNode) bool{
if left == nil && right == nil {
return true
}
if left == nil || right == nil {
return false
}
if left.Val != right.Val {
return false
}
return dfs(left.Left,right.Right) && dfs(left.Right,right.Left)
}
104.二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例: 给定二叉树 [3,9,20,null,null,15,7],
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
return max(maxDepth(root.Left),maxDepth(root.Right)) + 1
}
func max (a, b int) int {
if a > b {
return a;
}
return b;
}
111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func minDepth(root *TreeNode) int {
if root == nil {
return 0
}
if root.Left == nil && root.Right != nil {
return minDepth(root.Right)+1
}
if root.Right == nil && root.Left != nil {
return minDepth(root.Left)+1
}
return min(minDepth(root.Left),minDepth(root.Right))+1
}
func min(a int,b int) int{
if a < b {
return a
}
return b
}
222.完全二叉树的节点个数
给出一个完全二叉树,求出该树的节点个数。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//本题直接就是求有多少个节点,无脑存进数组算长度就行了。
func countNodes(root *TreeNode) int {
if root == nil {
return 0
}
res := 1
if root.Right != nil {
res += countNodes(root.Right)
}
if root.Left != nil {
res += countNodes(root.Left)
}
return res
}
110.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isBalanced(root *TreeNode) bool {
return height(root) >= 0
}
func height(node *TreeNode) int {
if node == nil {
return 0
}
leftH := height(node.Left)
rightH := height(node.Right)
if leftH == -1 || rightH == -1 || abs(leftH-rightH) > 1 {
return -1
}
return max(height(node.Left),height(node.Right))+1
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
func abs(x int) int {
if x < 0 {
return -1 * x
}
return x
}
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func binaryTreePaths(root *TreeNode) []string {
path := []string{}
var traversal func(node *TreeNode, str string)
traversal = func(node *TreeNode,str string){
if node.Left == nil && node.Right == nil{
v:=str+strconv.Itoa(node.Val)
path=append(path,v)
}
str += strconv.Itoa(node.Val)+"->"
if node.Left != nil {
traversal(node.Left,str)
}
if node.Right != nil {
traversal(node.Right,str)
}
}
traversal(root,"")
return path
}
404.左叶子之和
计算给定二叉树的所有左叶子之和
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func sumOfLeftLeaves(root *TreeNode) int {
if root == nil {
return 0
}
sum := 0
getLeft(root,&sum)
return sum
}
func getLeft(node *TreeNode,sum *int) {
if node.Left != nil && node.Left.Left == nil && node.Left.Right == nil {
*sum += node.Left.Val
}
if node.Left != nil {
getLeft(node.Left,sum)
}
if node.Right != nil {
getLeft(node.Right,sum)
}
}
513.找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
var maxDepth int
var value int
func findBottomLeftValue(root *TreeNode) int {
if root.Left==nil&&root.Right==nil{//需要提前判断一下
return root.Val
}
findLeft(root,maxDepth)
return value
}
func findLeft(node *TreeNode,depth int){
if node.Left == nil && node.Right == nil {
if depth > maxDepth {
maxDepth = depth
value = node.Val
}
}
if node.Left != nil {
depth++
findLeft(node.Left,depth)
depth--
}
if node.Right != nil{
depth++
findLeft(node.Right,depth)
depth--
}
}
112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func hasPathSum(root *TreeNode, targetSum int) bool {
if root == nil {
return false
}
targetSum -= root.Val // 将targetSum在遍历每层的时候都减去本层节点的值
if root.Left == nil && root.Right == nil && targetSum == 0 { // 如果剩余的targetSum为0, 则正好就是符合的结果
return true
}
return hasPathSum(root.Left, targetSum) || hasPathSum(root.Right, targetSum) // 否则递归找
}
113. 路径总和ii
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func pathSum(root *TreeNode, targetSum int) (ans [][]int) {
result := make([][]int, 0)
traverse(root, &result, new([]int), targetSum)
return result
}
func traverse(node *TreeNode, result *[][]int, currPath *[]int, targetSum int) {
if node == nil {
return
}
targetSum -= node.Val
*currPath = append(*currPath,node.Val)
if node.Left == nil && node.Right == nil && targetSum == 0 {
copyPath := make([]int,len(*currPath))
for k,v := range *currPath {
copyPath[k] = v
}
*result = append(*result,copyPath)
}
traverse(node.Left,result,currPath,targetSum)
traverse(node.Right,result,currPath,targetSum)
*currPath = (*currPath)[:len(*currPath)-1]
}
106.从中序与后序遍历序列构造二叉树
根据一棵树的中序遍历与后序遍历构造二叉树。
注意: 你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3]
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func buildTree(inorder []int, postorder []int) *TreeNode {
if len(inorder)<1||len(postorder)<1{return nil}
//先找到根节点(后续遍历的最后一个就是根节点)
nodeValue:=postorder[len(postorder)-1]
//从中序遍历中找到一分为二的点,左边为左子树,右边为右子树
left:=findRootIndex(inorder,nodeValue)
//构造root
root:=&TreeNode{Val: nodeValue,
Left: buildTree(inorder[:left],postorder[:left]),//将后续遍历一分为二,左边为左子树,右边为右子树
Right: buildTree(inorder[left+1:],postorder[left:len(postorder)-1])}
return root
}
func findRootIndex(inorder []int,target int) (index int){
for i:=0;i<len(inorder);i++{
if target==inorder[i]{
return i
}
}
return -1
}