Description
难度中等:剑指 Offer 36. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
以下面的二叉搜索树为例:

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
Solution
由于二叉搜索树是有序的,链接的后的双向链表也要求是有序的,而在遍历二叉树的算法中,中序遍历是按顺序进行遍历的,所以可以利用中序遍历的思想来解决这个问题。
非递归的中序遍历
使用一个栈来保存遍历的节点,同时使用双指针 fast、slow,fast 指针进行中序遍历,而 slow 则指向 fast 的前一个节点,依次将 fast 与 slow 链接在一起
/*// Definition for a Node.class Node {public int val;public Node left;public Node right;public Node() {}public Node(int _val) {val = _val;}public Node(int _val,Node _left,Node _right) {val = _val;left = _left;right = _right;}};*/class Solution {public Node treeToDoublyList(Node root) {if (root == null) return root;Stack<Node> stack = new Stack<>();Node fast = root, slow = null,res = null;// 中序遍历// 选取二叉树中最小元素的节点,也就是双向链表中的头节点while (fast != null){stack.push(fast);fast = fast.left;}res = stack.peek();while (!stack.isEmpty() || fast != null){if (fast != null){stack.push(fast);fast = fast.left;}else {fast = stack.pop();if (slow != null){ // 将 fast 与 slow 节点进行链接fast.left = slow;slow.right = fast;}slow = fast; // 更新 slow 节点的指向fast = fast.right; // 更新 fast 节点的指向}}res.left = slow;slow.right = res;return res;}}
递归版本的中序遍历
class Solution {Node pre = null;Node res = null;public Node treeToDoublyList(Node root) {if (root == null) return root; // 处理边界条件// 中序遍历,链接节点inOrder(root);// 构建成循环双向链表res.left = pre;pre.right = res;return res;}public void inOrder(Node cur){if ( cur == null) // 边界条件return ;inOrder(cur.left); // 递归左子树if (pre == null) res = cur; // pre 为空,当前节点为链表的 head 节点else pre.right = cur; // pre 不为空,链接 pre 和 cur 节点cur.left = pre;pre = node;inOrder(node.right); // 递归右子树}}
执行结果:通过
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了95.79%的用户
