二叉树的遍历(递归,非递归,Morris)

迈不过友情╰ 2023-06-23 02:52 186阅读 0赞

二叉树的遍历


目录

  1. 递归遍历
  2. 非递归遍历
  3. Morris遍历

1. 递归遍历

递归版遍历只要当前节点不为null,就可以三次回到当前节点。

  1. public static void preOrderRecur(Node head) {
  2. if (head == null) {
  3. return;
  4. }
  5. System.out.print(head.value + " ");
  6. preOrderRecur(head.left);
  7. preOrderRecur(head.right);
  8. }
  9. public static void inOrderRecur(Node head) {
  10. if (head == null) {
  11. return;
  12. }
  13. inOrderRecur(head.left);
  14. System.out.print(head.value + " ");
  15. inOrderRecur(head.right);
  16. }
  17. public static void posOrderRecur(Node head) {
  18. if (head == null) {
  19. return;
  20. }
  21. posOrderRecur(head.left);
  22. posOrderRecur(head.right);
  23. System.out.print(head.value + " ");
  24. }

2. 非递归遍历

其中后序遍历打印顺序为左右中,由先序遍历打印顺序为中左右,所以可以对先序遍历改进为中右左(改变添加顺序),添加到另外一个栈中,最后遍历打印就是左右中顺序了。

  1. public static void preOrderUnRecur(Node head) {
  2. System.out.println("pre-order: ");
  3. while (head != null) {
  4. Stack<Node> stack = new Stack<>();
  5. stack.push(head);
  6. while (!stack.isEmpty()) {
  7. Node node = stack.pop();
  8. System.out.println(node.value + " ");
  9. if (node.right != null) {
  10. stack.push(node.right);
  11. }
  12. if (node.left != null) {
  13. stack.push(node.left);
  14. }
  15. }
  16. }
  17. }
  18. public static void inOrderUnRecur(Node head) {
  19. System.out.print("in-order: ");
  20. if (head != null) {
  21. Stack<Node> stack = new Stack<>();
  22. while (!stack.isEmpty() || head != null) {
  23. if (head != null) {
  24. stack.push(head.left);
  25. head = head.left;
  26. } else {
  27. head = stack.pop();
  28. System.out.println(head.value + " ");
  29. head = head.right;
  30. }
  31. }
  32. }
  33. System.out.println();
  34. }
  35. public static void posOrderUnRecur1(Node head) {
  36. System.out.print("pos-order: ");
  37. if (head != null) {
  38. Stack<Node> s1 = new Stack<>();
  39. Stack<Node> s2 = new Stack<>();
  40. s1.push(head);
  41. while (!s1.isEmpty()) {
  42. head = s1.pop();
  43. s2.push(head);
  44. if (head.left != null) {
  45. s1.push(head.left);
  46. }
  47. if (head.right != null) {
  48. s1.push(head.right);
  49. }
  50. while (!s2.isEmpty()) {
  51. System.out.print(s2.pop().value + " ");
  52. }
  53. }
  54. }
  55. System.out.println();
  56. }

3. Morris遍历

Morris遍历法,能以时间复杂度O(N),空间复杂度O(1)实现二叉树的遍历。

程序流程:
假设指针cur指向当前节点,cur从头结点开始。

  1. 如果cur无左孩子,则cur = cur.right;
  2. 如果cur有左孩子,则找到cur左子树上最右的节点,记为mostRight,分为以下两种情况:

    1. 若mostRight的right指针为null,则让其指向cur,且cur = cur.left;
    2. 若mostRight的right指针指向cur,则让其指回null,且cur = cur.right。

假设二叉树如下:

  1. 1
  2. 2 3
  3. 4 5 6 7

则Morris遍历顺序为:1,2,4,2,5,1,3,6,3,7

Morris 遍历代码实现:

  1. public static void morrisIn(Node head) {
  2. if (head == null) {
  3. return;
  4. }
  5. Node cur = head;
  6. Node mostRight = null;
  7. while (cur != null) {
  8. mostRight = cur.left;
  9. if (mostRight != null) {
  10. while (mostRight.right != null && mostRight.right != cur) {
  11. mostRight = mostRight.right;
  12. }
  13. if (mostRight.right == null) {
  14. mostRight.right = cur;
  15. cur = cur.left;
  16. continue;
  17. } else {
  18. mostRight.right = null;
  19. }
  20. }
  21. cur = cur.right;
  22. }
  23. }

