• 能带你起飞的【数据结构】成王第十篇:二叉树4


    前言

     

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

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

    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

    百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大

    完整代码

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

    第二种思路

     

     

     

    1.用两个栈存储路径

    2.路径存好了,求栈的大小

    3.让栈中多的元素出差值个元素

    4.开始出栈,知道栈顶元素相同,此时就是公共祖先

     5.如何去找到从根节点到一个指定1节点的路径?

    完整代码

    1. class Solution {
    2. //root 根节点 node 指定的节点 stack 存放从根节点到指定节点的路径
    3.     ppublic boolean getPath(TreeNode root,TreeNode node,Stack<TreeNodestack){
    4.         if(root == null || node == null){
    5.             return false;
    6.         }
    7.         stack.push(root);
    8.         if(root == node){
    9.             return true;
    10.         }
    11.         boolean flg = getPath(root.left,node,stack);
    12.         if(flg == true){
    13.             return true;
    14.         }
    15.         flg = getPath(root.right,node,stack);
    16.         if(flg == true){
    17.             return true;
    18.         }
    19.         stack.pop();
    20.         return false;
    21.     }
    22.     public TreeNode lowestCommonAncestor(TreeNode rootTreeNode pTreeNode q) {
    23.         if(root == null) return null;
    24.         Stack<TreeNode> stack1 = new Stack<>();
    25.         getPath(root,p,stack1);
    26.         Stack<TreeNode> stack2 = new Stack<>()          
    27.         getPath(root,q,stack2);
    28.          int size1 = stack1.size();
    29.          int size2 = stack2.size();
    30.          if(size1 > size2){
    31.              int size = size1 - size2;
    32.              while(size != 0){
    33.                  stack1.pop();
    34.                  size--;
    35.              }
    36.              while(!stack1.isEmpty()  && !stack2.isEmpty() ){
    37.             if(stack1.peek() == stack2.peek()){
    38.                 return stack1.pop();
    39.             }else{
    40.                 stack1.pop();
    41.                 stack2.pop();
    42.             }
    43.         } 
    44.          }else{
    45.          int size = size2 - size1;
    46.         while(size != 0){
    47.             stack2.pop();
    48.             size--;
    49.         }
    50.          while(!stack1.isEmpty()  && !stack2.isEmpty()){
    51.             if(stack1.peek() == stack2.peek()){
    52.                 return stack2.pop();
    53.             }else{
    54.                 stack1.pop();
    55.                 stack2.pop();
    56.             }
    57.         } 
    58.         }
    59.         return null;
    60.          
    61.     }
    62. }

     二叉树搜索树转换成排序双向链表

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

     

     

    如果让这棵二叉搜索树变为链表,1的前驱为空, 3的前驱是1,4的前驱是3,5的前驱是4,6的前驱是5,7的前驱是6,8的前驱是7

     1的后继是3,3的后继是4,4的后继是5,5的后继是6,6的后继是7,7的后继是8

     这是我们最终想要的效果

     left:变成双向链表的前驱

    right:变成双向链表的后驱

    需要在中序遍历的过程中,修改每个节点的left和right

    1.中序遍历不难

    2.难点在于如何修改指向

    完整代码

    1. public class Solution {
    2. TreeNode prev = null;
    3. //先写一个中序遍历
    4. public void inorder(TreeNode pcur){
    5. if(pcur == null) return ;
    6. inorder(pcur.left);
    7. pcur.left =prev;
    8. if(prev != null){
    9. prev.right = pcur;
    10. }
    11. prev = pcur;
    12. System.out.print(pcur.val + " ");
    13. inorder(pcur.right);
    14. }
    15. public TreeNode Convert(TreeNode pRootOfTree) {
    16. if(pRootOfTree == null) return null;
    17. inorder(pRootOfTree);
    18. TreeNode head = pRootOfTree;
    19. while(head.left != null){
    20. head = head.left;
    21. }
    22. return head;
    23. }
    24. }

     根据一棵树的前序遍历与中序遍历构造二叉树

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

    给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

     

     pi来遍历这个字符串

    ri表示根的位置

    因为是前序遍历,pi++的时候,拿到的B就A这棵树的左树,此时B又是A这棵子树的根

     假设中序遍历的起始位置是ib,结束位置是ie

    1.先将pi下标的元素,创建 为root

    2,在中序遍历的数组当中,找到当前pi下标的元素,存在的位置ri

    完整代码

    1. class Solution {
    2.     public int preIndex = 0;
    3.     public TreeNode createTreeByPandI(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 = findIndexofI(inorder,inbegin,inend,preorder[preIndex]);
    9.         if(rootIndex == -1){
    10.             return null
    11.         }
    12.         preIndex++;   
    13.          root.left = createTreeByPandI(preorder,inorder,inbegin,rootIndex - 1);
    14.          root.right = createTreeByPandI(preorder,inorder,rootIndex+ 1,inend);
    15.          return root;
    16.     }
    17.      public int findIndexofI(int[] inorder,int inbegin,int inend,int key){
    18.             for(int i = inbegin; i <= inend; i++ ){
    19.                 if(inorder[i] == key){
    20.                     return i;
    21.                 }
    22.             }
    23.             return -1;
    24.         }
    25.     public TreeNode buildTree(int[] preorder, int[] inorder) {
    26.         if(preorder == null && inorder == null){
    27.             return null;
    28.         }
    29.         return createTreeByPandI(preorder,inorder,0,inorder.length - 1);
    30.     }
    31. }

    二叉树前序非递归遍历实现 

     144. 二叉树的前序遍历 - 力扣(LeetCode)

    首先,非递归就需要用循环

    用栈来处理

    让cur来遍历二叉树的每个节点,用前序遍历的方式

    cur开始指向root

    除了要打印A,然后把A也放到栈里面,来存储下来当前路径的信息

    cur继续往后走,只要cur不为空,继续打印,继续往栈里存放

    此时cur为空了

    说明D这棵树的左树没有了,根也被打印了,那么就开始往D的右边走 

    所以我们需要在这里额外定义一个引用top

    弹出栈顶元素

     然后让当前的cur等于D的right,D的right也是空,说明这棵树完了

    再来看当前栈为不为空,不为空弹出栈顶元素B

    然后再让cur = top.right,为空吗,不为空拿起来放到栈里,同时把E打印

     

    然后cur等于cur.left,为空再弹出栈顶元素,cur = top.right

    以此类推

    完整代码

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

    二叉树中序非递归遍历实现 

    94. 二叉树的中序遍历 - 力扣(LeetCode)

    完整代码

    1. class Solution {
    2. public List<Integer> preorderTraversal(TreeNode root) {
    3. List<Integer> list = new ArrayList<>();
    4. Stack<Integer> 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.print(cur.val + " ");
    13. list.add(cur.val);
    14. cur = top.right;
    15. }
    16. return list;
    17. }
    18. }

    二叉树后序非递归遍历实现 

    145. 二叉树的后序遍历 - 力扣(LeetCode)

    如果cur不为空就放到栈里面,继续往左走 

    cur为空了

    此时我们看一下栈顶的元素D

    此时如果要打印D就必须判断D的右边为不为空

    如果右边为空就打印D

    不为空就让cur = cur,right

    完整代码

    1. class Solution {
    2. public List<Integer> postorderTraversal(TreeNode root) {
    3. Stack<Integer> stack = new Stack<>();
    4. TreeNode cur = root;
    5. List<Integer> lsit = new ArrayList<>();
    6. TreeNode prev = null;
    7. while(cur != null || !stack.isEmpty()){
    8. while(cur != null){
    9. stack.push(cur);
    10. cur = cur.left;
    11. }
    12. TreeNode top = stack.peek();
    13. //如果当前节点的右子树被打印过 或者 遍历过直接弹出
    14. if(top.right == null || top.right == prev){
    15. stack.pop();
    16. lsit.add(cur.left);
    17. prev = top;//记录一下最近一次打印的节点
    18. }else{
    19. cur = top.right;
    20. }
    21. }
    22. }
    23. }

     

     

  • 相关阅读:
    Vue3 之 Vuex - 状态管理
    腾讯云双11服务器优惠活动价格表预热!
    【学习笔记】POJ 3834 graph game
    Mac | 使用 Wineskin 在 Mac 上运行 exe 程序
    详解 Moloch DAO 特性与治理模式
    PAT甲级:1040 Longest Symmetric String
    用patchelf改程序后在exp中用gdb.attach()调试堆栈
    2023年【通信安全员ABC证】找解析及通信安全员ABC证考试总结
    【目标检测】yolov7改进系列:添加CBAM注意力机制
    数据结构与算法课后题-第六章(图的存储及基本操作)
  • 原文地址:https://blog.csdn.net/m0_64397675/article/details/125470565