方法一
已知将二叉搜索树进行中序遍历可以得到由小到大的顺序排列,因此本题最直接的想法就是进行中序遍历。
将中序遍历的结果用数组存储下来,得到的数组是有从小到大顺序的。最后将数组中的结点依次连接即可。
示意图如下:
具体代码如下:
/* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: vector<TreeNode*> TreeList;//定义一个数组,根据中序遍历来存储结点。 void inorder(TreeNode* root){ if (!root) return; inorder(root->left); TreeList.push_back(root); inorder(root->right); } TreeNode* Convert(TreeNode* pRootOfTree) { if (!pRootOfTree) return pRootOfTree; inorder(pRootOfTree); for (int i=0;i<TreeList.size()-1;i++){ //根据数组中的顺序将结点连接,注意i的范围。 TreeList[i]->right = TreeList[i+1]; TreeList[i+1]->left = TreeList[i]; } return TreeList[0];//数组的头部存储的是双向链表的第一个结点。 } };
时间复杂度:O(N),等于中序遍历的时间复杂度。
空间复杂度:O(N),开辟了一个数组来存储结点。
方法二
依旧采取中序遍历。
根据题目的要求1,不能创建新的结点,而方法一的数组中存储的其实是结点,并不满足题意;所以需要在中序遍历的过程中,直接对结点的指针进行调整。
调整的思路如下:
(1)使用一个指针preNode指向当前结点root的前继。(例如root为指向10的时候,preNode指向8,如图所示:)
(2)对于当前结点root,有root->left要指向前继preNode(中序遍历时,对于当前结点root,其左孩子已经遍历完成了,此时root->left可以被修改。);同时,preNode->right要指向当前结点(当前结点是preNode的后继),此时对于preNode结点,它已经完全加入双向链表。
具体代码如下:
/* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: TreeNode* preNode;//preNode一定是全局变量。 TreeNode* Convert(TreeNode* pRootOfTree) { if (!pRootOfTree) return pRootOfTree; TreeNode* p = pRootOfTree; while (p->left) p = p->left;//找到双向链表的开头。 inorder(pRootOfTree); return p; } void inorder(TreeNode* root){ if (!root) return; inorder(root->left); //当前结点中需要进校的调整。 root->left = preNode; if (preNode){ preNode->right = root; } preNode = root;//更新preNode,指向当前结点,作为下一个结点的前继。 inorder(root->right); } };
时间复杂度:O(N),等于中序遍历的时间复杂度。
空间复杂度:O(N)。没有申请新的空间,但是递归调用栈占用了N的空间。