算法题型:二叉树(leetcode+剑指offer)

目录

一、111. 二叉树的最小深度

 二、100. 相同的树

三、101. 对称二叉树

四、110. 平衡二叉树

五、剑指offer(24)二叉树中和为某一值的路径

六、剑指offer(三十八)计算二叉树的最大深度

七、剑指offer(五十六)二叉树的下一个节点

八、剑指offer(五十八)按照Z型打印二叉树

九、剑指offer(五十九)层序遍历二叉树(改进版,需存进双层链表中)

十、剑指offer(六十)序列化二叉树

十一、剑指offer(六十一)二叉树的第K个节点


一、111. 二叉树的最小深度


1.题目描述
评论 (282)
题解(30)New
提交记录
给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最小深度  2.

2.分析:

刚开始我的方法是:
 

public int minDepth(TreeNode root) {
        if (root==null)return 0;
        return Math.min(1+minDepth(root.left),1+minDepth(root.right));
    }

乍一看没什么毛病,提交之后发现
    1
   /  \
2      null
这种情况,我的代码返回1,而正确答案是2。
原来,题目要求的是根节点到叶子节点的最小距离,而1->null,不属于这个范畴,因为1不是叶子节点。

---->于是重新冷静分析一下:
题目应该分成以下四种情况:
(1)当前节点为null,return 0;

(2)左孩子节点与右孩子节点都为null,代表当前节点为叶子节点,return 1;

(3)左孩子或者右孩子节点有一个为空,此时只要递归调用不为空的孩子节点,这样才能到达叶子节点

(4)左右孩子都不为空,此时需要左右孩子同时递归,并且判断二者返回结果较小的一方。

通过以上分析,我们将(3)(4)两点通过if语句整合在一起:

3.代码如下:

public class Solution111 {
    public int minDepth(TreeNode root) {
        if (root==null)return 0;
        if (root.left==null&&root.right==null)return 1;
        int min = Integer.MAX_VALUE;
        if (root.left!=null){
            min=Math.min(min,minDepth(root.left));
        }
        if (root.right!=null){
            min=Math.min(min,minDepth(root.right));
        }
        return min+1;
    }
}

 二、100. 相同的树

1.题目描述
给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:  1         1
           / \       / \
         2   3   2   3

        [1,2,3],   [1,2,3]

输出: true
示例 2:

输入:  1        1
          /           \
         2             2

        [1,2],     [1,null,2]

输出: false
示例 3:

输入:  1        1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

输出: false

2.分析:

看代码,分为四种情况,然后递归判断。

3.代码:

class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p==null&&q==null)return true;
        //if (p==null&&q!=null)return false;
        //if (q==null&&p!=null)return false;
        if (p==null||q==null)return false;
        if(p.val!=q.val)return false;
        return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
    }
}

三、101. 对称二叉树

1.题目描述
给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3
 

2.分析:

核心思想是判断左子树的右子树右子树的左子树是否相等  以及 左子树的左子树右子树的右子树 是否相等

由于是判断对称,所以需要存储左右子树的信息,因此我们需要再创建一个函数,用来判断是否对称。

3.代码:

public class Solution101 {
    public boolean isSymmetric(TreeNode root) {
        return isMirror(root,root);
    }
    public boolean isMirror(TreeNode left,TreeNode right){
        if (left==null&&right==null)return true;
        if (left==null||right==null)return false;//在第一个判断的基础上,第二个判断的真实含义就变成:只有一个节点为null
        boolean valEqul = left.val==right.val;
        boolean mirror = isMirror(left.left,right.right)//左子树的左子树,右子树的右子树
                &&isMirror(left.right,right.left);//左子树的右子树,右子树的左子树,是否对称?
        return valEqul&&mirror;
    }
}

四、110. 平衡二叉树

1.题目描述

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

2.分析:

(方法1):
high函数用于计算当前传入节点的高度值,isBalanced函数递归调用左右子树,并且判断左右子树高度差

(方法2):

从底层往上的过程中,每一次都判断是否不符合条件(即renturn -1),如果不符合,会一路返回-1,如果全部符合,才返回值。

3.代码: 

