力扣刷题 | 二叉树专题
各自努力,最高处见!加油!
二叉树专题
- 1、从上到下打印二叉树I
- 2、 二叉树的深度
- 3、从上到下打印二叉树 II
- 4、从上到下打印二叉树 III
- 5、对称的二叉树
- 6、平衡二叉树
- 7、树的子结构
- 8、翻转二叉树
- 9、完全二叉树的节点个数
- 10、另一棵树的子树
- 11、找树左下角的值
- 12、合并二叉树
- 13、二叉搜索树中的搜索
- 14、验证二叉搜索树
- 15、二叉搜索树中的众数
- 16、二叉树的最近公共祖先
- 17、二叉搜索树中的插入操作
- 18、删除二叉搜索树中的节点
- 19、将有序数组转换为二叉搜索树(递归+二分思想)
- 经验:
- 力扣runtime: out of memory: cannot allocate 4194304-byte block (469499904 in use
- 如何计算树的最大深度
- 递归法什么时候有返回值?
1、从上到下打印二叉树I
打印二叉树属于广度优先搜索(BFS)问题,通常用队列来解决问题。go语言中没有官方定义的队列类型,但是可以使用list包来完成队列的功能。
go语言中list的具体操作学习:https://haicoder.net/golang/golang-list.html
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func levelOrder(root *TreeNode) []int {result:=make([]int,0)if root==nil{return result}que:=list.New()que.PushBack(root)for que.Len()!=0{value := que.Front().Value.(*TreeNode)result=append(result,value.Val)if value.Left!=nil{que.PushBack(value.Left)}if value.Right!=nil{que.PushBack(value.Right)}que.Remove(que.Front())//更新队头}return result
}
2、 二叉树的深度
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {count:=0arr:=list.New()//存放一层数据que:=list.New()if root!=nil{arr.PushBack(root)}else{return count}for arr.Len()!=0{count++//把arr中存的一层数据全部放入队列中que.PushBackList(arr)arr=list.New()for que.Len()!=0{value := que.Front().Value.(*TreeNode)que.Remove(que.Front())//更新队头if value.Left!=nil{arr.PushBack(value.Left)}if value.Right!=nil{arr.PushBack(value.Right)}}}return count}
上面方法是利用层序遍历的方法来计算深度,该方法利用了两个队列,消耗的内存很大,需要继续优化。
3、从上到下打印二叉树 II
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
import "container/list"
func levelOrder(root *TreeNode) [][]int {if root==nil{return nil}var array [][]intqueue:=list.New()arr:=list.New()arr.PushBack(root)var value *TreeNodefor arr.Len()!=0{temp:=make([]int,0)for arr.Len()!=0 {value = arr.Front().Value.(*TreeNode)queue.PushBack(value)temp=append(temp,value.Val)arr.Remove(arr.Front())//更新队头} array=append(array,temp)for queue.Len()!=0{value=queue.Front().Value.(*TreeNode)if value.Left!=nil{arr.PushBack(value.Left)}if value.Right!=nil{arr.PushBack(value.Right)}queue.Remove(queue.Front())}}return array}
双队列法实现,速度很快,但是消耗内存较多
4、从上到下打印二叉树 III
多了一步数组的倒序处理,这个方法的运行速度很快,但是内存占比很大,如何优化使用内存?
func levelOrder(root *TreeNode) [][]int {queue:=list.New()next:=list.New()statu:=trueresult:=make([][]int,0)if root==nil {return nil}queue.PushBack(root)for queue.Len()!=0{array:=make([]int,0)for queue.Len()!=0{var value *TreeNodevalue=queue.Front().Value.(*TreeNode)//取出队头queue.Remove(queue.Front())//更新队头array=append(array,value.Val)//加入数组中if value.Left!=nil{next.PushBack(value.Left)}if value.Right!=nil{next.PushBack(value.Right)}}if statu == false {l := len(array)for i := 0; i < l-1-i; i++ {temp := array[l-1-i]array[l-1-i] = array[i]array[i] = temp}}result=append(result,array)//数组输出queue.PushBackList(next)next=list.New()//转换输出方式if statu==true{statu=false}else{statu=true}}return result}
5、对称的二叉树
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func isSymmetric(root *TreeNode) bool {if root==nil{return true}return recure(root.Left,root.Right)
}
func recure(L,R *TreeNode)bool{if L==nil&&R==nil{return true}if L==nil||R==nil||L.Val!=R.Val{return false}f1:=recure(L.Left,R.Right)f2:=recure(L.Right,R.Left)return f1&&f2
}
递归法
6、平衡二叉树
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func isBalanced(root *TreeNode) bool {if recure(root)==-1{return false}return true
}func recure(root *TreeNode) int {if nil==root{return 0}L:=recure(root.Left)if L==-1{return -1}R:=recure(root.Right)if R==-1{return -1}if L-R<2&&L-R>-2{return max(L,R)+1}return -1
}func max(a int,b int) int{if a>b{return a}return b
}
解题的关键点:如何求解二叉树的深度差
此树的深度 =max(左子树的深度 , 右子树的深度) +1
递归的方法,从底层开始算起,根节点为nil就向上返回0,表示这是第0层。此后每层的计算方法都按照上面的公式计算,逐层递增1,直到根节点。在递归过程中,如果出现一边的返回结果为-1,说明有子树不满足平衡二叉树的要求,直接返回-1.
7、树的子结构
回溯法+递归比对
用递归函数查找某节点的树结构跟目标树结构是否一致。
回溯法切换查找的节点。
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func isSubStructure(A *TreeNode, B *TreeNode) bool {if B==nil||A==nil{return false}return (recur(A,B)||isSubStructure(A.Left,B)||isSubStructure(A.Right,B))// return recur(A,B)//这种写法只是判断根节点开始的树有没有与目标树相同的结构}func recur(A *TreeNode, B *TreeNode) bool{//递归结构:如果节点的值相同,判断它的左右子树是否跟目标结构相同if B==nil{return true//子树的节点为空,说明检索完成,存在完全相同的子结构}if A==nil||A.Val!=B.Val{return false}return recur(A.Left,B.Left)&&recur(A.Right,B.Right)
}
8、翻转二叉树
递归解法:递归前序遍历翻转每个节点的左右节点
/*** 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}//交换左右节点temp:=root.Leftroot.Left=root.Rightroot.Right=tempinvertTree(root.Left)invertTree(root.Right)return root
}
前序迭代法
/*** 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}stack:=list.New()stack.PushBack(root)// stack.PushBack(nil)for stack.Len()>0{back:=stack.Back().Valuestack.Remove(stack.Back())if back==nil{node:=stack.Back().Value.(*TreeNode)stack.Remove(stack.Back())node.Left,node.Right=node.Right,node.Leftcontinue}node:=back.(*TreeNode)if node.Right!=nil{stack.PushBack(node.Right)}if node.Left!=nil{stack.PushBack(node.Left)}stack.PushBack(node)stack.PushBack(nil)}return root}
9、完全二叉树的节点个数
层序遍历解法
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func countNodes(root *TreeNode) int {//层序遍历count:=0if root==nil{return count}queue:=list.New()queue.PushBack(root)count++for queue.Len()>0{front:=queue.Front().Value.(*TreeNode)queue.Remove(queue.Front())if front.Left!=nil{count++queue.PushBack(front.Left)}if front.Right!=nil{count++queue.PushBack(front.Right)}}return count}
完全二叉树递归解法
10、另一棵树的子树
递归解法
暴力遍历该二叉树的节点,如果发现跟subroot根节点值相同的节点,就以该节点为根节点判断该子树是否与subroot树结构相同。isSub()判断两棵树是否为相同的树。
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func isSubtree(root *TreeNode, subRoot *TreeNode) bool {if root==nil&&subRoot==nil{return true}if root==nil||subRoot==nil{return false}if root.Val==subRoot.Val{if isSub(root,subRoot){return true}}return isSubtree(root.Left,subRoot)||isSubtree(root.Right,subRoot)
}
func isSub(root *TreeNode, subRoot *TreeNode)bool{//if root==nil&&subRoot==nil{return true}if root==nil||subRoot==nil||root.Val!=subRoot.Val{return false}return isSub(root.Left,subRoot.Left)&&isSub(root.Right,subRoot.Right)
}
11、找树左下角的值
递归法
需要搞明白的两个问题:
1、最左边的节点不一定在最底层
2、最底层最左边的节点不一定是左节点
递归解答本题的核心在于
if root.Left==nil&&root.Right==nil{//叶子节点if length>maxLength{//只有该层的第一个元素执行这个if语句maxLength=lengthleftNode=root.Val}
}
扫描到叶子节点就检测该节点所在的层是否大于已经扫描的最大层数,如果大于,就更新全局数据。这个if语句只有在扫描到每一层的第一个节点才会更新,所以就保证了该数据存的是该层的最左节点。
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
var maxLength int
var leftNode int
func findBottomLeftValue(root *TreeNode) int {//递归法需要搞明白的两个问题://1、最左边的节点不一定在最底层//2、最底层最左边的节点不一定是左节点if root.Left==nil&&root.Right==nil{return root.Val}maxLength=0leftNode=0findLeftLeaves(root,0)return leftNode}
func findLeftLeaves(root *TreeNode,length int){//当前为length层if root.Left==nil&&root.Right==nil{//叶子节点if length>maxLength{//只有该层的第一个元素执行这个if语句maxLength=lengthleftNode=root.Val}}if root.Left!=nil{findLeftLeaves(root.Left,length+1)}if root.Right!=nil{findLeftLeaves(root.Right,length+1)}
}
12、合并二叉树
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {if root1==nil&&root2==nil{return nil}else if root1==nil{return root2}else if root2==nil{return root1}// root:=&TreeNode{// Val:root2.Val+root1.Val,// Left:mergeTrees(root1.Left,root2.Left),// Right:mergeTrees(root1.Right,root2.Right),// }root1.Val+=root2.Valroot1.Left=mergeTrees(root1.Left,root2.Left)root1.Right=mergeTrees(root1.Right,root2.Right)//递归是从最底层回溯,所以可以更改root1节点return root1
}
13、二叉搜索树中的搜索
循环解法
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func searchBST(root *TreeNode, val int) *TreeNode {for {if root==nil{return nil}else if root.Val==val{return root}if root.Val>val{root=root.Left}else{root=root.Right}}
}
递归解法
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func searchBST(root *TreeNode, val int) *TreeNode {if root==nil{return nil}if root.Val==val{return root}if root.Val<val{return searchBST(root.Right,val)}else{return searchBST(root.Left,val)}
}
14、验证二叉搜索树
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
var isBST func(node *TreeNode) bool
func isValidBST(root *TreeNode) bool {var pre *TreeNode = nilisBST = func(root *TreeNode) bool {if root == nil {return true}left := isBST(root.Left)if pre != nil && pre.Val >= root.Val {return false}pre = rootright := isBST(root.Right)return left && right}return isBST(root)}
15、二叉搜索树中的众数
递归解法:
判断截止该节点,相同值的节点数量是否大于maxCount,如果大于则更新maxCount并清空数组的数据。
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
var searchBST func(*TreeNode)
func findMode(root *TreeNode) []int {res:=make([]int,0)if root.Left==nil&&root.Right==nil{//only oneres=append(res,root.Val)return res}var pre *TreeNodemaxCount:=0count:=0searchBST=func(root *TreeNode){if root==nil{return }searchBST(root.Left)if pre==nil{count=1}else if pre!=nil{if pre.Val==root.Val{count++}else{count=1//重新计数}}pre=rootif count>maxCount{//计算到本节点为止众数的数量是否超过maxCountres=make([]int,0)res=append(res,pre.Val)maxCount=count}else if count==maxCount{res=append(res,pre.Val)}searchBST(root.Right)}searchBST(root)return res}
16、二叉树的最近公共祖先
如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。(摘自代码随想录)这道题有一个点是,即使两个pq两个节点都在根节点的左子树,其右子树一样也会遍历,只不过返回的是nil。如在本题例子中(上图),p为jiedian4,q为节点5,公共祖先为5.采用如下递归法遍历到5时便向上一层返回该节点。而1节点返回nil,这时程序就可以判定5就是公共祖先。
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {if root==nil||root==p||root==q{return root}left:=lowestCommonAncestor(root.Left,p,q)right:=lowestCommonAncestor(root.Right,p,q)if left!=nil&&right!=nil{return root}else if left==nil&&right!=nil{return right}else if left!=nil&&right==nil{return left}else{return nil}
}
17、二叉搜索树中的插入操作
递归解法
我写的代码如下,我的思路是只有当val所在的方向为空时,才接受返回值,但是其实这一步完全可以省略。因为这是在递归中,在递归回溯的时候,左子节点或右子节点会赋值到原来的位置。见改进写法(参考代码随想录)。
func insertIntoBST(root *TreeNode, val int) *TreeNode {if root==nil{root=&TreeNode{Val:val,Left:nil,Right:nil,}}if val<root.Val&&root.Left!=nil{insertIntoBST(root.Left,val)}else if val>root.Val&&root.Right!=nil{insertIntoBST(root.Right,val)}else if val<root.Val&&root.Left==nil{root.Left=insertIntoBST(root.Left,val)}else if val>root.Val&&root.Right==nil{root.Right=insertIntoBST(root.Right,val)}return root
}
改进写法
func insertIntoBST(root *TreeNode, val int) *TreeNode {if root==nil{root=&TreeNode{Val:val,Left:nil,Right:nil,}}if val<root.Val{root.Left=insertIntoBST(root.Left,val)}else if val>root.Val{root.Right=insertIntoBST(root.Right,val)}return root
}
18、删除二叉搜索树中的节点
递归法分情况讨论:代码随想录yyds
第一种情况:没找到删除的节点,遍历到空节点直接返回了
找到删除的节点
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
func deleteNode(root *TreeNode, key int) *TreeNode {if root==nil{return nil}if root.Val==key{//找到删除的节点if root.Left==nil&&root.Right==nil{return nil}if root.Left==nil&&root.Right!=nil{return root.Right}if root.Right==nil&&root.Left!=nil{return root.Left}if root.Right!=nil&&root.Left!=nil{ptr:=root.Rightfor ptr.Left!=nil{ptr=ptr.Left}ptr.Left=root.Leftreturn root.Right}}//没找到要删除的节点root.Left=deleteNode(root.Left,key)root.Right=deleteNode(root.Right,key)return root}
19、将有序数组转换为二叉搜索树(递归+二分思想)
func sortedArrayToBST(nums []int) *TreeNode {return traversal(nums,0,len(nums)-1)
}
func traversal(nums []int,left int,right int)*TreeNode{if left>right{return nil}mid:=left+(right-left)/2root:=&TreeNode{Val:nums[mid],Left:traversal(nums,left,mid-1),Right:traversal(nums,mid+1,right),}return root
}
经验:
力扣runtime: out of memory: cannot allocate 4194304-byte block (469499904 in use
出现这种错误先检查代码中是否存在死循环,可能是循环的条件有错或者for循环的 i 变量没有递增导致的。
如何计算树的最大深度
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
}
递归法什么时候有返回值?
如果需要遍历整棵树,递归函数就不能有返回值。如果需要遍历某一条固定路线,递归函数就一定要有返回值!
力扣刷题 | 二叉树专题相关推荐
- 力扣刷题之二叉树的层序遍历
Welcome to you, 每日一刷系列 二叉树的层序遍历 二叉树的层序遍历II 二叉树的右视图 ...
- 《剑指Offer》力扣刷题笔记(03-10)
<剑指Offer>力扣刷题笔记(03-10) 最近确实有点闲,想在进组搬砖之前找点有意义的事干,于是,就开始刷<剑指Offer>.<程序员面试金典>等书上的题目,也 ...
- 教你创建电脑、手机同步的markdown云笔记--力扣刷题力荐!
开篇先致歉 其他不谈,开篇必须先给各位读者道个歉,年后工作上比较忙,加上最近闲暇的时间都用来在力扣上刷算法题了,导致公众号断更有些严重啊.再加上年后将健身减重提上了日程,时间上就更显的捉襟见肘了. 不 ...
- 『力扣刷题』5275_找出井字棋的获胜者 解题代码
LeetCode-cn 力扣刷题 LeetCode-cn力扣刷题目录 165周赛 5275_找出井字棋的获胜者 * 5275. 找出井字棋的获胜者 显示英文描述* 用户通过次数0* 用户尝试次数0* ...
- 『力扣刷题』5276_不浪费原料的汉堡制作方案 解题代码
LeetCode-cn 力扣刷题 LeetCode-cn力扣刷题目录 165周赛 5276_不浪费原料的汉堡制作方案 * 5276. 不浪费原料的汉堡制作方案 显示英文描述* 用户通过次数212* 用 ...
- 『力扣刷题』5238_找出给定方程的正整数解 解题代码
html: embed_local_images: true embed_svg: true offline: true toc: undefined print_background: false ...
- 力扣刷题全局变量WA,局部变量AC的问题
力扣刷题全局变量WA,局部变量AC问题 题目 原因 题目 golang力扣leetcode 1219.黄金矿工 原因 今天在刷题的时候,遇到一个现象,本地跑测试全部通过,一提交就错错错,非常离谱 然后 ...
- 力扣刷题pdf(java版本,内含暗黑版和光明版),都在这里了
BAT大佬力扣刷题pdf,都在这里了! 相信很多小伙伴刷题的时候面对力扣上近两千道题目,感觉无从下手! 我找了很久,今天终于让找到了Java版leetcode算法题解笔记,强烈建议先按照本篇介绍pd ...
- 力扣刷题-前k个高频元素
力扣刷题-前k个高频元素 题目: 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素.你可以按 任意顺序 返回答案. 示例 1: 输入: nums = [1,1,1,2 ...
- 力扣刷题记录--哈希表相关题目
当遇到需要快速判断一个元素是否出现在集合里面的时候,可以考虑哈希法,牺牲一定的空间换取查找的时间. java常用的哈希表有HashMap.HashSet以及用数组去模拟哈希,这几种方法各有优劣. 数组 ...
最新文章
- 2018人工智能行业创新情报白皮书
- python代码写龙卷风_python面试题
- 作用域和闭包的通俗理解
- 自己写一个树形导航菜单
- 【pmcaff专栏】一个“Uber”血染中国共享经济
- euv光刻机有什么用_台积电又买了13台EUV光刻机?
- OpenStack组件
- python重载运算符乘法_Python | 使用乘法运算符创建一个字符串的多个副本
- vue-cli3项目移动设备调试访问报错WDS:Disconnected,无法自动刷新或模块热替换
- Android:内部存储(读写文件、追加模式)
- 电商手机端促销海报设计PSD分层模板,来给你保驾护航!
- 一天快速入门 Python
- 计算机网络—三种CSMA协议
- linux下安装anconda
- How to scale the BERT Training with Nvidia GPUs?
- 文科生也能学会的Excel VBA 宏编程入门
- 教你如何用python把玩守望先锋新英雄
- 【opencv 450 core】使用统一向量指令(Universal Intrinsics)对代码进行矢量化
- zoho在线文档使用小技巧
- 使用故障恢复控制台解决XP系统无法启动的常见故障