开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情
二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
思路
遇到树相关的题目,递归肯定是没错的,题目所给的是二叉搜索树,故采用中序遍历,以保证节点的有序性,先把整体的框架写上,然后再想如何对根节点进行操作。
// 中序遍历
void dfs(Node root) {
if(root == null) return;
dfs(root.left); // 左
... // 根
dfs(root.right); // 右
}
根据题目要求,头尾节点也需要指针连接,这种情况无法使用递归实现,所以需要自己定义递归函数,调用该递归函数,然后再将首尾节点连接。
因为需要记录左子节点的状态,以作为前驱节点,故需要全局变量来记录前驱节点。
记前驱节点为 pre,当前节点为 cur,则我们需要做的操作是
- 将 pre 的后继节点赋值为 cur
- cur 的前驱节点赋值为 pre
- 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);
}
}