1.题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
二叉搜索树的结构如下:
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
2.基本思路
中序遍历二叉排序树会得到一个有序的序列。因此这道题考察的是中序遍历。
如果使用递归来做,那么因为在一个递归中,当前节点p仅能访问到其左子树节点和右子树节点,而p在双向链表中的直接前驱节点不一定是其左子节点,因此需要一个全局变量来记录在访问一个节点p时,前一个访问的节点的指针是谁。
Note:
-
- 假设已经转化完毕,那么你如何确定双向链表的头节点?
- 再遍历一遍 or 更为节省时间的做法? 访问节点p时,如果前一个访问的节点是NULL, 则说明该节点是双向链表的头节点。
- 假设前驱节点是pre,当前访问节点是p,在转化成双向链表时,一定要注意你是用p的左指针指向pre还是右指针指向pre?这个顺序很重要,它直接关系到最外层的中序遍历能否进行下去。正确顺序如下:
p->left=pre;
// 这样才能保证在左子树访问完毕后,还能接着访问右子树,如果你这里使用是p->right=pre
,则遍历就会失败。pre->right=p;
- 假设已经转化完毕,那么你如何确定双向链表的头节点?
3.代码如下
递归代码
void middleVisited(TreeNode *pHead,TreeNode **head,TreeNode **pre){ if(pHead){ middleVisited(pHead->left,head,pre); if( (*pre)==NULL ){ // 如果前一个节点为NULL,则其为头节点 (*head)=pHead; (*pre)=pHead; }else{// 如果前一个访问的节点不是NULL,则该节点为双向链表中p节点的直接前驱节点 pHead->left=(*pre); (*pre)->right=pHead; (*pre)=pHead; } middleVisited(pHead->right,head,pre); } } TreeNode* Convert(TreeNode* pRootOfTree) { if(pRootOfTree==NULL) return NULL; TreeNode *head,*pre,*pHead; pHead=pRootOfTree; head=NULL; pre=NULL; middleVisited(pHead,&head,&pre); return head; }
循环代码
TreeNode* Convert(TreeNode* pRootOfTree) { if(pRootOfTree==NULL) return NULL; TreeNode *p,*pre,*head; p=pRootOfTree; pre=NULL; head=NULL; stack<TreeNode*> S; while(p!=NULL || S.size() ){ while(p!=NULL){ S.push(p); p=p->left; } p=S.top(); S.pop(); if(pre==NULL){ head=p; pre=p; }else{ p->left=pre; pre->right=p; pre=p; } p=p->right; } return head; }