题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
思路一--vector
我们使用vector<Node*>来存储各个节点,利用二叉搜索树本身性质,存储到vector容器中,在分别对[1,size-1]之内的元素做指针处理,再对0,size两个节点的指针做单独变换.这种思路相对来讲比较容易想,时间复杂度也是O(N).就是空间复杂度比较高,为O(N)
#include<iostream>
#include<vector>
using namespace std;
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
class Solution {
public:
vector<Node*> vec;
void dfs(Node* root) {
if (!root) return;
dfs(root->left);
vec.push_back(root);
dfs(root->right);
}
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
if (!root->left && !root->right) {
root->left = root;
root->right = root;
return root;
}
dfs(root); //左指针前驱,右指针后继
for (int i = 1; i < vec.size()-1; ++i) {
vec[i]->left = vec[i - 1];
vec[i]->right = vec[i + 1];
}
int size = vec.size();
if (size>=2) {
vec[0]->left = vec[size-1];
vec[0]->right = vec[1];
vec[size-1]->left= vec[size-2];
vec[size-1]->right = vec[0];
}
return vec[0];
}
};
思路二--head,pre指针变幻
DFS:我们第一次遍历到值最小的节点处时,设置head节点.同时令pre节点指向当前最小节点,以后每次遍历,令pre->right=root,root->left=pre.都令pre=root节点,返回上一层中又是同样的结果,从而达到递归的效果.再在最后的问题函数中更改好pre节点(此时为尾节点)与head节点的指向即可.
class Solution {
public:
Node* pre, *head;
void dfs(Node* root) {
if (!root) return;
dfs(root->left);
if (!pre) {
head = root;
pre = root;
}
else {
pre->right = root;
root->left = pre;
pre = root;
}
dfs(root->right);
}
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
dfs(root);
head->left = pre;
pre->right = head;
return head;
}
};
此算法的时间复杂度为O(N),空间复杂度为O(1)
小结
递归和回溯的思想在本题中体现的淋漓尽致,我们正是使用回溯,提前规定好了pre节点,设置当前root和pre节点之间设置双向指针,在最后遍历完后,使用提前设置的head节点和pre节点来设置循环指针,不得不说,十分巧妙!