二叉树相关试题

层次遍历二叉树时,输出完一行的节点后换行。

这里写图片描述

在原有使用队列层次遍历二叉树的基础上,增加两个变量

  • lastNode 指向当前行的最右节点
  • nlastNode 指向下一行的最右节点

此处遍历时,每当输出一个节点后,将把此节点的左右孩子压入队列,压入队列时,让 nlastNode 跟踪入队的节点,然后判断 刚输出的节点是否是当前行的最右节点(node==lastNode?),如果是,就换行,此时 nlastNode 指向的节点一定是下一行的最右节点,将 nlastNode 赋给 lastNode。继续出栈,输出下一行。

    private static void LayerOrder1(TreeNode root) {
        if(root==null){
            System.out.println("empty tree");
        }else{
            Queue<TreeNode> queue=new LinkedList<>();
            queue.add(root);
            TreeNode lastNode=root;//指向当前行的最右节点
            TreeNode nlastNode=root;//指向下一行的最右节点
            while(!queue.isEmpty()){
                TreeNode node=queue.poll();
                System.out.print(node.val);
                if(node.left!=null){
                    nlastNode=node.left;
                    queue.add(node.left);
                }
                if(node.right!=null){
                    nlastNode=node.right;
                    queue.add(node.right);
                }
                if(node==lastNode){
                    System.out.println();
                    lastNode=nlastNode;
                }
            }
        }
    }

变种:要求装到二维数组内返回
有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树。
给定二叉树的根结点root,请返回打印结果,结果按照每一层一个数组进行储存,所有数组的顺序按照层数从上往下,且每一层的数组内元素按照从左往右排列。

由于之前不知道数组的大小,所以先用 ArrayList< ArrayList< Integer>> 最后再赋给数组,返回。要注意二维数组的初始化问题。初始化时,必须指定行数,即指定有几个一维数组, 每个一维数组都是默认初始化值null,在对二维数组赋值前必须初始化每一行的一维数组。

class TreePrinter {
    public int[][] printTree(TreeNode root) {
        if(root==null){
            return null;
        }
        Queue<TreeNode> queue=new LinkedList<TreeNode>();
        queue.add(root);
        ArrayList<ArrayList<Integer>> result=new ArrayList<ArrayList<Integer>>();
        TreeNode lastNode=root;
        TreeNode nlastNode=root;
        ArrayList<Integer> temp=new ArrayList<Integer>();//临时的list 装每一层的元素
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            temp.add(node.val);
            if(node.left!=null){
                nlastNode=node.left;
                queue.add(node.left);
            }
            if(node.right!=null){
                nlastNode=node.right;
                queue.add(node.right);
            }
            if(node==lastNode){
                result.add(temp);
                temp=new ArrayList<Integer>();//此处必须是new 一个,如果只是用clear清空,那以前装入result里的对象也会清空
                lastNode=nlastNode;
            }
        }
        int[][] mresult=new int[result.size()][];//二维数组初始化,必须指定行数,即指定有几个一维数组, 每个一维数组都是默认初始化值null 
        for(int i=0;i<result.size();i++){
            mresult[i]=new int[result.get(i).size()];//在往二维数组里放元素之前,必须初始化这一行的一维数组
            for(int j=0;j<result.get(i).size();j++){
                mresult[i][j]=result.get(i).get(j);
            }
        }
        return mresult;
    }
}

lintcode 71. 二叉树的锯齿形层次遍历

描述
给出一棵二叉树,返回其节点值的锯齿形层次遍历(先从左往右,下一层再从右往左,层与层之间交替进行)

样例
给出一棵二叉树 {3,9,20,#,#,15,7}

    3
   / \
  9  20
    /  \
   15   7

返回其锯齿形的层次遍历为:

[
  [3],
  [20,9],
  [15,7]
]

思路:上题的变种,只需增加一个变量记录行数,偶数行不需要反转,奇数行需要
注意:root 为空时,返回一个 ArrayList<List<Integer>>() 实例,不能返回 null ,如果测试用例不能全部通过,需要考虑这方面问题。

class Solution2 {
    /**
     * @param root: A Tree
     * @return: A list of lists of integer include the zigzag level order traversal of its nodes' values.
     */
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        if(root==null){
            return new ArrayList<List<Integer>>();
        }
        Queue<TreeNode> queue=new LinkedList<TreeNode>();
        queue.add(root);
        List<List<Integer>> result=new ArrayList<List<Integer>>();
        TreeNode lastNode=root;
        TreeNode nlastNode=root;
        List<Integer> temp=new ArrayList<Integer>();//临时的list 装每一层的元素
        int rowNum=0;//记录行数,偶数行不需要翻转,奇数行需要
        while(!queue.isEmpty()){
            TreeNode node=queue.poll();
            temp.add(node.val);
            if(node.left!=null){
                nlastNode=node.left;
                queue.add(node.left);
            }
            if(node.right!=null){
                nlastNode=node.right;
                queue.add(node.right);
            }
            if(node==lastNode){
                if(rowNum%2!=0){
                    Collections.reverse(temp);
                }
                result.add(temp);
                rowNum++;
                temp=new ArrayList<Integer>();
                lastNode=nlastNode;
            }
        }
        return result;
    }
}