/**
 * IMUHERO
 * 2019/8/1
 */
package IMUHERO;

//public class Solution110 {
//    public boolean isBalanced(TreeNode root) {
//        if (root==null)return true;
//        return Math.abs(depth(root.left) - depth(root.right)) <= 1 
//               && isBalanced(root.left) 
//               && isBalanced(root.right);
//    }

//    public int high(TreeNode root){
//        if (root==null)return 0;
//        return Math.max(1+high(root.left),1+high(root.right));
//    }
//}

//方法二:
public class Solution110 {
    public boolean isBalanced(TreeNode root) {
        return high(root)!=-1;
    }
    //以下事实上就是计算深度
    public int high(TreeNode root){
        if (root==null)return 0;
        int left = high(root.left);
        if (left==-1)return -1;
        int right = high(root.right);
        if (right==-1)return -1;
        //只要从底层来的判断,有一个为-1,则不再递归,直接返回-1;
        return (Math.abs(left-right)<=1)?(Math.max(left,right)+1):-1;
    }
}

五、剑指offer(24)二叉树中和为某一值的路径

1.题目描述

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

2.分析:

见注释,主要是递归判断target是否为0,满足条件就添加进listAll中。不管是否添加,都要将队尾删除,回退到根节点,然后去另一个节点判断。

3.代码:

//存储计算结果
    ArrayList> listAll = new ArrayList<>();
    //存储计算过程值
    ArrayListlist = new ArrayList<>();
    public ArrayList> FindPath(TreeNode root, int target) {
        if (root==null)return listAll;
        //每到一个节点,减去节点的值,target为0时为正确结果
        target-=root.val;
        list.add(root.val);
        //注意题意是叶子结点,因此还要有后面两个判断
        if (target==0&&root.left==null&&root.right==null){
            //注意此处是new了一个list,不能直接listAll.add(list),否则是一直指向同一个链表了
            listAll.add(new ArrayList(list));
            //注意此处不能直接return,因为底下还要删除队尾元素,以便进行下一次判断
            //return listAll;
        }
        //递归遍历所有情况
        FindPath(root.left,target);
        FindPath(root.right,target);
        //删除队尾元素,下一次判断相当于回退到根节点,判断另一个子节点。
        list.remove(list.size()-1);
        return listAll;
    }

六、剑指offer(三十八)计算二叉树的最大深度

1.题目:

计算二叉树的最大深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)return 0;
        int left=TreeDepth(root.left)+1;
        int right=TreeDepth(root.right)+1;
        return Math.max(left,right);
    }
}

七、剑指offer(五十六)二叉树的下一个节点


1题目描述
给定一个二叉树和其中的一个结点,
请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
 

2.我的方法:(不建议)

使用一个链表存储中序遍历的所有节点,然后按顺序遍历
如果当前节点等于题目所给的节点,那么当前节点之后的节点就是我们需要的答案。

56二叉树的下一个节点
题目描述
给定一个二叉树和其中的一个结点,
请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。


package IMUHERO;

import java.util.LinkedList;

class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;
    TreeLinkNode(int val) {
        this.val = val;
    }
}

