在学习二叉树的时候,也去写了一些相关的题目,作为学习笔记记录解题思路。
第一题:LeetCode,100. 相同的树 - 力扣(LeetCode)。
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
这里我将题目的解题思路分成3个阶段:
1:先去检查根节点,是否为空,然后再对比是否相等。
2:使用递归,检查左孩子是否为空,是否相等。
3:使用递归,检查右孩子是否为空,是否相等。
以下是题解核心代码:
public boolean isSameTree(TreeNode p, TreeNode q){
if(p == null && q == null){
return true;
}
if((p != null && q == null)|| ( p == null && q !=null)){
return false;
}
if( p.val != q.val){
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
第二题:LeetCode,572. 另一棵树的子树 - 力扣(LeetCode)。
给你两棵二叉树 root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。
二叉树 tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。
题目的解题思路分为四个阶段:
1:先进行根节点的判断,如果不为空,且相同,则比较左孩子和右孩子是否相同。
2:如果根节点不相同,则将root的左子树与subRoot比较,从左子树的根节点开始进行比较。
3:如果左子树遍历完后还是不相同,在于root的右子树进行比较。
4:因为核心还是比较二叉树各个节点是否相同,因此可以使用上一个题目的代码。
以下是题解核心代码:
public boolean isSubtree(TreeNode root, TreeNode subRoot){
if(root == null || subRoot == null ){
return false;
}
if(isSameTree(root,subRoot){
return true;
}
if(isSameTree(root.left,subRoot){
return true;
}
if(isSameTree(root.right,subRoot){
return true;
}
return false;
}
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null){
return true;
}
if((p != null && q == null) || (p == null && q != null) ){
return false;
}
if(p.val != q.val){
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
第三题:LeetCode,226. 翻转二叉树 - 力扣(LeetCode)。
你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
题目的解题思路分为两个阶段:
1:翻转二叉树,根节点不翻转,翻转左子树和右子树
2:不断递归,直至整颗二叉树翻转完成。
public TreeNode invertTree(TreeNode root){
if(root == null){
return null;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
第四题:LeetCode,110. 平衡二叉树 - 力扣(LeetCode)。
给定一个二叉树,判断它是否是
平衡二叉树
这题解题思路分为两个阶段:
1:最核心的是平衡二叉树,平衡的条件是左子树减去右子树的高度的绝对值小于等于1。
2:先去计算出左子树的高度,然后是右子树的高度,进行相减,如果值小于等于1则是平衡二叉树。
public boolean isBalanced(TreeNode root){
if(root == null){
return true;
}
return getHeight(root) >=0;
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int leftHeight = getHeight(root.left);
if(leftHeight < 0){
return -1;
}
int rightHeight = getHeight(root.left);
if(Math.abs(leftHeight - rightHeight) <= 1 &&
leftHeight >=0 && rightHeight >= 0){
return Math.max(leftHeight,rightHeight)+1;
}else{
return -1;
}
}
第五题:LeetCode,对称二叉树101. 对称二叉树 - 力扣(LeetCode)对称二叉树。
给你一个二叉树的根节点 root
, 检查它是否轴对称。
这题解题思路分为阶段:
1:轴对称的二叉树,在第二层的左子树的左孩子等于右子树的右孩子,左子树的右孩子等于右子树的左孩子。
2:使用递归进行解题。
public boolean isSymmetric(TreeNode root) {
if(root == null){
return false;
}
return isSymmetricChild(root.left,root.right);
}
public boolean isSymmetricChild(TreeNode p,TreeNode q){
if((p != null && q == null) || (p == null && q != null)){
return false;
}
if(p == null && q == null){
return true;
}
if(p.val != q.val){
return false;
}
return isSymmetricChild(p.left,q.right)&&
isSymmetricChild(p.right,q.left);
}
第六题:牛客网,二叉树遍历_牛客题霸_牛客网 (nowcoder.com)。
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
这题解题思路:
由于牛客没定义树节点,因此先由自己定义树节点,由于是字符串输入,因此要使用hasNextLine。
因为题目给的字符串是前序遍历的顺序,#代表空树,因此用一个字符串接受题目输入的字符串,然后写出一个前序遍历的方法,在这方法用while循环遍历字符串,在循环中再次调用前序遍历的方法进行构造二叉树。最后返回根节点,然后再写一个中序遍历的方法接收根节点,最后用调用中序遍历进行打印。
import java.util.Scanner;
class TreeNode{
public TreeNode left;
public TreeNode right;
public char val;
public TreeNode(char val){
this.val = val;
}
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static int i = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String str = in.nextLine();
TreeNode root =createTree(str);
inorder(root);
}
}
public static TreeNode createTree(String str){
//1.遍历字符串str
// for(int i = 0;i < str.length(); i++){
// char ch = str.charAt(i);
// }
TreeNode root = null;
if(str.charAt(i) != '#'){
//2.根据前序遍历创建二叉树
root = new TreeNode(str.charAt(i));
i++;
root.left = createTree(str);
root.right = createTree(str);
}else{
i++;
}
//3.返回根节点
return root;
}
public static void inorder(TreeNode root){
if(root == null){
return;
}
inorder(root.left);
System.out.print(root.val+ " ");
inorder(root.right);
}
}
第七题:LeedCod,102. 二叉树的层序遍历 - 力扣(LeetCode)。
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
这个题目的解题思路:
层序遍历的话,其实要思考的是怎么去存储每一层的节点,并且题目很有意思的是必须要用二维数组的形式进行输出,所以这边得用数组去存储层序遍历出来的每一层的节点,如何去存储每一层的节点呢,用队列就好,先进先出的特性可以很好的存储每一层的节点。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if (root == null) {
return ret;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> tmp = new ArrayList<>();
while (size != 0) {
TreeNode cur = queue.poll();
//System.out.println(cur.value + " ");
tmp.add(cur.val);
size--;
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
ret.add(tmp);
}
return ret;
}
}
第八题:LeedCod,236. 二叉树的最近公共祖先 - 力扣(LeetCode)。
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
这题的解题思路:
想要求两个节点的最近祖先,我们可以利用栈这个数据结构,先去树中找到p和q这两个节点的位置,在遍历的过程中,将p和q的所有的父亲节点存储到栈当中,不是的就删除,这样栈中存下来的就包含了两个p和q的共同的最近祖先节点,然后将两个栈进行对比,大的就先出栈,直到两个栈中的节点数量保持一致,最后共同出栈,直到出栈的节点相同,那么这就是他们的最近公共祖先。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
if(root == null){
return null;
}
Stack<TreeNode> StackP = new Stack<>();
Stack<TreeNode> StackQ = new Stack<>();
getPath(root, p, StackP);
getPath(root, q, StackQ);
int sizeQ = StackQ.size();
int sizeP = StackP.size();
if(sizeQ > sizeP){
int size = sizeQ - sizeP;
while(size != 0){
StackQ.pop();
size--;
}
}else{
int size = sizeP - sizeQ;
while(size != 0){
StackP.pop();
size--;
}
}
while(!StackP.isEmpty() && !StackQ.isEmpty()){
if(StackP.peek() == StackQ.peek()){
return StackP.peek();
}else{
StackP.pop();
StackQ.pop();
}
}
return null;
}
private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack){
if(root == null || node ==null){
return false;
}
stack.push(root);
if(root == node){
return true;
}
boolean flg = getPath(root.left,node,stack);
if(flg == true){
return true;
}
boolean flg2 = getPath(root.right,node,stack);
if(flg2 == true){
return true;
}
stack.pop();
return false;
}
}
第九题:LeedCod,105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
这题的解题思路:
先序遍历,第一个节点一定是根节点,中序遍历中找到根节点的位置,这样在中序遍历中根节点的左边是左子树,右边是右子树。通过先序遍历的节点一个一个从中序遍历中找出来,然后按照根左右的顺序进行构造。核心是如何确定先序遍历中怎么跳出遍历,一步一步往下走。
class Solution {
int preIndex;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeChild(preorder,inorder,0,inorder.length-1);
}
private TreeNode buildTreeChild(int[] preorder,int[] inorder,int inbegin, int inend){
if(inbegin > inend){
return null;
}
TreeNode root = new TreeNode(preorder[preIndex]);
int rootIndex = findIndex(inorder,inbegin,inend,preorder[preIndex]);
preIndex++;
root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex - 1);
root.right = buildTreeChild(preorder,inorder,rootIndex + 1,inend);
return root;
}
private int findIndex(int[] inorder,int inbegin,int inend,int key){
for(int i = inbegin;i <= inend;i++){
if(inorder[i] == key){
return i;
}
}
return -1;
}
}
第十题:LeedCod,106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
这题的解题思路:
跟第九题一样,从先序遍历变成了后序遍历,由于后序遍历最后一个才是根节点,所以得从最后一个往前开始遍历,并且是左右根,所以先构造右子树,再构造左子树。先在前面把postIndex建立好。
class Solution {
public int postIndex ;
public TreeNode buildTree(int[] inorder, int[] postorder) {
postIndex = postorder.length-1;
return buildTreeChilde(inorder,postorder,0,inorder.length-1);
}
public TreeNode buildTreeChilde(int[] inorder, int[] postorder,int inbegin,int inend) {
if(inbegin > inend){
return null;
}
TreeNode root = new TreeNode(postorder[postIndex]);
int rootIndex = findIndex(inorder,inbegin,inend,postorder[postIndex]);
if(rootIndex == -1){
return null;
}
postIndex--;
root.right = buildTreeChilde(inorder,postorder,rootIndex + 1,inend);
root.left = buildTreeChilde(inorder,postorder,inbegin,rootIndex - 1);
return root;
}
private int findIndex(int[] inorder,int inbegin,int inend,int key){
for(int i = inbegin; i <= inend; i++){
if(inorder[i] == key){
return i;
}
}
return -1;
}
}
第十一题:LeedCod,606. 根据二叉树创建字符串 - 力扣(LeetCode)
给你二叉树的根节点 root
,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
空节点使用一对空括号对 "()"
表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
这题的解题思路:
由于有括号的存在,所以要考虑很多情况,根节点不存在直接返回null,根节点存在,左子树存在,就要先加一个左括号,然后再次调用遍历,结束左子树构造后再加上右括号,这时如果右子树不存在,则直接返回,第二种情况是,左子树不存在,右子树存在,那么左子树不存在直接加左右两个括号,遍历右子树的时候也是先左括号,遍历完成后加上右括号,如果不存在的话直接返回,不加括号。
class Solution {
public String tree2str(TreeNode root) {
StringBuilder stringBuilder = new StringBuilder();
tree2strChilde(root,stringBuilder);
return stringBuilder.toString();
}
public void tree2strChilde(TreeNode t,StringBuilder stringBuilder) {
if(t == null){
return;
}
stringBuilder.append(t.val);
if(t.left != null){
stringBuilder.append("(");
tree2strChilde(t.left,stringBuilder);
stringBuilder.append(")");
}else{
if(t.right == null){
return;
}else{
stringBuilder.append("()");
}
}
if(t.right != null){
stringBuilder.append("(");
tree2strChilde(t.right,stringBuilder);
stringBuilder.append(")");
}else{
return;
}
}
}