输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
分析:
方法1:中序遍历+List 存储
从以上结果来看,可以看出这是一个中序遍历的二叉树,要求让我们实现双向链表,实际上就是让每个按中序排列的节点的左指针指向前节点,右指针指向后节点。最简单粗暴的方法就是按中序遍历,然后将遍历的节点存储到 List 集合中,然后遍历 List 集合来改变指针。
时间复杂度:O(n)
空间复杂度:O(n)
/*
// 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 {
//定义集合,存储各个节点
List<Node> list = new ArrayList<>();
public Node treeToDoublyList(Node root) {
//空节点判断
if(root == null){
return null;
}
//中序遍历
dfs(root);
//记录集合长度
int size = list.size();
//定义头节点
Node head = list.get(0);
//遍历使集合中相邻指针互指
for(int i = 0; i < size-1; ++i){
list.get(i).right = list.get(i+1);
list.get(i+1).left = list.get(i);
}
//头尾互指
list.get(0).left = list.get(size-1);
list.get(size-1).right = list.get(0);
return head;
}
//中序遍历
public void dfs(Node node){
//节点为空直接返回
if(node == null){
return;
}
dfs(node.left);
//添加节点
list.add(node);
dfs(node.right);
}
}
方法2: 中序遍历+记录上一节点
虽然 List 集合的方法理解起来很方便,但是创建和遍历 List 集合会消耗一定的时间空间,那么我们是不是可以直接在中序遍历的过程就改变指针呢?其实我们可以一个节点来表示上一节点,因为在回溯时上一节点就不会再遍历了,所以我们直接将当前节点和上一节点互指。
关于头节点,第一个遍历到的叶子结点就是头节点,尾结点直接 root.right 遍历就可以得到,最后头尾互指,返回头节点即可。
时间复杂度:O(n)
空间复杂度:O(n)
/*
// 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 {
//记录头节点和上一节点
Node head, pre;
public Node treeToDoublyList(Node root) {
//空树判断
if(root == null){
return null;
}
//中序遍历
dfs(root);
//查找尾结点
while(root.right != null){
root = root.right;
}
//头尾结点互指
head.left = root;
root.right = head;
//记录头尾节点
return head;
}
public void dfs(Node node){
//左子树不为空,继续遍历左子树
if(node.left != null){
dfs(node.left);
}
//第一次遍历到的叶子结点为头节点
else if(head == null){
head = node;
}
//前后节点指针相指
if(pre != null){
pre.right = node;
node.left = pre;
}
//当前节点赋为上一节点
pre = node;
//右子树不为空,继续遍历右子树
if(node.right != null){
dfs(node.right);
}
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof