题目描述
给定一棵二叉排序树(BST),将该树转换成一棵双向循环链表。请看下面的图示说明,你可以更清楚的了解题意。
BST的结构定义如下:
struct node {
int data;
struct node* left;
struct node* right;
};
typedef struct node Node;
图1)一棵存储1-5的二叉搜索树(BST)
图2)根据上面的BST转换得到的有序循环链表。其中,树的左右孩子指针替换成了pre和next指针,分别指向链表的前一个和后一个结点。
分析
看到这个题目,相信大多数人第一反应就是中序遍历这棵二叉树,同时改变树中结点的left和right指针。这里需要额外考虑的是如何将最后一个结点的right指针指向第一个结点,如下图所展示的那样。
图3)一个双向循环链表
以中序遍历遍历一棵二叉树的时候,每遍历到一个结点,我们就可以修改该结点的left指针指向前一个遍历到的结点,因为在后续操作中我们不会再用到left指针;与此同时,我们还需要修改前一个遍历结点的right指针,让前一个遍历结点的right指针指向当前结点。比如我们遍历到结点2,则我们修改结点2的left指针指向结点1,同时需要修改结点1的right指针指向结点2。
需要注意一点,这里的前一个遍历结点不是当前结点的父结点,而是当前结点的前一个比它小的结点。
看似问题已经解决,慢着,我们其实落下了重要的两步。1)我们没有对头结点head赋值。 2)最后一个结点的right指针没有指向第一个结点。
解决这两个问题的方案非常简单:在每次递归调用的时候,更新
当前遍历结点的right指针让其指向头结点head,同时更新
头结点head的left指针让其指向当前遍历结点。当递归调用结束的时候,链表的头尾结点会指向正确的位置。不要忘记只有一个结点的特殊情况,它的left和right指针都是指向自己。
图4)只有一个结点的双向循环链表
这个解法非常的精巧,因为该算法是对中序遍历的一个改进,因此它的时间复杂度为O(N),N为结点数目。当然,相比中序遍历,我们在每次递归调用过程中增加了额外的赋值操作。
// 这是一个改进的中序遍历
// prev (初始化为NULL) 用于跟踪前一个遍历结点
// 当递归结束时,head指向链表的头部
void treeToDoublyList(Node *p, Node *& prev, Node *& head) {
if (!p) return;
treeToDoublyList(p->left, prev, head);
// 当前结点p的left指向前一个结点prev
p->left = prev;
if (prev)
prev->right = p; //前一个结点的right指向当前结点
else
head = p; //如果前面没有结点,则设置head为当前结点(当前结点为最小的结点)。
//递归结束后,head的left指针指向最后一个结点,最后一个结点的右指针指向head结点。注意保存p的right指针,因为在后面代码中会修改该指针。
Node *right = p->right;
head->left = p;
p->right = head;
prev = p;//更新前一个结点
treeToDoublyList(right, prev, head);
}
//主函数,初始化prev和head为NULL
Node* treeToDoublyList(Node *root) {
Node *prev = NULL;
Node *head = NULL;
treeToDoublyList(root, prev, head);
return head;
}
代码解析
这个递归算法挺复杂的,还是从函数功能上来理解会容易懂一点。函数treeToDoublyList(Node *p, Node *&prev, Node *&head)的功能是将以p为根结点的树转换成有序的双向循环链表。函数执行前,prev总是指向遍历到p之前的前一个结点。函数执行完成后,prev指向p,head指向链表头结点。
以上面图中的二叉树为例,treeToDoublyList(Node *p, Node *&prev, Node *&head)过程如下:根结点为4,调用treeToDoublyList(p->left, prev, head)遍历完左子树后,左子树构成有序双向循环链表1=2=3,prev指向的是结点3,head指向当前双向循环链表的头结点1。更新p->left=prev,即4的left指针指向3,设置prev->right=p,即3的right指针指向4。同时更新head结点1的left为节点4,4的right指针更新为1,则此时双向循环链表变成1=2=3=4,接下来递归遍历右子树结点,保持双向循环链表的性质。