每日算法——将二叉搜索树转换为双向链表

165 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情


二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

为了让您更好地理解问题,以下面的二叉搜索树为例:

 

 

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

 

 

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

思路

遇到树相关的题目,递归肯定是没错的,题目所给的是二叉搜索树,故采用中序遍历,以保证节点的有序性,先把整体的框架写上,然后再想如何对根节点进行操作。

// 中序遍历
void dfs(Node root) {
    if(root == null) return;
    dfs(root.left); // 左
    ... // 根
    dfs(root.right); // 右
}

根据题目要求,头尾节点也需要指针连接,这种情况无法使用递归实现,所以需要自己定义递归函数,调用该递归函数,然后再将首尾节点连接。

因为需要记录左子节点的状态,以作为前驱节点,故需要全局变量来记录前驱节点。

记前驱节点为 pre,当前节点为 cur,则我们需要做的操作是

  1. 将 pre 的后继节点赋值为 cur
  2. cur 的前驱节点赋值为 pre
  3. pre 后移,即 pre = cur

递归完成以上操作。

初始状态下,pre 为空,则此时的 cur 为头节点,将该节点保存为 head,以作为返回值。

题解

class Solution {
    private Node head, pre;

    public Node treeToDoublyList(Node root) {
        if(root == null) {
            return null;
        }
        dfs(root);
        pre.right = head;     //此时 pre 位于尾节点,需要连接头节点
        head.left = pre;
        return head;
    }

    private void dfs(Node cur) {
        if(cur == null) {
            return;
        }
        dfs(cur.left);
        
        if(pre != null) {           // 1. 将 pre 的后继节点赋值为 cur
            pre.right = cur;
        }else {                     // !!!  pre 为空,表示当前访问的是头节点
            head = cur;
        }
        cur.left = pre;            // 2. cur 的前驱节点赋值为 pre
        pre = cur;                 // 3. pre 后移,即 pre = cur
        
        dfs(cur.right);
    }
}