基本上能使用“递归法”的都能用“迭代法”,优先学会“递归法”!!!
递归法:
1.二叉树的前中后序遍历
2.N叉树的前后序遍历
3.其他内容
首先来复习二叉树的基础知识点:
1.二叉树分类:
2.二叉树的存储:
(1)顺序存储:利用数组,不常用;
*(2)链表存储:常用存储方式,二叉树的链表里包括值、左指针和右指针;如果是N叉树,那么包括值和孩子指针。
3.二叉树的遍历:
---深度优先遍历(DFS):前中序遍历,常用递归;
---广度优先遍历(BFS):层序遍历,常用迭代;
主要掌握“递归法”,将层序遍历学会后可以当成一个模板解决好几个问题,只需要改变一点逻辑关系就完成了。
144.二叉树的前序遍历
思路:前序遍历是“中左右”的遍历顺序,将遍历到的节点的值加入到集合中;然后是对当前节点的左右子树一直进行递归,让每到一个节点就以此节点为主节点又去获得它的左右子树,一直重复这个过程,直到没有节点为止。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
preorder(root,res);
return res;
}
public void preorder(TreeNode node,List<Integer> list){
if(node==null){
return;
}
list.add(node.val);
preorder(node.left,list);
preorder(node.right,list);
}
}
94.二叉树的中序遍历
思路:中序遍历是“左中又”,和前序相较只是遍历顺序变成了先走左,然后再将中节点的值加入集合最后是右。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
inorder(root,res);
return res;
}
public void inorder(TreeNode node,List<Integer> list){
if(node==null){
return;
}
inorder(node.left,list);
list.add(node.val);
inorder(node.right,list);
}
}
145.二叉树的后序遍历
思路:后续遍历是“左右中”,方式也是和上列一样,只是顺序改变成先是走左右也就是递归左右子树,最后再将中节点的值加入到集合中。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<Integer>();
postorder(root,res);
return res;
}
public void postorder(TreeNode node,List<Integer> list){
if(node==null){
return;
}
postorder(node.left,list);
postorder(node.right,list);
list.add(node.val);
}
}
589.N叉树的前序遍历
思路:基本上同二叉树的前序遍历一样的,只是N叉树有N个孩子,那么需要遍历所有孩子,再将所有孩子进行递归。
class Solution {
public List<Integer> preorder(Node root) {
List<Integer> result=new ArrayList<Integer>();
preGo(root,result);
return result;
}
public void preGo(Node node,List<Integer> list){
if(node==null){
return;
}
list.add(node.val);
for(Node no:node.children){
preGo(no,list);
}
}
}
590.N叉树的后序遍历
思路:基本上同二叉树的后续遍历一样,只是N叉树有多个孩子,需要遍历所有孩子,然后递归所有孩子。
class Solution {
public List<Integer> postorder(Node root) {
List<Integer> result=new ArrayList<Integer>();
postGo(root,result);
return result;
}
public void postGo(Node node,List<Integer> list){
if(node==null){
return;
}
for(Node no:node.children){
postGo(no,list);
}
list.add(node.val);
}
}
104.二叉树的最大深度
辨析:二叉树的高度与深度
-高度:从下到上越来越高(可理解为楼高),由下向上反馈高度,后序遍历;
-深度:从上到下越来越深(可理解为水深),由上向下反馈深度,前序遍历;
最大深度也可以看成是最大高度问题,所以使用前序后序遍历都是可以的!
思路:计算最大深度的过程:每次都是计算到当前节点子树的深度,但是没有算上这个节点的,所以需要加1是因为要算上每次的当前节点,获得左右两边的深度然后获得较大。
class Solution {
public int maxDepth(TreeNode root) {
int high=0;
if(root==null){
return 0;
}
int lefthigh=maxDepth(root.left);
int righthigh=maxDepth(root.right);
return Math.max(lefthigh,righthigh)+1;
}
}
559.N叉树的最大深度
思路:总体和二叉树的最大深度是一样的,唯一不同是N叉树有N个孩子,所以需要遍历所有孩子并进行递归,然后求得在孩子中最深的。
//递归法
class Solution {
public int maxDepth(Node root) {
if(root==null){
return 0;
}
int res=0;
if(root.children!=null){
for(Node no:root.children){
int chigh=maxDepth(no);
res=Math.max(res,chigh);
}
}
return res+1;
}
}
111.二叉树的最小深度
思路:最小深度的问题较复杂,需要分三类情况来进行讨论:
如下图的三种情况:
(1)根节点不具有左子树,这个时候很容易出现一个错想:就是把1当成了最小深度;所以当节点的左子树为空时,返回右子树长度加1;
(2)根节点不具有右子树,同不具有左子树是一样的,只是当右子树为空时,返回左子树长度加1;
(3)根节点左右子树都具有,那么就返回左右子树中最小(Math.max)的加1。
自己理解说明:加1是因为:每次计算的深度都是当前节点的子树深度,是没有包含这个节点的,所以每次算的时候加1是加上了节点的深度。
class Solution {
public int minDepth(TreeNode root) {
//递归结束条件:当节点不存在后,返回0表示当前高为0;
if(root==null){
return 0;
}
//现在计算的高度只是子树的高度,是没有加上根节点的,所以最终结果需要加上1;
int lefthigh=minDepth(root.left);
int righthigh=minDepth(root.right);
if(root.left==null){
return 1+righthigh;
}
if(root.right==null){
return 1+lefthigh;
}
return Math.min(lefthigh,righthigh)+1;
}
}
222.完全二叉树的节点个数
层序法是可以直接套用模板,然后直接在节点出队时对于节点总数加一,最后返回的是总节点数。
以下是递归法:
思路:在迭代法中,设置一个变量记录每次出队时候的数量最后得到每层遍历的总数量完成;对于递归法来说:递归可以说是按照一个个节点来遍历的,所以递归获得当前节点的左右子树的个数再加一。
class Solution {
public int countNodes(TreeNode root) {
if(root==null){
return 0;
}
int leftsum=countNodes(root.left);
int rightsum=countNodes(root.right);
int sum=leftsum+rightsum+1;
return sum;
}
}
110.平衡二叉树
思路:平衡二叉树的概念是:左右子树的高度差不超过1;所以此题递归获得左右子树的高度,然后每次获得较长的减去另外一部分看是否超过1,不满足要求返回-1,当左右子树高度为-1时也返回-1。
class Solution {
public boolean isBalanced(TreeNode root) {
return getHigh(root)!=-1;
}
public int getHigh(TreeNode node){
//递归结束条件:当遍历到的节点为null时,表示“本次”结果返回0;
if(node==null){
return 0;
}
int lefthigh=getHigh(node.left);
if(lefthigh==-1){
return -1;
}
int righthigh=getHigh(node.right);
if(righthigh==-1){
return -1;
}
if(Math.abs(lefthigh-righthigh)>1){
return -1;
}
return Math.max(lefthigh,righthigh)+1;
}
}
404.左叶子之和
思路:满足要求的左叶子是:当前节点的左子树不为空但是节点的左子树的左子树还有右边的都为空时,这个就是满足要求的节点,所以获取到此节点的值;因为递归是按照左右边一个个来遍历的,所以是分开了左右两边来进行这个递归的,所以最后需要将左值+右值+当前满足的值,因为左右的值是对之前满足条件的值相加的总量。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root==null){
return 0;
}
int leftvalue=sumOfLeftLeaves(root.left);
int rightvalue=sumOfLeftLeaves(root.right);
int midvalue=0;//要赋值
if(root.left!=null&&root.left.left==null&&root.left.right==null){
midvalue=root.left.val;
}
int sum=midvalue+leftvalue+rightvalue;
return sum;
}
}
257.二叉树的所有路径
思路:获得的所有路径从根节点出发到叶子节点,所以这道题涉及到“回溯”,还要注意的是回溯是与递归是一体的,当出现递归后面就必须要是回溯,回溯就是往上返回;
本题的主要思路是:要将路径用“->”连接起来成为一个字符串,最后加入集合中,从根出发到叶子节点为一条路径,那么此路径的终止条件是遍历到的节点左右子树都为空时,这个过程创建一个存放每次遍历到的节点的值,在终止条件成立后开始遍历这个集合,然后创建一个“StringBuilder”,将遍历到的值append入集合中,然后每次加入一个数字之前继续append("->"),只遍历到倒数第二个数,等这个遍历结束之后,再往StringBuilder里append入集合中最后一个数,最后将StringBuilder.toString()转为字符串后加入到存放这些字符串的集合中。
//最后返回的是路径字符串构成的集合中
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res=new ArrayList<String>();
if(root==null){
return res;
}
List<Integer> list=new ArrayList<Integer>();
getAll(root,list,res);
return res;
}
public void getAll(TreeNode node,List<Integer> list,List<String> result){
list.add(node.val);
StringBuilder sb=new StringBuilder();
if(node.left==null&&node.right==null){
for(int i=0;i<list.size()-1;i++){
sb.append(list.get(i)).append("->");
}
sb.append(list.get(list.size()-1));
result.add(sb.toString());
return;
}
if(node.left!=null){
getAll(node.left,list,result);
list.remove(list.size()-1);
}
if(node.right!=null){
getAll(node.right,list,result);
list.remove(list.size()-1);
}
}
}
层序遍历-迭代法,可以将层序遍历理解看成是一个模板,还可以完成下面九道题,只要更改一些逻辑即可完成:
102.二叉树的层序遍历
class Solution {
public List<List<Integer>> res=new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
cheFun(root);
return res;
}
public void cheFun(TreeNode node){
if(node==null){
return;
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(node);
while(!queue.isEmpty()){
List<Integer> list=new ArrayList<Integer>();
int len=queue.size();
while(len>0){
TreeNode no=queue.poll();
list.add(no.val);
if(no.left!=null){
queue.offer(no.left);
}
if(no.right!=null){
queue.offer(no.right);
}
len--;
}
res.add(list);
}
}
}
107.二叉树的层序遍历2
思路:层序遍历是从上到下遍历的,而此题是从下往上,所以就将原来层序遍历获得的集合从后往前遍历加入新集合中就能得到满足条件的集合。
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
List<List<Integer>> resf=new ArrayList<List<Integer>>();
if(root==null){
return resf;
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> list=new ArrayList<Integer>();
int len=queue.size();
while(len>0){
TreeNode nonono=queue.poll();
list.add(nonono.val);
if(nonono.left!=null) queue.offer(nonono.left);
if(nonono.right!=null) queue.offer(nonono.right);
len--;
}
res.add(list);
}
for(int i=res.size()-1;i>=0;i--){
resf.add(res.get(i));
}
return resf;
}
}
199.二叉树的右视图
思路:在层序遍历中,将每一层金队列的最后一个数的集合就是右视图的所有节点。
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
if(root==null){
return list;
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
int len=queue.size();
for(int i=0;i<len;i++){
TreeNode nono=queue.poll();
if(i==len-1){
list.add(nono.val);
}
if(nono.left!=null){
queue.offer(nono.left);
}
if(nono.right!=null){
queue.offer(nono.right);
}
}
}
return list;
}
}
637.二叉树的层平均值
思路:层序遍历将每层入队列的节点都从头开始遍历,然后将遍历到的值叠加起来,最后除以长度,再将这几层的计算结果都加入到集合中。
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> list=new ArrayList<Double>();
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
double sum=0;
int len=queue.size();
for(int i=0;i<len;i++){
TreeNode node=queue.poll();
sum+=node.val;
if(node.left!=null){
queue.offer(node.left);
}
if(node.right!=null){
queue.offer(node.right);
}
}
sum=sum/len;
list.add(sum);
}
return list;
}
}
429.N叉树的层序遍历
基本上同二叉树的层序遍历一样,但是N叉树有n个孩子,如何将所有存在的子孩子加入到队列中是关键!-遍历所有子孩子,再逐个将子孩子都放进队列中。
class Solution {
public List<List<Integer>> res=new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(Node root) {
order(root);
return res;
}
public void order(Node node){
if(node==null){
return;
}
Queue<Node> queue=new LinkedList<Node>();
queue.offer(node);
while(!queue.isEmpty()){
List<Integer> list=new ArrayList<Integer>();
int len=queue.size();
for(int i=0;i<len;i++){
Node no=queue.poll();
list.add(no.val);
if(no.children!=null){
for(Node n:no.children){
queue.offer(n);
}
}
}
res.add(list);
}
}
}
515.在每个树行中找最大值
思路:层序遍历的模板:获得每层的节点加入队列中,然后遍历此队列,每遍历到一个就poll(),然后利用Math.max对每个遍历到的节点值进行比较,和第一个节点的值进行比较的数是先设置:Integer.MIN_VALUE这样一个最小数来先与第一个节点数比较,之后就是每层间的所有节点一一相互比较,最后结束这个循环求出最大值将这个值加入到集合中。
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> list=new ArrayList<Integer>();
if(root==null){
return list;
}
Queue<TreeNode> queue=new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
int res=Integer.MIN_VALUE;
int len=queue.size();
for(int i=0;i<len;i++){
TreeNode no=queue.poll();
res=Math.max(no.val,res);
if(no.left!=null){
queue.offer(no.left);
}
if(no.right!=null){
queue.offer(no.right);
}
}
list.add(res);
}
return list;
}
}
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针2
以下两个在递归里面有递归的办法做,迭代也可以完成!
104.二叉树的最大深度
111.二叉树的最小深度