LeetCode 236. Lowest Common Ancestor of a Binary Tree

给定一棵二叉树,找到两个节点的最近公共父节点(LCA)。最近公共祖先是两个节点的公共的祖先节点且具有最大深度。

对于下面这棵二叉树

  4
 / \
3   7
   / \
  5   6

LCA(3, 5) = 4
LCA(5, 6) = 7
LCA(6, 7) = 7

思路:
分治递归的思想:
考虑当前状态
(1) 如果 root == null 就是没找到 => return null
(2) 如果 root == p, 找到,返回这个p;如果 root == q, 找到,返回这个q

(1) & (2) 加一起写成:
if (root == null || root == p|| root == q) {
return root;
}
(3) 分别算出left part and right part 搜索 有没有 ancestor
(4) 如果left part and right part 返回的值不是NULL,说明两个值分别在左子树和右子树, 这样的情况 => 公共祖先只能是root,直接返回root
(5) 如果一个有null,一个不为null,那么他们的公共祖先只可能是不为null的子孩子结点,返回另外一个不是 null 的 node

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) {
            return root;
        }
        TreeNode leftNode = lowestCommonAncestor(root.left, p, q);
        TreeNode rightNode = lowestCommonAncestor(root.right, p, q);
        if (leftNode != null && rightNode != null) {
            return root;
        } else {
            return leftNode != null ? leftNode : rightNode;
        }
    }
}

lintcode73. 前序遍历和中序遍历树构造二叉树

根据前序遍历和中序遍历树构造二叉树.
你可以假设树中不存在相同数值的节点

样例

给出中序遍历:[3, 4, 5, 7, 6] 和前序遍历:[4, 3, 7, 5, 6] 返回如下的树:
这里写图片描述

思路:

  1. 根据前序遍历的结果可以知道根结点的位置。
  2. 中序遍历的结果根据根结点的位置,前面是左子树,后面是右子树。
  3. 根据2 可以得到左子树长度,右子树长度,进而可以得到前序序列和中序序列中左右子树的位置:
    1. 前序:[根节点 左子树 右子数]
    2. 中序:[左子树 根结点 右子树]
  4. 中序遍历的任意一个子树都是中序遍历输出,前序也是一样,所以可以递归解决问题。
class Solution {
    /**
     * @param inorder: A list of integers that inorder traversal of a tree
     * @param postorder: A list of integers that postorder traversal of a tree
     * @return: Root of a tree
     */
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int pre_len=preorder.length;
        int in_len=pre_len;//中序长度和前序长度一定相等
        return buildTree(preorder,0,pre_len,inorder,0,in_len);

    }

    // [pre_start,pre_end) 表示该子树前序遍历元素在 preorder[] 中的位置
    // [in_start,in_end) 表示该子树中序遍历元素在 preorder[] 中的位置
    private TreeNode buildTree(int[] preorder, int pre_start, int pre_end, int[] inorder, int in_start, int in_end) {
        if(in_start>=in_end){//递归退出条件
            return null;
        }
        TreeNode root=new TreeNode(preorder[pre_start]);//根结点位置就是前序遍历第一个元素位置
        int i;
        for(i=in_start;i<in_end;i++){//开始在中序遍历序列中找根结点
            if(inorder[i]==preorder[pre_start]){
                break;
            }
        }
        int left_len=i-in_start;
        //递归得到左右子树
        root.left=buildTree(preorder, pre_start+1, pre_start+1+left_len, inorder, in_start, in_start+left_len);
        root.right=buildTree(preorder, pre_start+1+left_len, pre_end, inorder, in_start+left_len+1, in_end);
        return root;
    }
}

lintcode 72. 中序遍历和后序遍历树构造二叉树

描述
根据中序遍历和后序遍历树构造二叉树
样例

给出中序遍历:[3, 4, 5, 7, 6] 和后序遍历:[ 3, 5, 6, 7, 4] 返回如下的树:
这里写图片描述

思路:与上题类似,主要递归时候的坐标边界

class Solution2 {
    /**
     * @param inorder: A list of integers that inorder traversal of a tree
     * @param postorder: A list of integers that postorder traversal of a tree
     * @return: Root of a tree
     */
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        int in_len=inorder.length;
        int post_len=postorder.length;
        return buildTree(inorder,0,in_len,postorder,0,post_len);
    }

    private TreeNode buildTree(int[] inorder, int in_start, int in_end, int[] postorder, int post_start, int post_end) {
        if(in_start>=in_end){//递归退出条件
            return null;
        }
        TreeNode root=new TreeNode(postorder[post_end-1]);//根结点位置就是后序遍历第最后一个元素位置
        int i;
        for(i=in_start;i<in_end;i++){//开始在中序遍历序列中找根结点
            if(inorder[i]==root.val){
                break;
            }
        }
        int left_len=i-in_start;//根据左段的长度,就可以定位左右子树了

        //递归得到左右子树
        root.left=buildTree(inorder,in_start,in_start+left_len, postorder,post_start,post_start+left_len);
        root.right=buildTree(inorder,in_start+left_len+1, in_end,postorder,post_start+left_len,post_end-1);
        return root;
    }
}

