树的常见面试题

树的常见面试题

1 树的后继节点

node的后继节点指的是中序遍历树之后,排在node后面一个节点

1.1 题型一:求二插搜索树的后继节点(后继者)

题目链接:https://leetcode.cn/problems/successor-lcci/

在这里插入图片描述
思路:

利用二叉搜索树特性:根节点的值大于左子树,根节点的值小于右子树

首先,如果要寻找的p节点的右子树不为null
①p.right != null
直接找p右子树的最左节点,更新successor的值

if(p.right != null){
    //p的右子树不为null -> p的右子树的最左节点
    TreeNode node = p.right;
    while(node.left != null){
        node = node.left;
    }
    successor = node;
} 

②p.right == null
通用代码:

TreeNode node = root;
while(node != null){
	if(p.val < node.val){
		successor = node;
		node = node.left;
	}else{
		//如果p节点在root的先左后右或先右后左,会返回null
		//如果在一直左或一直右,返回其parent
		node = node.right;
	} 
}

详细分析:

p的右子树为null,判断p节点在root节点的左边还是右边
1)如果p在root节点左边:
一直向下找,找到p节点的父节点,
如果是"左右"(root节点左子树的右子树,先左后右):直接返回父节点的父节点
如果是"左左":直接返回父节点

TreeNode node = root;
while(node != null){
	if(p.val < node.val){
		successor = node;
		node = node.left;
	}else{
		//如果p节点在root的先左后右或先右后左,会返回null
		//如果在一直左或一直右,返回其parent
		node = node.right;
	} 
}

2)如果p在root节点的右边:
如果是"右左"(root节点的右子树的左子树):直接返回parent
如果是"右右":直接返回null

TreeNode node = root;
while(node != null){
	if(p.val < node.val){
		successor = node;
		node = node.left;
	}else {
		node = node.right;
	}
}

全部代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        TreeNode successor = null;
        if(p.right != null){
            //p的右子树不为null -> p的右子树的最左节点
            TreeNode node = p.right;
            while(node.left != null){
                node = node.left;
            }
            successor = node;
        } 
        //p的右子树为null
        TreeNode node = root;
        while(node != null){
            if(p.val < node.val){
                //node往左
                successor = node;
                node = node.left;
            } else {
                //p的值大于node的值
                node = node.right;
            }
        }
        return successor;
    }
}

1.2 题型二:node节点多一个属性parent【指向其父亲】

Node结构:

public static class Node{
    private int value;
    private Node left;
    private Node right;
    private Node parent;

    public Node(int value){
        this.value = value;
    }
}

分两种情况:一种node的right为null,一种node的right不为null
思路:
①node.right != null

找到node节点右子树的最左节点【中序:左中右】

//找到最左边节点
public static Node getLeftMost(Node node){
    if(node == null){
        return node;
    }
    while(node.left != null){
        node = node.left;
    }
    return node;
}

②node.right == null

判断node是parent的左子树还是右子树:
如果是左子树,直接返回parent;
如果是右子树,让parent往上找【如果node为root的最右节点,while循环之后会返回null】

Node parent = node.parent;
while(parent != null && parent.right == node){ //当前节点是其父亲节点的右孩子【null】
    node = parent;
    parent = node.parent;
}
//当前节点是其父亲的左孩子
return parent;

全部代码:

public static class Node{
     private int value;
     private Node left;
     private Node right;
     private Node parent;

     public Node(int value){
         this.value = value;
     }
 }

 public static Node getSuccessorNode(Node node){
     if(node == null){
         return null;
     }
     if(node.right != null){
         //node节点的右子树不为null,找到右子树的最左边节点
         return getLeftMost(node.right);
     } else { //无右子树【node:1.左孩子  2.右孩子 】
         Node parent = node.parent;
         while(parent != null && parent.right == node){ //当前节点是其父亲节点的右孩子【null】
             node = parent;
             parent = node.parent;
         }
         //当前节点是其父亲的左孩子
         return parent;
     }
 }
 //找到最左边节点
 public static Node getLeftMost(Node node){
     if(node == null){
         return node;
     }
     while(node.left != null){
         node = node.left;
     }
     return node;
 }

2 验证类题目

2.1 判断树是否为完全二叉树或满二叉树

2.1.1 是否是完全二叉树

完全二叉树是指除最后一层以外,其他层全满,且最后一层叶子节点排布必须是从左到右

先介绍解法二:【通过递归】
思路:创建信息收集类,根据树的递归最后返回结果
①信息收集类Info

//创建信息收集类
 public static class Info {
     //是否是完全二叉树
     public boolean isCBT;
     //是否是满二叉树
     public boolean isFull;
     public int height;

     public Info(boolean isCBT, boolean isFull, int height) {
         this.isCBT = isCBT;
         this.isFull = isFull;
         this.height = height;
     }
 }

②调用process方法返回结果

public static boolean isCBT2(Node head) {
    if (head == null) {
        return true;
    }
    return process(head).isCBT;
}

③处理方法process的编写

//处理类
public static Info process(Node node) {
    if (node == null) {
        //base case:跳出递归
        return new Info(true, true, 0);
    }
    Info leftInfo = process(node.left);
    Info rightInfo = process(node.right);
    //处理高度[+1:本层高度]
    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    //判断是否是满二叉树:左右是否是满,左右高度是否相等
    boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
    //判断是否是完全二叉树:①左右树都为满     ②左子树为满 右子树为完全  ③左子树为完全 右子树为满
    boolean isCBT = false;
    if (isFull) {
        //为满二叉树的树一定是完全二叉树
        isCBT = true;
    } else {
        if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
            isCBT = true;
        }
        if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
            isCBT = true;
        }
        if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
            //左为满,右下面为null
            isCBT = true;
        }
    }
    return new Info(isCBT, isFull, height);
}