特点:

  1. 当某个节点有左子树,则会到达该节点两次,如果没有左子树,只会到达一次。
  2. 当第二次回到某个节点时,它的左子树已遍历完。

实质:
Morris遍历是利用左子树最右节点的指针指向null或指向自己来标记是第一次来到该节点还是第二次来到该节点。


Morris遍历改先序遍历

在以下两种情况下打印节点:

  1. 当节点没有左子树时,打印当前节点;
  2. 当节点有左子树时并且第一次访问时打印该节点,就是当mosrRight的右指针为null时。

    public static void morrisPre(Node head) {

    1. if (head == null) {
    2. return;
    3. }
    4. Node cur = head;
    5. Node mostRight = null;
    6. while (cur != null) {
    7. mostRight = cur.left;
    8. if (mostRight != null) {
    9. while (mostRight.right != null && mostRight.right != cur) {
    10. mostRight = mostRight.right;
    11. }
    12. if (mostRight.right == null) {
    13. mostRight.right = cur;
    14. System.out.print(cur.value + " ");
    15. cur = cur.left;
    16. continue;
    17. } else {
    18. mostRight.right = null;
    19. }
    20. } else {
    21. System.out.print(cur.value + " ");
    22. }
    23. cur = cur.right;
    24. }
    25. System.out.println();
    26. }

Morris遍历改中序遍历

在以下两种情况下打印节点:

  1. 当节点没有左子树时,说明第一次和第二次是重合在一起的,打印当前节点即可。
  2. 当节点有左子树时,那么需要处理完左子树再打印,即当前节点准备右移时打印。

    public static void morrisIn(Node head) {

    1. if (head == null) {
    2. return;
    3. }
    4. Node cur = head;
    5. Node mostRight = null;
    6. while (cur != null) {
    7. mostRight = cur.left;
    8. if (mostRight != null) {
    9. while (mostRight.right != null && mostRight.right != cur) {
    10. mostRight = mostRight.right;
    11. }
    12. if (mostRight.right == null) {
    13. mostRight.right = cur;
    14. cur = cur.left;
    15. continue;
    16. } else {
    17. mostRight.right = null;
    18. }
    19. }
    20. System.out.print(cur.value + " ");
    21. cur = cur.right;
    22. }
    23. System.out.println();
    24. }

Morris遍历改后序遍历

在以下两种情况下打印节点:

  1. 只有当到达某个节点两次时逆序打印该节点左子树的右边界;
  2. 在代码的最后逆序打印整棵树的右边界,而逆序的过程就和单向链表的反转过程类似。

    public static void morrisPos(Node head) {

    1. if (head == null) {
    2. return;
    3. }
    4. Node cur1 = head;
    5. Node cur2 = null;
    6. while (cur1 != null) {
    7. cur2 = cur1.left;
    8. if (cur2 != null) {
    9. while (cur2.right != null && cur2.right != cur1) {
    10. cur2 = cur2.right;
    11. }
    12. if (cur2.right == null) {
    13. cur2.right = cur1;
    14. cur1 = cur1.left;
    15. continue;
    16. } else {
    17. cur2.right = null;
    18. printEdge(cur1.left);
    19. }
    20. }
    21. cur1 = cur1.right;
    22. }
    23. printEdge(head);
    24. System.out.println();
    25. }
    26. public static void printEdge(Node head) {
    27. Node tail = reverseEdge(head);
    28. Node cur = tail;
    29. while (cur != null) {
    30. System.out.print(cur.value + " ");
    31. cur = cur.right;
    32. }
    33. reverseEdge(tail);
    34. }
    35. public static Node reverseEdge(Node from) {
    36. Node pre = null;
    37. Node next = null;
    38. while (from != null) {
    39. next = from.right;
    40. from.right = pre;
    41. pre = from;
    42. from = next;
    43. }
    44. return pre;
    45. }

发表评论

表情:
评论列表 (有 0 条评论,186人围观)

还没有评论,来说两句吧...

相关阅读