public class Sword56_GetNext {
    LinkedList list =new LinkedList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode==null)return null;
        //找到根节点
        TreeLinkNode cur = pNode;
        while(cur.next!=null){
            cur=cur.next;
        }
        TreeLinkNode root = cur ;
        midOrder(root);
        TreeLinkNode ret = null ;
        for(int i=0;i

3.别人的方法(推荐)

算法题型:二叉树(leetcode+剑指offer)_第1张图片
思路:

(1) 若该节点存在右子树:则下一个节点为右子树最左子节点(如图节点 B )

(2) 若该节点不存在右子树:这时分两种情况:

2.1 该节点为父节点的左子节点,则下一个节点为其父节点(如图节点 D )

2.2 该节点为父节点的右子节点,则沿着父节点向上遍历,知道找到一个节点的父节点的左子节点为该节点,则该节点的父节点下一个节点(如图节点 I ,沿着父节点一直向上查找找到 B ( B 为其父节点的左子节点),则 B 的父节点 A 为下一个节点)。

package IMUHERO;
public class Solution {    
    public TreeLinkNode GetNext(TreeLinkNode pNode) {     
        if (pNode == null)       
            return pNode;    
        if (pNode.right != null) {
            // 节点有右子树            
            pNode = pNode.right;         
            while (pNode.left != null) {  
                pNode = pNode.left;          
            }            return pNode;    
        } 
        else if ( pNode.next != null && pNode.next.left == pNode) { 
            // 节点无右子树且该节点为父节点的左子节点         
            return pNode.next; 
        } else if (pNode.next != null && pNode.next .right == pNode) { 
            // 节点无右子树且该节点为父节点的右子节点         
            while(pNode.next != null && pNode .next .left != pNode){       
                pNode = pNode.next ;       
            }           
            return pNode.next ;     
        }else{        
            return pNode.next ;
            //节点无父节点 ,即节点为根节点       
            }  
    }
}

八、剑指offer(五十八)按照Z型打印二叉树

1.题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

2.分析:

1.使用两个栈S1和S2存储奇数层和偶数层节点

2.奇数层从左到右push进左右子树(出队时就可以是先右后左了)

3.偶数层从右到左push进左右子树(出队时就可以是先左后右)

package IMUHERO;
import java.util.ArrayList;
import java.util.Stack;

public class Sword58_PrintZ {
    package IMUHERO;

import java.util.ArrayList;
import java.util.Stack;

public class Solution {
    public static ArrayList> Print(TreeNode pRoot) {
        ArrayList>res = new ArrayList<>();
        if (pRoot==null)return res;
        Stack stack1 = new Stack<>();
        Stack stack2 = new Stack<>();
        int deepth = 1;
        stack1.push(pRoot);
        while (!stack1.isEmpty()||!stack2.isEmpty()){
            //奇数层
            if (deepth%2!=0){
                ArrayListlist1 = new ArrayList<>(); //用来暂存结果
                while (!stack1.isEmpty()){
                    TreeNode cur = stack1.pop();
                    list1.add(cur.val);
                    if (cur.left!=null){
                        stack2.push(cur.left);
                    }
                    if (cur.right!=null){
                        stack2.push(cur.right);
                    }
                }
                if (!list1.isEmpty()){
                    res.add(list1);
                    deepth++;
                }
            }
            //偶数层
            else {
                ArrayListlist2 = new ArrayList<>(); //用来暂存结果
                while (!stack2.isEmpty()){
                    TreeNode cur = stack2.pop();
                    list2.add(cur.val);
                    if (cur.right!=null){
                        stack1.push(cur.right);
                    }
                    if (cur.left!=null){
                        stack1.push(cur.left);
                    }
                }
                if (!list2.isEmpty()){
                    res.add(list2);
                    deepth++;
                }
            }
        }
        return res;
    }
}
}

九、剑指offer(五十九)层序遍历二叉树(改进版,需存进双层链表中)

1.题目描述

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

2.分析:

(1)我的方法:

1.使用一个自定义类Pair存储深度deepth和节点信息。

2.按照通常的方法层序遍历,并在添加的时候加一层判断,根据深度存储在链表的不同位置。

import java.util.ArrayList;
import java.util.LinkedList;

public class Solution {
    ArrayList > Print(TreeNode pRoot) {
        ArrayList> res = new ArrayList<>();  //用于返回结果
        if(pRoot==null)return res;                      //特殊情况,根节点为空则直接返回空表
        LinkedList list = new LinkedList<>();     //用于遍历,存储Pair
        list.addLast(new Pair(0,pRoot));        //先添加第一个节点

        while(!list.isEmpty()){
            Pair cur = list.removeFirst();
            int deep = cur.deepth;
            //如果该表还没有初始化,则初始化
            if(res.size()<=deep){
                ArrayList al = new ArrayList<>();
                al.add(cur.node.val);
                res.add(deep,al);
            }
            else{
                res.get(deep).add(cur.node.val);
            }//已经初始化的直接往后添加
            if(cur.node.left!=null){
                list.addLast(new Pair(deep+1,cur.node.left));
            }
            if(cur.node.right!=null){
                list.addLast(new Pair(deep+1,cur.node.right));
            }
        }
        return res;
    }
    private class Pair{
        int deepth;
        TreeNode node;
        public Pair(int deepth,TreeNode node){
            this.deepth = deepth;
            this.node = node;
        }
    }
}

(2)其他方法:

与上题的不同在于,将两个栈变为两个队列,即实现了不同的共能

当然,在具体偶数层的时候也是不一样,前一题是变为先入右子树,再入左子树,这题则无需改变

package IMUHERO;

        import java.util.ArrayList;
        import java.util.LinkedList;
public class Solution {
    ArrayList > Print(TreeNode pRoot) {
        ArrayList>res = new ArrayList<>();
        if (pRoot==null)return res;
        LinkedList queue1 = new LinkedList<>();
        LinkedList queue2 = new LinkedList<>();
        int deepth = 1;
        queue1.add(pRoot);
        while (!queue1.isEmpty()||!queue2.isEmpty()){
            //奇数层
            if (deepth%2!=0){
                ArrayListlist1 = new ArrayList<>(); //用来暂存结果
                while (!queue1.isEmpty()){
                    TreeNode cur = queue1.pop();
                    list1.add(cur.val);
                    if (cur.left!=null){
                        queue2.add(cur.left);
                    }
                    if (cur.right!=null){
                        queue2.add(cur.right);
                    }
                }
                if (!list1.isEmpty()){
                    res.add(list1);
                    deepth++;
                }
            }
            //偶数层
            else {
                ArrayListlist2 = new ArrayList<>(); //用来暂存结果
                while (!queue2.isEmpty()){
                    TreeNode cur = queue2.pop();
                    list2.add(cur.val);
                    if (cur.left!=null){
                        queue1.add(cur.left);
                    }
                    if (cur.right!=null){
                        queue1.add(cur.right);
                    }
                }
                if (!list2.isEmpty()){
                    res.add(list2);
                    deepth++;
                }
            }
        }
        return res;
    }
}

十、剑指offer(六十)序列化二叉树

1.题目描述
请实现两个函数,分别用来序列化和反序列化二叉树 
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,
从而使得内存中建立起来的二叉树可以持久保存。
序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,
序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),
以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。

