1递归遍历二叉树
1. 1 前序遍历:递归法
-
144.二叉树的前序遍历(中序和后序差不多) -
思路介绍: 1)首先写主函数 2)然后开始写递归函数(包含两部分,截止条件和递归次序:中间左边右边)
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList();
preorder(root , list);
return list;
}
public static void preorder(TreeNode root,List<Integer> list){
if(root==null) return;
list.add(root.val);
preorder(root.left,list);
preorder(root.right,list);
}
}
1. 2 前序遍历:迭代法(深度优先算法)
- 思路
1)前序遍历的顺序中左右,那么只需要把入栈的顺序改为中-右-左即可。这样出栈顺序就是中-左-右了 2)代码书写过程类似层序遍历, (1):定义集合和栈 (2):把首节点放进去 (3):开始进行遍历操作,一层while循环,然后就是先出栈后压栈(顺序为中右边左边) - 代码
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
if(root==null) return list;
deque.push(root);
while(deque.size()>0){
TreeNode fa=deque.pop();
list.add(fa.val);
if(fa.right!=null) deque.push(fa.right) ;
if(fa.left!=null) deque.push(fa.left) ;
}
return list;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
if(root==null) return list;
deque.push(root);
while(deque.size()>0){
TreeNode temp=deque.pop();
list.add(temp.val);
if(temp.left!=null) deque.push(temp.left);
if(temp.right!=null) deque.push(temp.right);
}
Collections.reverse(list);
return list;
}
}
1. 3 层序遍历:迭代法
- 思路步骤
1) 定义二维数组集合与队列 2)特殊情况先考虑,并把根节点放进去 3)根节点不为空,那么就一层一层放东西(两层while循环) (1)第一层while循环,得到目前队列的大小,判断是否还有下一层,然后把上一层的数据装载入集合中 (2) 第二层while循环,(先存子节点,和父节点值,再删父节点)循环遍历队列中的元素,看子节点是否存在,存在就进行输入队列,然后把父节点值存入,再删除父节点。 (3)在第一层里,第二层外,把每一层的数据存起来。 4)输出二维集合数据 - 代码展示
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> data=new ArrayList<>();
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
if(root==null) return data;
deque.offer(root);
int size;
while(deque.size()!=0){
List<Integer> list=new ArrayList<>();
size=deque.size();
while(size-->0){
if(deque.peek().left!=null) deque.offer(deque.peek().left);
if(deque.peek().right!=null) deque.offer(deque.peek().right);
list.add(deque.poll().val);
}
data.add(list);
}
return data;
}
- 总结
1)层序遍历是一个经典的bfs算法(广度搜索算法)。对一个地方都会进行遍历一遍,能够解决很多类似问题 2)能够解决一些类型问题
3)层序遍历算法是使用迭代法,使用队列装节点。
1. 3.1 Leecode:116. 填充每个节点的下一个右侧节点指针
-
题目描述 -
思路说明‘ 1)需要额外的两个操作 (1):父节点右边节点与相邻节点的左边节点链接 (2):父节点的子节点之间的连接(非空一定有) -
代码展示
class Solution {
public Node connect(Node root) {
if(root == null || root.left == null) return root;
root.left.next = root.right;
if(root.next != null) root.right.next = root.next.left;
connect(root.left);
connect(root.right);
return root;
}
}
2 反转二叉树
2.1 前序遍历:深搜
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
swap(root);
invertTree(root.left);
invertTree(root.right);
return root;
}
public static void swap(TreeNode node){
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
}
}
2.2 后序遍历:深搜
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
invertTree(root.left);
invertTree(root.right);
swap(root);
return root;
}
public static void swap(TreeNode node){
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
}
}
2.3 层序遍历:广搜
class Solution {
public TreeNode invertTree(TreeNode root) {
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
if(root==null) return root;
deque.push(root);
while(deque.size()>0){
int size=deque.size();
while(size-->0){
TreeNode node=deque.pop();
swap(node);
if(node.left!=null) deque.push(node.left);
if(node.right!=null) deque.push(node.right);
}
}
return root;
}
public static void swap(TreeNode node){
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
}
}
3 对称二叉树
- 题目描述
- 思路
1)书写递归方法,输入两个子节点 2)截止条件:判断两个子节点的情况(四种情况,有空值,值不相等) 3)递归:保证两个子节点是相等的,非空的:A:两个子节点的外侧;B:两个子节点的内侧 4)该递归是有返回值的:不满足条件的时候,会立即退出
class Solution {
public boolean isSymmetric(TreeNode root) {
return method(root.left,root.right);
}
public static boolean method(TreeNode node1,TreeNode node2){
if(node1==null && node2==null) return true;
if(node1==null && node2!=null) return false;
if(node2==null && node1!=null) return false;
if(node2.val!=node1.val) return false;
boolean A=method(node1.left, node2.right);
boolean B=method(node1.right, node2.left);
return A && B;
}
}
4 二叉树最大深度
- 概念介绍
1)深度:从根节点到该节点的最长简单路径边(需要从上往下查,一般使用前序遍历) 2)高度:从该节点到叶子节点的最长简单路径边(从下到上查,一般使用后序遍历) - 后序遍历递归算法
class Solution {
public int maxDepth(TreeNode root) {
if(root==null) return 0;
int A=maxDepth(root.left);
int B=maxDepth(root.right);
return Math.max(A,B)+1;
}
2. 前序遍历递归算法
5 N叉树最大深度
- 后序遍历递归算法
class Solution {
public int maxDepth(Node root) {
if(root==null) return 0;
int result=0;
for(int i=0;i<root.children.size();i++){
result=Math.max(result,maxDepth(root.children.get(i)));
}
return result+1;
}
}
2. 前序遍历递归算法
6 二叉树最小深度
- 思路
1)其实整体上是延续二叉树最大深度的想法 2)在处理两个子节点,其中一个为空的情况,返回值不为此处的零,而为另一边节点深度+1 - 代码
1)方法一:后序遍历的递归 只是在两个子节点,其中一个为空的情况下,更改下逻辑
class Solution {
public int minDepth(TreeNode root) {
if(root==null) return 0;
int leftDepth=minDepth(root.left);
int rightDepth=minDepth(root.right);
if(root.left==null) return 1+rightDepth;
if(root.right==null) return 1+leftDepth;
return 1+Math.min(leftDepth,rightDepth);
}
}
2)方法二:层序遍历的迭代 需要找到叶子节点,那么就输出该层的高度 在第二层while循环中加入这一句:
if(temp.left==null&&temp.right==null){
7. 完全二叉树的节点个数
- 概念说明
1)完全二叉树: 2) - 题目说明
- 思路说明
1)如果把它看成是一个简单的树,那么可以用简单的递归(方法一) 2)通过层序遍历,也可以计算出来(简单树) (方法二) 3)如果看成是完全二叉树,则需要先找到完全二叉树,找到之后用公式,没找到,递归孩子节点用公式。(方法三) - 代码说明
1)方法一:递归,看是一般的树
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
return countNodes(root.left)+countNodes(root.right)+1;
}
}
2)方法二:看成是完全二叉树,通过递归进行求解
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
TreeNode left=root.left;
TreeNode right=root.right;
int lefthigh=0;
int righthigh=0;
while(left!=null){
left=left.left;
lefthigh++;
}
while(right!=null){
right=right.right;
righthigh++;
}
if(lefthigh==righthigh) {
return (2<<lefthigh)-1;
}
return countNodes(root.left)+countNodes(root.right)+1;
}
}
3)方法三:看成是一般的树,通过迭代进行求解
class Solution {
public int countNodes(TreeNode root) {
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
if(root==null) return 0;
deque.offer(root);
int num=0;
while(deque.size()>0){
int size=deque.size();
while(size-->0){
num++;
TreeNode node=deque.poll();
if(node.left!=null) deque.offer(node.left);
if(node.right!=null) deque.offer(node.right);
}
}
return num;
}
}
8. 平衡二叉树
- 题目说明
- 思路说明
1)代码的核心思路是通过后续遍历,求解树的最大高度 2)核心代码与前一题求取树的最大深度类似 3)在前一题基础上,多加了一些判断,如果节点的左右子树不平衡,就返回-1进行停止。
class Solution {
public boolean isBalanced(TreeNode root) {
if(gethigh(root)==-1) {
return false;
}else return true;
}
public static int gethigh(TreeNode root){
if(root==null) return 0;
int lefthigh=gethigh(root.left);
if(lefthigh==-1) return -1;
int righthigh=gethigh(root.right);
if(righthigh==-1) return -1;
if(Math.abs(lefthigh-righthigh)>1) return -1;
return Math.max(lefthigh,righthigh)+1;
}
}
9. 二叉树的所有路径(广搜的思想)
-
题目: -
思路: 1)使用前序遍历进行全局遍历,因为是搜索全树,满足条件的路径。 2)递归函数的返回值类型和入参: 返回值类型为空(因为要遍历全树),入参为(根节点,String集合存结果,Integer存路径) 3)第一步:把根节点加入Integer集合中 4)(中):如果是根节点,那么就返回路径的全部值 5)(左):左边非空,递归左边,并且再回溯一步(即把最后一个路径值返回) 6)(右):右边非空,递归右边,并且再回溯一步(即把最后一个路径值返回) -
代码
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
if(root==null) return result;
method(root,result,path);
return result;
}
public void method(TreeNode root,List<String> res,List<Integer> path){
path.add(root.val);
if(root.left==null &&root.right==null){
StringBuilder sb=new StringBuilder();
for(int i=0;i<path.size();i++){
if(i==path.size()-1){
sb.append(path.get(i));
}else{
sb.append(path.get(i)).append("->");
}
}
res.add(sb.toString());
return;
}
if(root.left!=null){
method(root.left,res,path);
path.remove(path.size()-1);
}
if(root.right!=null){
method(root.right,res,path);
path.remove(path.size()-1);
}
}
}
10. 404. 左叶子之和
- 题目
- 思路
1)好好体会前序遍历的思想,从上往下依次进行遍历,先对头的信息进行处理(暂时不需要直到其叶子节点的值) 2)递归三部曲 (注意,如果直接用这个函数,中间需要一个全局变量,或者写为一个子函数,传入已给变量) (1):截止条件:根节点为空,给节点没有叶子节点,均会返回0 (2):中:遇到了左叶子节点,那么就把左叶子节点之和给加起来 (3):前序遍历常规的递归操作,左右进行遍历 (4):返回值sum - 代码详解
class Solution {
public int sum=0;
public int sumOfLeftLeaves(TreeNode root) {
if(root==null) return 0;
if(root.left==null && root.right==null) return 0;
if(root.left!=null && root.left.left==null && root.left.right==null){
sum+=root.left.val;
}
if(root.left!=null) sumOfLeftLeaves(root.left);
if(root.right!=null) sumOfLeftLeaves(root.right);
return sum;
}
}
class Solution {
public int sum=0;
public int sumOfLeftLeaves(TreeNode root) {
method(root);
return sum;
}
public void method(TreeNode root){
if(root==null || root.left==null && root.right==null) return;
if(root.left!=null && root.left.left==null && root.left.right==null){
sum+=root.left.val;
}
if(root.left!=null) method(root.left);
if(root.right!=null) method(root.right);
}
}
- 总结
1)如果是要遍历全树,那么递归函数不能有返回值(有返回值也不能用,只能作为最后的输出),因为一旦用了,那么就会在中途某个地方停下来。
11. 找树左下角的值
- 题目
- 思路说明
1)方法一:层序遍历,对每一层的第一个数进行保留 2)方法二:递归,前序遍历:在前的操作中,保存最大深度,在出现叶子节点的时候,判断深度是否超出原来的,超出就记录一下最新的深度,并且保留左叶子节点的值。(遍历顺序,默认为第一个) - 代码展示
1)方法一:层序遍历
class Solution {
public int findBottomLeftValue(TreeNode root) {
ArrayDeque<TreeNode> deque=new ArrayDeque<>();
int result=0;
deque.offer(root);
while(deque.size()>0){
List<Integer> list=new ArrayList<>();
int size=deque.size();
for(int i=0;i<size;i++){
TreeNode node=deque.poll();
if(i==0) result=node.val;
if(node.left!=null) deque.offer(node.left);
if(node.right!=null) deque.offer(node.right);
}
}
return result;
}
}
2)方法二:递归遍历
class Solution {
private int maxdepth=-1;
private int depth=0;
private int result;
public int findBottomLeftValue(TreeNode root) {
method(root);
return result;
}
public void method(TreeNode root){
if(root==null) return;
if(root.left==null &&root.right==null){
if(depth>maxdepth){
maxdepth=depth;
result=root.val;
}
}
if(root.left!=null){
depth++;
method(root.left);
depth--;
}
if(root.right!=null){
depth++;
method(root.right);
depth--;
}
}
}
12. 路径总和(深度:有返回值,要对左右递归值进行判别)
- 题目描述
- 思路
1)这道题目不需要遍历全局,只需要遍历局部就可以,所以递归是带有返回值的(方便及时退出) 2)求和的过程中:遇到一个节点就减去,而不是最后拿一个集合装,简便运算 3)递归三部曲 (1):截止条件:如果遇到了叶子节点,就先判断,目前的和是否为0了 (2):不是叶子节点,就继续左右遍历,四步(1)先减去该节点的值,2)然后进行递归判断,3)如果结果为true那么就停止);4)然后跟上一个回溯(没退出)。 4)最后递归返回值为false,不满足任何一个true的条件
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null) return false;
return find(root,targetSum-root.val);
}
public boolean find(TreeNode root, int targetSum){
if(root.left==null && root.right==null){
if(targetSum==0){
return true;
}else return false;
}
if(root.left!=null){
targetSum-=root.left.val;
boolean flag=find(root.left,targetSum);
if(flag==true) return true;
targetSum+=root.left.val;
}
if(root.right!=null){
targetSum-=root.right.val;
boolean flag=find(root.right,targetSum);
if(flag==true) return true;
targetSum+=root.right.val;
}
return false;
}
}
13 .路径总和ii(广度遍历)
-
题目 -
中心思路 1)因为需要遍历全树,故递归无返回值 2)这道题与求路径的题目非常像,知识在(中)的时候,进行一个判断,只有满足要求的才能被装进去,且是深度复制 -
代码解析
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
if(root==null) return result;
method(root,targetSum,result,path);
return result;
}
public void method(TreeNode root, int targetSum, List<List<Integer>> result,List<Integer> path){
path.add(root.val);
int sum=0;
if(root.left==null && root.right==null){
for(int i:path) sum+=i;
if(sum==targetSum) result.add(new ArrayList<>(path));
}
if(root.left!=null){
method(root.left,targetSum,result,path);
path.remove(path.size()-1);
}
if(root.right!=null){
method(root.right,targetSum,result,path);
path.remove(path.size()-1);
}
}
}
14 .从中序与后序遍历序列构造二叉树(有一点绕)
- 题目说明
- 题目思路
1)其实就是一个递归的三部曲操作, (1):截止条件有两个:一个是差值为0,一个是差值为1; (2)先找到分割点的索引,然后找到分割点的值,并新建树的节点(新建树,用new TreeNode() (3)然后递归找到左右子树,左边的两组给左边,右边的两组给右边 最后输出根节点。
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return method(inorder,0,inorder.length,postorder,0,postorder.length);
}
public TreeNode method(int[]inorder, int inleft, int inright, int[]postorder, int postleft, int postright){
if(inright-inleft==0) return null;
if (inright - inleft == 1) return new TreeNode(inorder[inleft]);
int temp=postorder[postright-1];
TreeNode root=new TreeNode(temp);
int target=0;
for(int i=inleft;i<inright;i++){
if(inorder[i]==temp){
target=i;
break;
}
}
root.left=method(inorder,inleft,target,postorder,postleft,postleft+(target-inleft));
root.right=method(inorder, target+1,inright, postorder, postleft+(target-inleft),postright-1);
return root;
}
}
15 . 从前序与中序遍历序列构造二叉树(和上题一样思路)
- 题目说明
根据前序和中序遍历能够构造唯一的二叉树。思路和根据中序和后序类似,重点是左右递归参数索引的选定,比较容易弄错,可以中间输出一个日志进行说明。 - 代码说明
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return method(preorder,0,preorder.length, inorder,0,inorder.length);
}
public TreeNode method(int[] preorder, int preleft, int preright, int[] inorder, int inleft, int inright){
if(inright-inleft==0) return null;
if(inright-inleft==1) return new TreeNode(preorder[preleft]);
int target=preorder[preleft];
TreeNode node=new TreeNode(target);
int targetsize=0;
for(int i=inleft;i<inright;i++){
if(inorder[i]==target){
targetsize=i;
break;
}
}
node.left=method(preorder,preleft+1,targetsize-inleft+(preleft+1), inorder,inleft,targetsize);
node.right=method(preorder,targetsize-inleft+(preleft+1),preright, inorder,targetsize+1,inright);
return node;
}
}
|