题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
什么是二叉搜索树 ?
二叉搜索树是一棵有序的二叉树,所以我们也可以称它为二叉排序树。具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点;若它的右子树不为空,那么右子树上所有值均大于它的根节点。它的左子树和右子树分别也为二叉搜索树。
二叉搜索树的中序遍历是:左=>根=>右; 二叉搜索树的中序遍历从小到大是有序的。
解题思路:
本文解法基于性质:二叉搜索树的中序遍历为 递增序列 。
将 二叉搜索树 转换成一个 “排序的循环双向链表” ,其中包含三个要素:
排序链表: 节点应从小到大排序,因此应使用 中序遍历 “从小到大”访问树的节点。
双向链表: 在构建相邻节点的引用关系时,设前驱节点 pre 和当前节点 cur ,不仅应构建 pre.right = cur ,也应构建 cur.left = pre 。
循环链表: 设链表头节点 head 和尾节点 tail ,则应构建 head.left = tail 和 tail.right = head 。
中序遍历 为对二叉树作 “左、根、右” 顺序遍历,递归实现如下:
根据以上分析,考虑使用中序遍历访问树的各节点 cur ;并在访问每个节点时构建 cur 和前驱节点 pre 的引用指向;中序遍历完成后,最后构建头节点和尾节点的引用指向即可。
算法流程:
midOrderTree(cur): 递归法中序遍历;
1、终止条件: 当节点 cur 为空,代表越过叶节点,直接返回;
2、递归左子树,即 midOrderTree(cur.left) ;
3、构建链表:
(1)当 pre 为空时: 代表正在访问链表头节点,记为 head ;
(2)当 pre 不为空时: 修改双向节点引用,即 pre.right = cur , cur.left = pre ;
(3)保存 cur : 更新 pre = cur ,即节点 cur 是后继节点的 pre ;
4、递归右子树,即 midOrderTree(cur.right) ;
treeToDoublyList(root):
1、特例处理: 若节点 root 为空,则直接返回;
2、初始化: 空节点 pre ;
3、转化为双向链表: 调用 midOrderTree(root) ;
4、构建循环链表: 中序遍历完成后,head 指向头节点, pre 指向尾节点,因此修改 head 和 pre 的双向节点引用即可;
5、返回值: 返回链表的头节点 head 即可;
// 打印中序遍历
void dfs(Node* root) {
if(root == nullptr) return;
dfs(root->left); // 左
cout << root->val << endl; // 根
dfs(root->right); // 右
}
class Solution {
public:
Node *head = NULL,*pre = NULL;
Node* treeToDoublyList(Node* root)
{
//边界值
if(root == NULL) return NULL;
midOrderTree(root);
// 题目要求头尾连接
head->left = pre;
pre->right = head;
// 返回头节点
return head;
}
void midOrderTree(Node* cur)
{
if(cur == NULL) return; // 递归结束条件
midOrderTree(cur->left);
// 如果pre为空,就说明是第一个节点,头结点,然后用head保存头结点,用于之后的返回
if(pre == NULL) head = cur;
// 如果不为空,那就说明是中间的节点。并且pre保存的是上一个节点,
// 让上一个节点的右指针指向当前节点
else if(pre != NULL) pre->right = cur;
// 再让当前节点的左指针指向父节点,也就连成了双向链表
cur->left = pre;
// 保存当前节点,用于下层递归创建
pre = cur;
midOrderTree(cur->right);
}
};
思路:数组
利用二叉搜索树的特点:中序遍历为升序。将二叉搜索树的结点按升序存入数组中,借助数组为二叉搜索树的每个结点建立索引,再遍历数组为每个结点修改左右指针,需要考虑只有一个结点以及首尾结点指针修改的特殊情况。
class Solution {
public:
vector<Node*> midorder; //存放中序遍历的节点
Node* treeToDoublyList(Node* root)
{
if(root == NULL) return NULL;
midOrderTree(root);
//判断是否为空树
//if(midorder.size() < 1) return nullptr;
for(int i=0;i<midorder.size()-1;i++)
{
//形成双向链表
midorder[i]->right = midorder[i+1];
midorder[i+1]->left = midorder[i];
}
//链表的头尾相连
midorder.front()->left = midorder.back();
midorder.back()->right = midorder.front();
return midorder.front();
}
//中序遍历
void midOrderTree(Node* root)
{
//递归结束条件
if(root == NULL) return;
midOrderTree(root->left); //递归左子树
midorder.push_back(root);
midOrderTree(root->right); //递归右子树
}
};