• 【数据结构】——二叉树oj题详解


    目录

    1、100. 相同的树 - 力扣(LeetCode)

    2、572. 另一棵树的子树 - 力扣(LeetCode)

    3、110. 平衡二叉树 - 力扣(LeetCode)

    4、101. 对称二叉树 - 力扣(LeetCode)

    5、102. 二叉树的层序遍历 - 力扣(LeetCode)

     6、判断该树是否为完全二叉树

    七、二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

     八、236. 二叉树的最近公共祖先 - 力扣(LeetCode)

    九、二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

    十、105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

    十一、106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

    十二、非递归实现二叉树的前中后序遍历


    1、100. 相同的树 - 力扣(LeetCode)

     我们考虑它相同和不相同的情况,再用递归遍历:

    完整代码:

    1. public boolean isSameTree(TreeNode p, TreeNode q) {
    2. if(p == null && q != null || p != null && q == null){
    3. return false;
    4. }
    5. if (p == null && q == null){
    6. return true;
    7. }
    8. if(p.val != q.val){
    9. return false;
    10. }
    11. return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
    12. }

    2、572. 另一棵树的子树 - 力扣(LeetCode)

    这道题,我们可以直接用上一道题的代码,其思路如下:

    1. 我们首先判断root和subRoot是不是两颗相同的树,是的话就返回true 
    2. 不是的话,就继续判断subRoot是不是root.left的左子树的子树或者root.left是不是和和subRoot为相同的树
    3. 同理再判断subRoot和root.right;

    完整代码:

    1. class Solution {
    2. //时间复杂度为:n * m
    3. public boolean isSameTree(TreeNode p, TreeNode q) {
    4. if(p == null && q != null || p != null && q == null) {
    5. return false;
    6. }
    7. if(p == null && q == null) {
    8. return true;
    9. }
    10. if(p.val != q.val) {
    11. return false;
    12. }
    13. return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
    14. }
    15. public boolean isSubtree(TreeNode root, TreeNode subRoot) {
    16. if (root == null || subRoot == null){
    17. return false;
    18. }
    19. if (isSameTree(root,subRoot)){
    20. return true;
    21. }
    22. if(isSubtree(root.left,subRoot)){
    23. return true;
    24. }
    25. if(isSubtree(root.right,subRoot)){
    26. return true;
    27. }
    28. return false;
    29. }
    30. }

    3、110. 平衡二叉树 - 力扣(LeetCode)

    思路:

    1.  首先这道题判断平衡也就是左右子树的高度差,那就需要求出左右子树的高度,,进行比较
    2. 然后还需要求出左子树的子树的高度差,和右子树的子树的高度差,来判断左子树和右子树是否平衡。
    3. 所以需要遍历树的每一个结点

    完整代码:

    1. class Solution {
    2. //时间复杂度为:n * n
    3. int getHeight(TreeNode root){
    4. if (root == null){
    5. return 0;
    6. }
    7. int leftTree = getHeight(root.left);
    8. int rightTree = getHeight(root.right);
    9. return leftTree > rightTree ? leftTree+1 : rightTree+1;
    10. }
    11. public boolean isBalanced(TreeNode root) {
    12. if(root == null){
    13. return true;
    14. }
    15. int leftHeight = getHeight(root.left);
    16. int rightHeight = getHeight(root.right);
    17. return Math.abs(leftHeight-rightHeight) <= 1
    18. && isBalanced(root.left)
    19. && isBalanced(root.right);
    20. }
    21. }

     上面的这种解法的时间复杂度是O(n^2),会出现大量的的重复计算,当计算根结点的左子树高度时,递归到数字九时返回一个2就已经说明了该树不平衡,所以我们从下向上计算高度时顺便判断一下,这样的话时间复杂度就是O(n)

    1. class Solution {
    2. int getHeight(TreeNode root){
    3. if (root == null){
    4. return 0;
    5. }
    6. int leftHeight = getHeight(root.left);
    7. int rightHeight = getHeight(root.right);
    8. if(leftHeight >= 0 && rightHeight >= 0 && Math.abs(leftHeight - rightHeight) <= 1){
    9. return Math.max(leftHeight,rightHeight) + 1;
    10. }else{
    11. //返回-1 就说明已经出现了不平衡
    12. return -1;
    13. }
    14. }
    15. public boolean isBalanced(TreeNode root) {
    16. if(root == null){
    17. return true;
    18. }
    19. return getHeight(root) >= 0;
    20. }
    21. }

    4、101. 对称二叉树 - 力扣(LeetCode)

     思路就是判断root的左右子树是否对称:左子树的右树和右子树的左树是否相同:

    1. class Solution {
    2. public boolean isSymmetric(TreeNode root) {
    3. if(root == null) {
    4. return true;
    5. }
    6. return isSymmetricChild(root.left,root.right);
    7. }
    8. private boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
    9. //判断左子树的右树和右子树的左树是否相同.
    10. if (leftTree == null && rightTree != null || leftTree != null && rightTree == null){
    11. return false;
    12. }
    13. //是否都为空
    14. if (leftTree == null && rightTree == null){
    15. return true;
    16. }
    17. //判断val值
    18. if (leftTree.val != rightTree.val){
    19. return false;
    20. }
    21. //进行递归左子树的右树和右子树的左树
    22. return isSymmetricChild(leftTree.left, rightTree.right) &&
    23. isSymmetricChild(leftTree.right, rightTree.left);
    24. }
    25. }

    5、102. 二叉树的层序遍历 - 力扣(LeetCode)

    思路:

    1. 首先要创建两个顺序表来分层存储二叉树
    2. 创建一个队列,先把根结点入对,再把top弹出的同时并赋值给cur并打印
    3. 并且把top的左右子树入队,直到树为空

     完整代码:

    1. class Solution {
    2. public List> levelOrder(TreeNode root) {
    3. List> ret = new ArrayList>();
    4. if(root == null){
    5. return ret;
    6. }
    7. Queue queue = new LinkedList();
    8. queue.offer(root);
    9. while(!queue.isEmpty()){
    10. List leve = new ArrayList();
    11. int queue_size = queue.size();
    12. for(int i = 0; i < queue_size; i++){
    13. TreeNode node = queue.poll();
    14. leve.add(node.val);
    15. if(node.left != null){
    16. queue.offer(node.left);
    17. }
    18. if(node.right != null){
    19. queue.offer(node.right);
    20. }
    21. }
    22. ret.add(leve);
    23. }
    24. return ret;
    25. }
    26. }

     6、判断该树是否为完全二叉树

    思路:

    1. 将null也入队,如果队列中只剩下null,说明就为完全二叉树
    2. 当非完全二叉树入队时,出队的时候会发现队列中还剩下null和结点

     

     完整代码:

    1. boolean isCompleteTree(TreeNode root){
    2. if (root == null){
    3. return true;
    4. }
    5. Queue queue = new LinkedList<>();
    6. queue.offer(root);
    7. while (!queue.isEmpty()){
    8. TreeNode cur = queue.poll();
    9. if (cur != null){
    10. queue.offer(cur.left);
    11. queue.offer(cur.right);
    12. }else {
    13. break;
    14. }
    15. }
    16. while (!queue.isEmpty()){
    17. TreeNode cur = queue.peek();
    18. if (cur != null){
    19. //不是完全二叉树
    20. return false;
    21. }else {
    22. queue.poll();
    23. }
    24. }
    25. return true;
    26. }

    七、二叉树遍历_牛客题霸_牛客网 (nowcoder.com)

    1. import java.util.*;
    2. public class Main {
    3. class TreeNode{
    4. public char val;
    5. public TreeNode left;
    6. public TreeNode right;
    7. public TreeNode(char val){
    8. this.val = val;
    9. }
    10. }
    11. public int i = 0;
    12. //创建树
    13. public TreeNode createTree(String s){
    14. TreeNode root = null;
    15. if (s.charAt(i) != '#'){
    16. root = new TreeNode(s.charAt(i));
    17. i++;
    18. root.left = createTree(s);
    19. root.right = createTree(s);
    20. }else {
    21. i++;
    22. }
    23. return root;
    24. }
    25. public void inorder(TreeNode root){
    26. if (root == null){
    27. return;
    28. }
    29. inorder(root.left);
    30. System.out.print(root.val+" ");
    31. inorder(root.right);
    32. }
    33. public static void main(String[] args) {
    34. Scanner scanner = new Scanner(System.in);
    35. while (scanner.hasNextLine()){
    36. String s = scanner.nextLine();
    37. Main m = new Main();
    38. TreeNode root = m.createTree(s);
    39. m.inorder(root);
    40. }
    41. }
    42. }

     八、236. 二叉树的最近公共祖先 - 力扣(LeetCode)

     列出求公共祖先会出现的情况:

    分析:

    1. 假设这是一颗二叉搜索树(中序遍历是有序的),p == root || q == root, 此时公共祖先就是root
    2. 当p和q刚好在左右子树中,公共祖先就是根结点
    3. 当p.val和q.val都小于root.val,此时p和q都在root的左子树中,此时就在左子树中找,右子树同理;

    完整代码:

    1. class Solution {
    2. public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
    3. if(root == null){
    4. return null;
    5. }
    6. if(root == p || root == q){
    7. return root;
    8. }
    9. TreeNode retleft = lowestCommonAncestor(root.left,p,q);
    10. TreeNode retright = lowestCommonAncestor(root.right,p,q);
    11. if(retleft != null && retright != null){
    12. return root;
    13. }else if(retleft != null){//都在左树
    14. return retleft;
    15. }else{
    16. return retright;
    17. }
    18. }
    19. }

    当我们用栈的思想来解决这道题时,可以有:

     完整代码:

    1. class Solution {
    2. //找到根结点到指定节点node之间路径上的所有结点,存储到栈中
    3. private boolean getPath(TreeNode root, TreeNode node, Stack stack) {
    4. if (root == null || node == null) {
    5. return false;
    6. }
    7. stack.push(root);
    8. if (root == node) {
    9. return true;
    10. }
    11. boolean ret1 = getPath(root.left, node, stack);
    12. if (ret1) {
    13. return true;
    14. }
    15. boolean ret2 = getPath(root.right, node, stack);
    16. if (ret2) {
    17. return true;
    18. }
    19. //当前根结点不是,根的左右子树都不是
    20. stack.pop();
    21. return false;
    22. }
    23. public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
    24. Stack stack1 = new Stack<>();
    25. getPath(root,p,stack1);
    26. Stack stack2 = new Stack<>();
    27. getPath(root,q,stack2);
    28. int size1 = stack1.size();
    29. int size2 = stack2.size();
    30. if(size1 > size2){
    31. int tmp = size1 - size2;
    32. while(tmp != 0){
    33. stack1.pop();
    34. tmp--;
    35. }
    36. }else{
    37. int tmp = size2 - size1;
    38. while(tmp != 0){
    39. stack2.pop();
    40. tmp--;
    41. }
    42. }
    43. while(!stack1.empty() && !stack2.empty()){
    44. if(stack1.peek() == stack2.peek()){
    45. return stack1.peek();
    46. }else{
    47. stack1.pop();
    48. stack2.pop();
    49. }
    50. }
    51. return null;
    52. }
    53. }

    九、二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

    思路:

    1. 题目要求转换成一个排序的双向链表,并且是一颗二叉搜索树,所以我们要使用中序遍历,遍历树
    2. 然后利用每个节点的left和right作为前驱后继进行连接 

     

    1. public class Solution {
    2. TreeNode prev = null;
    3. public void ConvertChild(TreeNode root){
    4. if (root == null){
    5. return;
    6. }
    7. ConvertChild(root.left);
    8. root.left = prev;
    9. if (prev != null){
    10. prev.right = root;
    11. }
    12. prev = root;
    13. ConvertChild(root.right);
    14. }
    15. public TreeNode Convert(TreeNode pRootOfTree){
    16. if (pRootOfTree == null){
    17. return null;
    18. }
    19. ConvertChild(pRootOfTree);
    20. TreeNode head = pRootOfTree;
    21. while(head.left != null){
    22. head = head.left;
    23. }
    24. return head;
    25. }
    26. }

    十、105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

    思路:

     完整代码:

    1. class Solution {
    2. public int preIndex = 0;
    3. public TreeNode buildTreeChild(int[] preorder, int[] inorder,int inBegin,int inEnd){
    4. if(inBegin > inEnd){
    5. return null;
    6. }
    7. TreeNode root = new TreeNode(preorder[preIndex]);
    8. int rootIndex = findInorderIndex(inorder,preorder[preIndex],inBegin,inEnd);
    9. preIndex++;
    10. root.left = buildTreeChild(preorder,inorder, inBegin, rootIndex-1);
    11. root.right = buildTreeChild(preorder,inorder,rootIndex+1, inEnd);
    12. return root;
    13. }
    14. //找到根结点的位置
    15. private int findInorderIndex(int[] inorder,int val,int inBegin,int inEnd){
    16. for(int i = inBegin; i <= inEnd; i++){
    17. if(inorder[i] == val){
    18. return i;
    19. }
    20. }
    21. return -1;
    22. }
    23. public TreeNode buildTree(int[] preorder, int[] inorder) {
    24. return buildTreeChild(preorder,inorder,0,inorder.length-1);
    25. }
    26. }

    同理这道题也是一样的道理:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

    只不过变成了后序序列倒着是根结点

    1. class Solution {
    2. public int postIndex;
    3. public TreeNode buildTreeChild(int[] postorder, int[] inorder,int inBegin,int inEnd){
    4. if(inBegin > inEnd){
    5. return null;
    6. }
    7. TreeNode root = new TreeNode(postorder[postIndex]);
    8. int rootIndex = findInorderIndex(inorder,postorder[postIndex],inBegin,inEnd);
    9. postIndex--;
    10. root.right = buildTreeChild(postorder,inorder,rootIndex+1, inEnd);
    11. root.left = buildTreeChild(postorder,inorder, inBegin, rootIndex-1);
    12. return root;
    13. }
    14. private int findInorderIndex(int[] inorder,int val,int inBegin,int inEnd){
    15. for(int i = inBegin; i <= inEnd; i++){
    16. if(inorder[i] == val){
    17. return i;
    18. }
    19. }
    20. return -1;
    21. }
    22. public TreeNode buildTree(int[] inorder,int[] postorder) {
    23. postIndex = postorder.length-1;
    24. return buildTreeChild(postorder,inorder,0,inorder.length-1);
    25. }
    26. }

    十一、106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

     

     思路:

    每颗小子树走完都会加一个右括号,当然除了根结点剩下的结点都先加一个左括号,变美丽树,并将其打印出来:

    1. class Solution {
    2. public String tree2str(TreeNode root) {
    3. //通过StringBuilder修改字符串
    4. StringBuilder sb = new StringBuilder();
    5. tree2strChild(root,sb);
    6. return sb.toString();
    7. }
    8. private void tree2strChild(TreeNode t,StringBuilder sb){
    9. if(t == null){
    10. return;
    11. }
    12. sb.append(t.val);
    13. //左子树不为空就加个左括号
    14. if(t.left != null){
    15. sb.append("(");
    16. tree2strChild(t.left,sb);
    17. sb.append(")");
    18. }else{
    19. if(t.right == null){
    20. return;
    21. }else{
    22. sb.append("()");
    23. }
    24. }
    25. if(t.right == null){
    26. return;
    27. }else{
    28. sb.append("(");
    29. tree2strChild(t.right,sb);
    30. sb.append(")");
    31. }
    32. }
    33. }

    十二、非递归实现二叉树的前中后序遍

    前序遍历思路:

    创建一个栈,将根结点入栈并打印,再遍历root.left,当结点左右子树为空时就出栈并赋值给top记录下来,再继续遍历root.right:

    1. class Solution {
    2. public List preorderTraversal(TreeNode root){
    3. List ret = new ArrayList<>();
    4. Stack stack = new Stack<>();
    5. TreeNode cur = root;
    6. while (cur != null || !stack.isEmpty()) {
    7. while (cur != null) {
    8. stack.push(cur);
    9. System.out.println(cur.val + " ");
    10. ret.add(cur.val);
    11. cur = cur.left;
    12. }
    13. TreeNode top = stack.pop();
    14. cur = top.right;
    15. }
    16. return ret;
    17. }
    18. }

    中序遍历同理:

    1. class Solution {
    2. public List inorderTraversal(TreeNode root) {
    3. List list = new ArrayList<>();
    4. Stack stack = new Stack<>();
    5. TreeNode cur = root;
    6. while (cur != null || !stack.isEmpty()) {
    7. while (cur != null) {
    8. stack.push(cur);
    9. cur = cur.left;
    10. }
    11. TreeNode top = stack.pop();
    12. System.out.println(cur.val + " ");
    13. list.add(top.val);
    14. cur = top.right;
    15. }
    16. return list;
    17. }
    18. }

    后序遍历:

    1. class Solution {
    2. public List postorderTraversal(TreeNode root) {
    3. List list = new ArrayList<>();
    4. Stack stack = new Stack<>();
    5. TreeNode prev = null;
    6. TreeNode cur = root;
    7. while (cur != null || !stack.isEmpty()) {
    8. while (cur != null) {
    9. stack.push(cur);
    10. cur = cur.left;
    11. }
    12. //不能直接弹出
    13. TreeNode top = stack.peek();
    14. if(top.right == null || top.right == prev){
    15. stack.pop();
    16. list.add(top.val);
    17. prev = top;
    18. }else{
    19. cur = top.right;
    20. }
    21. }
    22. return list;
    23. }
    24. }

  • 相关阅读:
    Verilog刷题[hdlbits] :Always if2
    【Golang | reflect】利用反射实现方法的调用
    java-net-php-python-jspm综合彩妆店管理系统查重PPT计算机毕业设计程序
    JavaScript基础知识13——运算符:一元运算符,二元运算符
    【机械】基于matlab实现直齿圆柱齿轮应力计算附matlab代码
    TCP如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)
    spark学习笔记(十一)——sparkStreaming-概述/特点/构架/DStream入门程序wordcount
    戴着人工心脏上脱口秀大会——王十七的充电人生
    军用FPGA软件 Verilog语言的编码准测之触发器、锁存器
    【学习挑战赛】经典算法之直接选择排序
  • 原文地址:https://blog.csdn.net/weixin_62678196/article/details/126643443