题目描述

image.png

解题思路

二叉搜索树,利用其性质,使用中序遍历即可。
本文解法基于性质:二叉搜索树的中序遍历为 递增序列 。
将 二叉搜索树 转换成一个 “排序的循环双向链表” ,其中包含三个要素:

  • 排序链表: 节点应从小到大排序,因此应使用 中序遍历 “从小到大”访问树的节点。
  • 双向链表: 在构建相邻节点的引用关系时,设前驱节点 pre 和当前节点 cur ,不仅应构建 pre.right = cur ,也应构建 cur.left = pre 。
  • 循环链表: 设链表头节点 head 和尾节点 tail ,则应构建 head.left = tail 和 tail.right = head

image.png

算法流程:
dfs(cur): 递归法中序遍历;

  • 终止条件: 当节点 cur 为空,代表越过叶节点,直接返回;
  • 递归左子树,即 dfs(cur.left) ;
  • 构建链表:
    • 当 pre 为空时: 代表正在访问链表头节点,记为 head ;
    • 当 pre 不为空时: 修改双向节点引用,即 pre.right = cur , cur.left = pre ;
    • 保存 cur : 更新 pre = cur ,即节点 cur 是后继节点的 pre ;
  • 递归右子树,即 dfs(cur.right) ;

treeToDoublyList(root):

  • 特例处理: 若节点 root 为空,则直接返回;
  • 初始化: 空节点 pre ;
  • 转化为双向链表: 调用 dfs(root) ;
  • 构建循环链表: 中序遍历完成后,head 指向头节点, pre 指向尾节点,因此修改 head 和 pre 的双向节点引用即可;
  • 返回值: 返回链表的头节点 head 即可;

    1. class Solution {
    2. public Node treeToDoublyList(Node root) {
    3. if (root == null) {
    4. return head;
    5. }
    6. dfs(root);
    7. // 形成双向链表
    8. head.right = pre;
    9. pre.left = head;
    10. return head;
    11. }
    12. Node pre;
    13. Node head;
    14. public void dfs(Node cur) {
    15. if (cur == null) {
    16. return;
    17. }
    18. // 中序遍历
    19. dfs(cur.left);
    20. if (pre == null) head = pre; // 此时是头节点
    21. // 交换指向
    22. else pre.right = cur;
    23. cur.left = pre;
    24. pre = cur;
    25. dfs(cur.right);
    26. }
    27. }

    时间复杂度 O(N) : N 为二叉树的节点数,中序遍历需要访问所有节点。
    空间复杂度 O(N) : 最差情况下,即树退化为链表时,递归深度达到 N,系统使用 O(N) 栈空间。