目录
作为一名程序猿,树这种结构是我们必须要掌握的,关于树的基本操作可以参考之前的博文
https://blog.csdn.net/szy2333/article/details/88804090
今天主要是对剑指offer中的树是相关题目进行整理
首先树的基本结构定义为:
public class TreeNode {
public Integer val;
public TreeNode left;
public TreeNode right;
public TreeNode(Integer v) {
val = v;
}
}
二叉树的深度
解题思路:
使用递归的方式
当树为空的时候,返回0
如果树只有左子树没有右子树,那么树的深度为左子树的深度+1
如果树只有右子树没有左子树,那么树的深度为右子树的深度+1
如果该树既有左子树又有右子树,那么深度为左子树和右子树的最大值+1
代码实现:
public class TreeDepth {
public int TreeDepth(TreeNode root) {
if(root == null) {
return 0;
}
int left = TreeDepth(root.left);
int right= TreeDepth(root.right);
return Math.max(left, right) + 1;
}
}
也可以使用非递归的方式,即 使用层数遍历,设置一个变量来记录层数,思路和代码可以参考题目把二叉树打印成多行
重建二叉树
解题思路:
根据不同遍历方式的不同特点
前序遍历:根左右 ---> 第一个结点一定是根节点
中序遍历:左根右 ---> 第一个结点一定是左子节点
后序遍历:左右根 ---> 最后一个结点一定是根节点
从本题目出发,知道前序遍历和中序遍历分别为
前: 1 2 4 7 3 5 6 8
中: 4 7 2 1 5 3 8 6
根节点右边是同样的方法,这样通过递归就可以实现二叉树的重构啦
同样的,我们可以想到,只要知道任意两种遍历方式,就可以进行二叉树的重构
代码实现:
import java.util.Arrays;
public class RebuildTreeNode {
public TreeNode reBuildBinaryTree(int[] pre, int[] in) {
if(pre.length == 0 || in.length == 0) {
return null;
}
TreeNode root = new TreeNode(pre[0]);
for(int i = 0; i < pre.length; i++) {
if(pre[0] == in[i]) {
root.left = reBuildBinaryTree(Arrays.copyOfRange(pre, 1, i + 1),
Arrays.copyOfRange(in, 0, i));
root.right = reBuildBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length),
Arrays.copyOfRange(in, i + 1, pre.length));
break;
}
}
return root;
}
}
树的子结构
解题思路:
所谓树的子结构:B是A的一部分,则B为A的子结构,如下图中,圈出的部分都是大树的子结构
首先比较A,B的根节点是否相同,
如果相同,则分别比较B的左右子结点是否为null或等于A的左右子结点
如果不同,则判断B的根节点和A的左子节点是否相同,
如果相同,则分别比较B的左右子结点是否为null或等于A的左子节点的左右子结点
如果不同,则判断B的根节点和A的右子节点是否相同,
如果相同,则分别比较B的左右子结点是否为null或等于A的右子节点的左右子结点
...............
不断递归,得到结果
代码实现:
public class HasSubtree {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
boolean result = false;
if(root1 != null && root2 != null) {
if(root1.val == root2.val) {
result = isSubtree(root1, root2);
}
if(!result) {
result = isSubtree(root1.left, root2);
}
if(!result) {
result = isSubtree(root1.right, root2);
}
}
return result;
}
public boolean isSubtree(TreeNode root1, TreeNode root2) {
if(root2 == null) {
return true;
}
if(root1 == null) {
return false;
}
if(root1.val != root2.val) {
return false;
}
return isSubtree(root1.left, root2.left) && isSubtree(root1.right, root2.right);
}
}
二叉树的镜像
解题思路:
通过输入描述中的图片,我们可以看出,所谓镜像,就是:
树的左子树成为了右子树,右子树成为了左子树,并且每个子树都遵循同样的原则
通过递归可以实现
代码实现:
public class Mirror {
public void Mirror(TreeNode root) {
if(root == null) {
return;
}
TreeNode left = root.left;
TreeNode right = root.right;
root.left = right;
root.right = left;
Mirror(root.left);
Mirror(root.right);
}
}
对称的二叉树
解题思路1:
如果二叉树对称,那么其本身和自己的镜像树应该是一样的
首先调用镜像树,然后遍历结点,依次进行比较即可
解题思路2:
使用队列的方式对结点进行存储,由于队列先进先出,那么将节点以对称位置的方式存入,每次取出两个进行比较
首先将根节点的左右结点值存入,当队列不为空时,每次取出两个值
如果结点值不相同,则树不是对称的,返回false
否则,分别将左结点的左结点和右结点,右结点的右结点和左结点依次存入,然后重复操作
代码实现:
import java.util.LinkedList;
import java.util.Queue;
public class IsSymmetrical {
boolean isSymmetrical(TreeNode pRoot) {
if(pRoot == null) {
return true;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(pRoot.left);
queue.offer(pRoot.right);
while (!queue.isEmpty()) {
TreeNode left = queue.poll();
TreeNode right = queue.poll();
if(left == null && right == null) continue;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
queue.offer(left.left);
queue.offer(right.right);
queue.offer(left.right);
queue.offer(right.left);
}
return true;
}
}
从上往下打印二叉树
解题思路:
其本质就是层序遍历,然后将遍历的值依次输出
将元素存储在list中,并利用队列这种先进先出的结构特点进行操作
首先将根节点存入队列,如果队列不为空,则进行操作
得到队列的第一个结点元素node,将元素值存储于list中,然后让node的左右子结点分别进入队列并移除node
代码实现:
import java.util.ArrayList;
public class PrintFromTopToBottom {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
ArrayList<TreeNode> queue = new ArrayList<>();
if(root == null) {
return list;
}
queue.add(root);
while (queue.size() != 0) {
TreeNode node = queue.remove(0);
if(node.left != null) {
queue.add(node.left);
}
if(node.right != null) {
queue.add(node.right);
}
list.add(node.val);
}
return list;
}
}
把二叉树打印成多行
解题思路:
和上一个题原理相同,只不过稍微复杂了一点
需要按照每一层分别进行存储,那么是如何判断层数发生变化了呢?
我们采用list进行所有结点的存储,设置标志位start和end,end为list的大小,list的初始元素为根节点
当list不为空时
start从0开始,end = list.size(), 每次移除一个元素,start++,并将该元素的左右子结点存入,直至start等于end时,本层结束,进入下一层,此时start重新赋值为0,end也重新赋值为list.size(), 不断循环
代码实现:
import java.util.ArrayList;
public class PrintTreeByLayer {
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> resultList = new ArrayList<>();
if(pRoot == null) {
return resultList;
}
ArrayList<TreeNode> layer = new ArrayList<>();
ArrayList<Integer> layerList = new ArrayList<>();
layer.add(pRoot);
int start = 0;
int end = 1;
while (!layer.isEmpty()) {
TreeNode node = layer.remove(0);
layerList.add(node.val);
start++;
if(node.left != null) {
layer.add(node.left);
}
if(node.right != null) {
layer.add(node.right);
}
if(start == end) {
start = 0;
end = layer.size();
resultList.add(layerList);
layerList = new ArrayList<Integer>();
}
}
return resultList;
}
}
二叉搜索树的第k个结点
解题思路:
二叉搜索树就是:任何一个根节点的左子节点的值都比自己小,右子结点的值都比自己大
如果使用中序遍历,那么得到的结果刚好是按顺序排列的
根据这种特点,我们将树中的结点按照中序遍历的顺序存放入list中,最后取下标为k - 1的值即可
不过注意:不需要进行所有结点的遍历,在list的大小远远大于k的值时,就会大大提高效率,不需要对所有结点进行遍历
代码实现:
import java.util.ArrayList;
public class Solution {
TreeNode KthNode(TreeNode pRoot, int k) {
if(pRoot == null || k <= 0) {
return null;
}
ArrayList<TreeNode> list = new ArrayList<>();
in(pRoot, list, k);
if(k > list.size()) {
return null;
}
return list.get(k - 1);
}
public static void in(TreeNode pRoot, ArrayList<TreeNode> list, int k) {
if(pRoot == null) {
return;
}
in(pRoot.left, list, k);
list.add(pRoot);
if(list.size() == k) {
return;
}
in(pRoot.right, list, k);
}
}