• 算法体系-12 第 十二 二叉树的基本算法


    一 实现二叉树的按层遍历

    1.1 描述

    1)其实就是宽度优先遍历,用队列

    2)可以通过设置flag变量的方式,来发现某一层的结束(看题目)看下边的第四题解答

    1.2 代码

    1. public class Code01_LevelTraversalBT {
    2. public static class Node {
    3. public int value;
    4. public Node left;
    5. public Node right;
    6. public Node(int v) {
    7. value = v;
    8. }
    9. }
    10. public static void level(Node head) {
    11. if (head == null) {
    12. return;
    13. }
    14. Queue queue = new LinkedList<>();
    15. queue.add(head);
    16. while (!queue.isEmpty()) {
    17. Node cur = queue.poll();
    18. System.out.println(cur.value);
    19. if (cur.left != null) {
    20. queue.add(cur.left);
    21. }
    22. if (cur.right != null) {
    23. queue.add(cur.right);
    24. }
    25. }
    26. }

    二 实现二叉树的序列化和反序列化

    2.1描述

    1)先序方式序列化和反序列化

    2)按层方式序列化和反序列化

    将二叉树序力化为唯一的字符串叫序力化,字符串也能转出唯一的数二叉树叫反序力化

    2.2 分析

    2.3 前序列化代码

    1. public static class Node {
    2. public int value;
    3. public Node left;
    4. public Node right;
    5. public Node(int data) {
    6. this.value = data;
    7. }
    8. }
    9. public static Queue preSerial(Node head) {
    10. Queue ans = new LinkedList<>();
    11. pres(head, ans);
    12. return ans;
    13. }
    14. public static void pres(Node head, Queue ans) {
    15. if (head == null) {
    16. ans.add(null);
    17. } else {
    18. ans.add(String.valueOf(head.value));
    19. pres(head.left, ans);
    20. pres(head.right, ans);
    21. }
    22. }

    2.4 前序反序列化

    1. public static Node buildByPreQueue(Queue prelist) {
    2. if (prelist == null || prelist.size() == 0) {
    3. return null;
    4. }
    5. return preb(prelist);
    6. }
    7. public static Node preb(Queue prelist) {
    8. String value = prelist.poll();
    9. if (value == null) {
    10. return null;
    11. }
    12. Node head = new Node(Integer.valueOf(value));
    13. head.left = preb(prelist);
    14. head.right = preb(prelist);
    15. return head;
    16. }

    2.5 中序列化代码

    由上图可以知道,中序序例化是有歧义的,所以不存在中序的序列化

    1. public static Queue inSerial(Node head) {
    2. Queue ans = new LinkedList<>();
    3. ins(head, ans);
    4. return ans;
    5. }
    6. public static void ins(Node head, Queue ans) {
    7. if (head == null) {
    8. ans.add(null);
    9. } else {
    10. ins(head.left, ans);
    11. ans.add(String.valueOf(head.value));
    12. ins(head.right, ans);
    13. }
    14. }

    2.7 中序反列化 

    2.8 后序列化代码

    1. public static Queue posSerial(Node head) {
    2. Queue ans = new LinkedList<>();
    3. poss(head, ans);
    4. return ans;
    5. }
    6. public static void poss(Node head, Queue ans) {
    7. if (head == null) {
    8. ans.add(null);
    9. } else {
    10. poss(head.left, ans);
    11. poss(head.right, ans);
    12. ans.add(String.valueOf(head.value));
    13. }
    14. }

    2.9后序反列化代码

    1. public static Node buildByPosQueue(Queue poslist) {
    2. if (poslist == null || poslist.size() == 0) {
    3. return null;
    4. }
    5. // 左右中 -> stack(中右左) 默认是左右中,这种情况没法首先没法建立头节点,因此进行转化为头在前面的情况,把它放入stack(中右左),这是头就先出来,就可以新建head
    6. Stack stack = new Stack<>();
    7. while (!poslist.isEmpty()) {
    8. stack.push(poslist.poll());
    9. }
    10. return posb(stack);
    11. }
    12. public static Node posb(Stack posstack) {
    13. String value = posstack.pop();
    14. if (value == null) {
    15. return null;
    16. }
    17. Node head = new Node(Integer.valueOf(value));
    18. head.right = posb(posstack);
    19. head.left = posb(posstack);
    20. return head;
    21. }

    3.0 按层序列化和反序列化

    3.0.1分析

    3.0.2 按层序列化 代码

    1. public static Queue levelSerial(Node head) {
    2. Queue ans = new LinkedList<>();
    3. if (head == null) {
    4. ans.add(null);
    5. } else {
    6. ans.add(String.valueOf(head.value));
    7. Queue queue = new LinkedList();
    8. queue.add(head);
    9. while (!queue.isEmpty()) {
    10. head = queue.poll(); // head 父 子
    11. if (head.left != null) {
    12. ans.add(String.valueOf(head.left.value));
    13. queue.add(head.left);
    14. } else {
    15. ans.add(null);
    16. }
    17. if (head.right != null) {
    18. ans.add(String.valueOf(head.right.value));
    19. queue.add(head.right);
    20. } else {
    21. ans.add(null);
    22. }
    23. }
    24. }
    25. return ans;
    26. }

    3.0.3 按层反序列化 代码

    1. public static Node buildByLevelQueue(Queue levelList) {
    2. if (levelList == null || levelList.size() == 0) {
    3. return null;
    4. }
    5. Node head = generateNode(levelList.poll());
    6. Queue queue = new LinkedList();
    7. if (head != null) {
    8. queue.add(head);
    9. }
    10. //因为要记录上一次的节点,这里借用队列来完成
    11. Node node = null;
    12. while (!queue.isEmpty()) {
    13. node = queue.poll();
    14. node.left = generateNode(levelList.poll());
    15. node.right = generateNode(levelList.poll());
    16. if (node.left != null) {
    17. queue.add(node.left);
    18. }
    19. if (node.right != null) {
    20. queue.add(node.right);
    21. }
    22. }
    23. return head;
    24. }
    25. public static Node generateNode(String val) {
    26. if (val == null) {
    27. return null;
    28. }
    29. return new Node(Integer.valueOf(val));
    30. }

    三 Encode N-ary Tree to Binary Tree

    3.1 描述

    一颗多叉树,序历化为为二叉树,二叉树也能转为原来的多叉树;

    3.2 分析

    第一步 先将多叉树的每个节点的孩子放在对应节点左树的右边界上;

    左树的节点为该节点的第一个树,反回来看某个节点是否有孩子看该节点左数是否有右边孩子

    结构如下

    3.3 代码

    1. package class11;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. // 本题测试链接:https://leetcode.com/problems/encode-n-ary-tree-to-binary-tree
    5. public class Code03_EncodeNaryTreeToBinaryTree {
    6. // 提交时不要提交这个类
    7. public static class Node {
    8. public int val;
    9. public List children;
    10. public Node() {
    11. }
    12. public Node(int _val) {
    13. val = _val;
    14. }
    15. public Node(int _val, List _children) {
    16. val = _val;
    17. children = _children;
    18. }
    19. };
    20. // 提交时不要提交这个类
    21. public static class TreeNode {
    22. int val;
    23. TreeNode left;
    24. TreeNode right;
    25. TreeNode(int x) {
    26. val = x;
    27. }
    28. }
    29. // 只提交这个类即可
    30. class Codec {
    31. // Encodes an n-ary tree to a binary tree.
    32. public TreeNode encode(Node root) {
    33. if (root == null) {
    34. return null;
    35. }
    36. TreeNode head = new TreeNode(root.val);
    37. head.left = en(root.children);
    38. return head;
    39. }
    40. private TreeNode en(List children) {
    41. TreeNode head = null;
    42. TreeNode cur = null;
    43. for (Node child : children) {
    44. TreeNode tNode = new TreeNode(child.val);
    45. if (head == null) {
    46. head = tNode;
    47. } else {
    48. cur.right = tNode;
    49. }
    50. cur = tNode;
    51. cur.left = en(child.children);
    52. }
    53. return head;
    54. }
    55. // Decodes your binary tree to an n-ary tree.
    56. public Node decode(TreeNode root) {
    57. if (root == null) {
    58. return null;
    59. }
    60. return new Node(root.val, de(root.left));
    61. }
    62. public List de(TreeNode root) {
    63. List children = new ArrayList<>();
    64. while (root != null) {
    65. Node cur = new Node(root.val, de(root.left));
    66. children.add(cur);
    67. root = root.right;
    68. }
    69. return children;
    70. }
    71. }
    72. }

    四 求二叉树最宽的层有多少个节点

    4.1 描述

    打印二叉树每层的的节点树及最多的节点数;

    4.2 分析

    根据宽度优先遍历的基础上,要是能知道哪一层结束,那么就能算出每一层的节点数;

    设计两个数,Node curEnd = head; // 当前层,最右节点是谁Node nextEnd = null; // 下一层,最右节点是谁

    每次遍历当前节点时候,判断该节点是否和记录的curEnd节点相等,相等就是当前层结束了,把当前层的节点数更新到max中,

    再将当前节点的每一个左右孩子更新到队列中的过程中,每一步都更新nextEnd的值为当前加队列的值,下一层遍历来的时候更新curEnd值为nextEnd

    4.3 代码

    1. public static int maxWidthNoMap(Node head) {
    2. if (head == null) {
    3. return 0;
    4. }
    5. Queue queue = new LinkedList<>();
    6. queue.add(head);
    7. Node curEnd = head; // 当前层,最右节点是谁
    8. Node nextEnd = null; // 下一层的最右节点是谁。提前为下一层出来的end节点做准备
    9. int max = 0;
    10. int curLevelNodes = 0; // 当前层的节点数
    11. while (!queue.isEmpty()) {
    12. Node cur = queue.poll();
    13. if (cur.left != null) {
    14. queue.add(cur.left);
    15. nextEnd = cur.left;
    16. }
    17. if (cur.right != null) {
    18. queue.add(cur.right);
    19. nextEnd = cur.right;
    20. }
    21. curLevelNodes++;
    22. if (cur == curEnd) {
    23. max = Math.max(max, curLevelNodes);
    24. curLevelNodes = 0;
    25. curEnd = nextEnd;
    26. }
    27. }
    28. return max;
    29. }
    30. //使用额外HashMapd
    31. public static class Node {
    32. public int value;
    33. public Node left;
    34. public Node right;
    35. public Node(int data) {
    36. this.value = data;
    37. }
    38. }
    39. public static int maxWidthUseMap(Node head) {
    40. if (head == null) {
    41. return 0;
    42. }
    43. Queue queue = new LinkedList<>();
    44. queue.add(head);
    45. // key 在 哪一层,value
    46. HashMap levelMap = new HashMap<>();
    47. levelMap.put(head, 1);
    48. int curLevel = 1; // 当前你正在统计哪一层的宽度
    49. int curLevelNodes = 0; // 当前层curLevel层,宽度目前是多少
    50. int max = 0;
    51. while (!queue.isEmpty()) {
    52. Node cur = queue.poll();
    53. int curNodeLevel = levelMap.get(cur);
    54. if (cur.left != null) {
    55. levelMap.put(cur.left, curNodeLevel + 1);
    56. queue.add(cur.left);
    57. }
    58. if (cur.right != null) {
    59. levelMap.put(cur.right, curNodeLevel + 1);
    60. queue.add(cur.right);
    61. }
    62. if (curNodeLevel == curLevel) {
    63. curLevelNodes++;
    64. } else {
    65. max = Math.max(max, curLevelNodes);
    66. curLevel++;
    67. curLevelNodes = 1;
    68. }
    69. }
    70. max = Math.max(max, curLevelNodes);
    71. return max;
    72. }

    五 二叉树中的某个节点,返回该节点的后继节点

    5.1 描述

    后继节点 :比如中序遍历,求该节点的4的后继节点,就是中序遍历遍历到该节点后的所有节点

    二叉树结构如下定义:

    Class Node {

    V value;

    Node left;

    Node right;

    Node parent;

    }

    给你二叉树中的某个节点,返回该节点的后继节点

    5.2 分析 中序遍历

    5.2.1 方案一 先通过parrent 找到的他的跟节点后,然后通root找到他的中序遍历,然后就可以找到该节点的后继节点

    方案二

    5.2.2 情况1一 如果该节点有右数,那么他的后继节点一定是他右树的最左侧节点

    情况二 当该节点没有右树的时候,去找该节点是谁的节点左树的最右侧节点(中序遍历的本质理解)如果没有右子树,根据中序遍历的特点,下一个就应该是去找该节点是谁的节点左树的最右侧节点

    情况二 讨论如下 找一个数的后继节点,一直往上找,通过找到该节点的最后一个父节点,该节点的右子树就是他的后继节点

    如下,x是y左数的最右节点,所以打印完x就该打印y了 == 找的就是那个左树上的最右节点

    5.3 代码

    1. public class Code06_SuccessorNode {
    2. public static class Node {
    3. public int value;
    4. public Node left;
    5. public Node right;
    6. public Node parent;
    7. public Node(int data) {
    8. this.value = data;
    9. }
    10. }
    11. public static Node getSuccessorNode(Node node) {
    12. if (node == null) {
    13. return node;
    14. }
    15. if (node.right != null) {
    16. return getLeftMost(node.right);
    17. } else { // 无右子树
    18. Node parent = node.parent;
    19. while (parent != null && parent.right == node) { // 当前节点是其父亲节点右孩子
    20. node = parent;
    21. parent = node.parent;
    22. }
    23. return parent;
    24. }
    25. }
    26. public static Node getLeftMost(Node node) {
    27. if (node == null) {
    28. return node;
    29. }
    30. while (node.left != null) {
    31. node = node.left;
    32. }
    33. return node;
    34. }

  • 相关阅读:
    从零开始学React--环境搭建
    C++ 字符串
    (51单片机)第十一章-串行口应用提高
    CentOS 配置 sftp 服务
    2.1.2 运算放大器的组成与分类、运算放大器的发展历程
    低代码掀起“数字革命”,引领制造业数字化转型
    开发工具篇第七讲:阿里云日志查询与分析
    【Azure Developer】Azure AD 注册应用的 OAuth 2.0 v2 终结点获取的 Token 解析出来依旧为v1, 这是什么情况!
    uniapp 小程序 父组件调用子组件方法
    《Effective C++》条款12
  • 原文地址:https://blog.csdn.net/m0_61164038/article/details/136857904