lintcode11. 二叉查找树中搜索区间

描述

给定两个值 k1 和 k2(k1 < k2)和一个二叉查找树的根节点。找到树中所有值在 k1 到 k2 范围内的节点。即打印所有x (k1 <= x <= k2) 其中 x 是二叉查找树的中的节点值。返回所有升序的节点值。

样例

如果有 k1 = 10 和 k2 = 22, 你的程序应该返回 [12, 20, 22].

    20
   /  \
  8   22
 / \
4   12

思路:

  1. 查找树的特点:左子树所有结点都小于当前节点,右子树所有结点都大于当前节点。
  2. 用一个容器存放满足条件的值。
  3. 左 中 右子树递归查找满足条件的元素,加入集合。由于查找树的特点,集合中的元素即为升序。
    1. 如果根节点大于k1,那么左子树一定存在满足条件的节点。
    2. 如果跟节点满足条件,则加入。
    3. 如果跟节点小于k2,那么右子树一定存在满足条件的节点。

相当于树的线序变量,只不过在遍历前加了判断条件。

class Solution {
    /**
     * @param root:
     *            param root: The root of the binary search tree
     * @param k1:
     *            An integer
     * @param k2:
     *            An integer
     * @return: return: Return all keys that k1<=key<=k2 in ascending order
     */
    public List<Integer> searchRange(TreeNode root, int k1, int k2) {
        if(root==null){
            return null;
        }
        List<Integer> list = new ArrayList<Integer>();

        if (root.val > k1) {
            List<Integer> leftlist = searchRange(root.left, k1, k2);
            if(leftlist!=null){
                list.addAll(leftlist);
            }
        }
        if (root.val >= k1 && root.val <= k2) {
            list.add(root.val);
        }
        if (root.val < k2) {
            List<Integer> rightlist = searchRange(root.right, k1, k2);
            if(rightlist!=null){
                list.addAll(rightlist);
            }
        }
        //Collections.sort(list);
        return list;
    }
}

如果不想定义多个 ArrayList 接收每个递归的返回值,可以定义一个全局的 ArrayList,并将递归方法设为返回值为 void

public class Solution {
    private ArrayList<Integer> results;

    public ArrayList<Integer> searchRange(TreeNode root, int k1, int k2) {
        results = new ArrayList<Integer>();
        helper(root, k1, k2);
        //Collections.sort(results);
        return results;
    }

    private void helper(TreeNode root, int k1, int k2) {
        if (root == null) {
            return;
        }

        if (root.val > k1) {
            helper(root.left, k1, k2);
        }
        if (root.val >= k1 && root.val <= k2) {
            results.add(root.val);
        }
        if (root.val < k2) {
            helper(root.right, k1, k2);
        }
    }
}

lintcode 95. 验证二叉查找树

描述
给定一个二叉树,判断它是否是合法的二叉查找树(BST)

一棵BST定义为:

  • 节点的左子树中的值要严格小于该节点的值。
  • 节点的右子树中的值要严格大于该节点的值。
  • 左右子树也必须是二叉查找树。
  • 一个节点的树也是二叉查找树。

样例

  2
 / \
1   4
   / \
  3   5

方法一:如果是二叉排序树,中序遍历得到序列一定是递增的,遍历之,存到一个ArrayList中,判断这个list是否是递增的。

用中序遍历的递归算法:

public class Solution {
    /**
     * @param root:
     *            The root of binary tree.
     * @return: True if the binary tree is BST, or false
     */
    private List<Integer> list=new ArrayList<Integer>();
    public boolean isValidBST(TreeNode root) {
        InOrder(root);
        for(int i=0;i<list.size()-1;i++){
            if(list.get(i)>=list.get(i+1)){
                return false;
            }
        }
        return true;
    }

    private void InOrder(TreeNode root) {
        if(root==null){
            return;
        }
        InOrder(root.left);
        list.add(root.val);
        InOrder(root.right);
    }
}

方法二:用中序遍历的非递归算法,不需要再用一个list,可以在遍历过程中判断是否大于前一个数:

class Solution2 {
    /**
     * @param root:
     *            The root of binary tree.
     * @return: True if the binary tree is BST, or false
     */
    public boolean isValidBST(TreeNode root) {
        if (root == null || (root.left == null && root.right == null)) {
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();

        int lastval = Integer.MIN_VALUE;//记录上一个节点值,即为它即子节点最大的值
        int firsttime=1;//用于判断是否是中序遍历的第一的节点,如果是第一个就不用判断是否大于他上个节点了
        while (root != null || !stack.empty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            if (!stack.empty()) {
                TreeNode node = stack.pop();
                if(firsttime==1){
                    lastval = node.val;
                    firsttime=0;
                }else{
                    if (lastval >= node.val) {
                        return false;
                    }
                    lastval = node.val;
                }
                root = node.right;
            }
        }
        return true;
    }

}

猜你喜欢

转载自blog.csdn.net/zxm1306192988/article/details/80397138
今日推荐