解法二的全代码:

//创建信息收集类
public static class Info {
    //是否是完全二叉树
    public boolean isCBT;
    //是否是满二叉树
    public boolean isFull;
    public int height;

    public Info(boolean isCBT, boolean isFull, int height) {
        this.isCBT = isCBT;
        this.isFull = isFull;
        this.height = height;
    }
}

public static boolean isCBT2(Node head) {
    if (head == null) {
        return true;
    }
    return process(head).isCBT;
}

//处理类
public static Info process(Node node) {
    if (node == null) {
        //base case:跳出递归
        return new Info(true, true, 0);
    }
    Info leftInfo = process(node.left);
    Info rightInfo = process(node.right);
    //处理高度[+1:本层高度]
    int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    //判断是否是满二叉树:左右是否是满,左右高度是否相等
    boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
    //判断是否是完全二叉树:①左右树都为满     ②左子树为满 右子树为完全  ③左子树为完全 右子树为满
    boolean isCBT = false;
    if (isFull) {
        //为满二叉树的树一定是完全二叉树
        isCBT = true;
    } else {
        if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
            isCBT = true;
        }
        if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
            isCBT = true;
        }
        if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
            //左为满,右下面为null
            isCBT = true;
        }
    }
    

解法一:【通过队列依次来判断】

//使用队列
public static boolean isCBT1(Node head) {
    if (head == null) {
        return true;
    }
    LinkedList<Node> queue = new LinkedList<>();
    //是否遇到过左右两个孩子不双全的节点[叶子节点]
    boolean leaf = false;
    Node l = null;
    Node r = null;
    queue.add(head);
    while (!queue.isEmpty()) {
        head = queue.poll();
        l = head.left;
        r = head.right;
        if (leaf && (r != null || l != null) || (l == null && r != null)) {
            //遇到了不双全的节点,发现当前节点不是叶节点
            return false;
        }
        if (l != null) {
            queue.add(l);
        }
        if (r != null) {
            queue.add(r);
        }
        if (l == null || r == null) {
            leaf = true;
        }
    }
    return true;
}

最后,通过对数器验证解法二是否正确
全代码:


import java.util.LinkedList;

//判断是否是完全二叉树或满二叉树
public class CBTOrFullDemo {

    //创建节点类
    public static class Node {
        private int value;
        private Node left;
        private Node right;

        public Node(int value) {
            this.value = value;
        }
    }

    //使用队列
    public static boolean isCBT1(Node head) {
        if (head == null) {
            return true;
        }
        LinkedList<Node> queue = new LinkedList<>();
        //是否遇到过左右两个孩子不双全的节点[叶子节点]
        boolean leaf = false;
        Node l = null;
        Node r = null;
        queue.add(head);
        while (!queue.isEmpty()) {
            head = queue.poll();
            l = head.left;
            r = head.right;
            if (leaf && (r != null || l != null) || (l == null && r != null)) {
                //遇到了不双全的节点,发现当前节点不是叶节点
                return false;
            }
            if (l != null) {
                queue.add(l);
            }
            if (r != null) {
                queue.add(r);
            }
            if (l == null || r == null) {
                leaf = true;
            }
        }
        return true;
    }

    //创建信息收集类
    public static class Info {
        //是否是完全二叉树
        public boolean isCBT;
        //是否是满二叉树
        public boolean isFull;
        public int height;

        public Info(boolean isCBT, boolean isFull, int height) {
            this.isCBT = isCBT;
            this.isFull = isFull;
            this.height = height;
        }
    }

    public static boolean isCBT2(Node head) {
        if (head == null) {
            return true;
        }
        return process(head).isCBT;
    }

    //处理类
    public static Info process(Node node) {
        if (node == null) {
            //base case:跳出递归
            return new Info(true, true, 0);
        }
        Info leftInfo = process(node.left);
        Info rightInfo = process(node.right);
        //处理高度[+1:本层高度]
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        //判断是否是满二叉树:左右是否是满,左右高度是否相等
        boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
        //判断是否是完全二叉树:①左右树都为满     ②左子树为满 右子树为完全  ③左子树为完全 右子树为满
        boolean isCBT = false;
        if (isFull) {
            //为满二叉树的树一定是完全二叉树
            isCBT = true;
        } else {
            if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
                isCBT = true;
            }
            if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
                isCBT = true;
            }
            if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
                //左为满,右下面为null
                isCBT = true;
            }
        }
        return new Info(isCBT, isFull, height);
    }

    //定义对数器进行测试
    //for test
    public static Node generateTree(int maxLevel, int maxValue) {
        //从第1层开始
        return generate(1, maxLevel, maxValue);
    }

    //for test
    public static Node generate(int level, int maxLevel, int maxValue) {
        if (level > maxLevel || Math.random() < 0.5) {
            return null;
        }
        Node head = new Node((int) (Math.random() * maxValue));
        head.left = generate(level + 1, maxLevel, maxValue);
        head.right = generate(level + 1, maxLevel, maxValue);
        return head;
    }

    public static void main(String[] args) {
        int maxLevel = 6;
        int maxValue = 100;
        int times = 100_0000;
        for(int i = 0; i < times; i++){
            Node node = generateTree(maxLevel, maxValue);
            if(isCBT1(node) != isCBT2(node)){
                System.out.println("Oops!");
            }
        }
        System.out.println("finish!");
    }
}

2.1.2 是否是满二叉树

同上,同是否是完全二叉树一样,只需要返回isFull信息即可【递归方法】

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值