2.分析

1.序列化:采用递归前序遍历,将val添加到StringBuffer中,“,”进行分隔,“#”代表null

2.反序列化:类似于二叉树添加节点的操作,使用递归返回结果,并用左右子树接住改变。

3.注意此解法的灵活运用,如index设立在外围,并且初值为-1,node初值设立为null,if语句过滤掉null,如果到达Null,直接返回node

package IMUHERO;

public class Sword65_robot_moving {
    int index = -1;
    String Serialize(TreeNode root) {
        StringBuffer stb = new StringBuffer();
        if(root==null){
            stb.append("#,");
            return stb.toString();
        }
        stb.append(root.val);
        stb.append(",");
        stb.append(Serialize(root.left));
        stb.append(Serialize(root.right));
        return stb.toString();
    }
    TreeNode Deserialize(String str) {
        index++;
        TreeNode node = null;
        String [] s = str.split(",");
        if(!s[index].equals("#")){
            node = new TreeNode(Integer.parseInt(s[index]));
            node.left = Deserialize(str);
            node.right = Deserialize(str);
        }
        return node;
    }
}

 

十一、剑指offer(六十一)二叉树的第K个节点

1.题目描述

给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)    中,按结点数值大小顺序第三小结点的值为4。

2.分析:

1.二叉树的中序遍历就是从小到大排序的,所以只要找到中序遍历的第K个节点即可 

2.可以使用链表存储节点

3.要注意边界条件,K可能不是一个有效值

import java.util.ArrayList;
public class Solution {
    ArrayList list = new ArrayList<>();
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        inOrder(pRoot,k);
        if(k>0&&list.size()>=k){
            return list.get(k-1);
        }
        else return null;
    }
    private void inOrder(TreeNode pRoot,int k){
        if(pRoot==null)return;
        inOrder(pRoot.left,k);
        list.add(pRoot);
        if(list.size()==k)return;
        inOrder(pRoot.right,k);
    }
}

 

你可能感兴趣的:(算法学习)