搜索二叉树就是如下图:
转换成双向链表,如下图:
有三种解法:
1 增加一个额外的数据结构,记录头尾节点
2 通过查找尾节点,连接头尾节点
3 构成循环链表
我觉得原书说的有点啰嗦,其实归结最终的思想是:
需要知道左右子树的头尾节点,就可以把根节点和左右子树节点连接成双向链表了。
方法一如下,使用额外数据结构记录头尾节点:
//data structures
struct BiNode
{
int val;
BiNode *node1;
BiNode *node2;
BiNode():node1(nullptr), node2(nullptr), val(0){}
BiNode(int x):node1(nullptr), node2(nullptr), val(x){}
};
struct NodePair
{
BiNode *head;
BiNode *tail;
NodePair():head(nullptr), tail(nullptr){}
NodePair(BiNode *h, BiNode *t):head(h), tail(t){}
};
//solution 1: solve with additional data structure
void concat(BiNode *x, BiNode *y)
{
x->node2 = y;
y->node1 = x;
}
NodePair *convert(BiNode *root)
{
if (root == nullptr) return nullptr;
NodePair *part1 = convert(root->node1);
NodePair *part2 = convert(root->node2);
if (part1) concat(part1->tail, root);
if (part2) concat(root ,part2->head);
return new NodePair
(part1==nullptr? root:part1->head, part2==nullptr? root:part2->tail);
}
方法二,因为头节点都是知道的,所以可以查找尾节点,增加个getTail的函数就可以了。不过时间复杂度是O(n*n)。前面的是O(n)
//solution 2:
BiNode *getTail(BiNode *r)
{
if (!r) return nullptr;
while (r->node2)
{
r = r->node2;
}
return r;
}
//关键就是保留或者取得每一次递归回来的头尾指针
BiNode *convert2(BiNode *root)
{
if (!root) return nullptr;
BiNode *part1 = convert2(root->node1);
BiNode *part2 = convert2(root->node2);
if (part1) concat(getTail(part1), root);
if (part2) concat(root, part2);//注意:而不是getTail(part2));
return part1==nullptr? root:part1;
}
方法三最困难,因为概念转换不容易,如果对递归不够熟悉,那么就很容易出错。
注意下面程序注释的地方:
//solution 3
BiNode *convertToCircle(BiNode *root)
{
if (!root) return nullptr;
BiNode *p1 = convertToCircle(root->node1);
BiNode *p2 = convertToCircle(root->node2);
if (!(root->node1) && !(root->node2))
{
root->node1 = root;
root->node2 = root;
return root;
}
//假设之前的p1 和 p2都已经是double linklist了,然后再计算
//注意:这个关键的假设,才能处理好递归;而不是按照原来的二叉树来思考下面的计算
BiNode *tail;// = p2? p2->node1:nullptr;
if (p2)//因为已经假设是double linklist了,那么两个node1和node2都肯定不会为空
{
tail = p2->node1;
}
//join left to right
if (p1) concat(p1->node1, root);
else concat(p2->node1, root);
//join right to left
if (p2) concat(root, p2);
else concat(root, p1);
if (p1 && p2) concat(tail, p1);
return p1? p1:root;
}
也可以改成这样,我觉得逻辑更清晰一点:
BiNode *convertToCircle2(BiNode *root)
{
if (!root) return nullptr;
BiNode *p1 = convertToCircle2(root->node1);
BiNode *p2 = convertToCircle2(root->node2);
if (!p1 && !p2)
{
root->node1 = root;
root->node2 = root;
return root;
}
if (p1 && p2)
{
BiNode *tail = p2->node1;
concat(p1->node1, root);
concat(root, p2);
concat(tail, p1);
}
else if (p1 && !p2)
{
concat(p1->node1, root);
concat(root, p1);
}
else if (!p1 && p2)
{
concat(p2->node1, root);
concat(root, p2);
}
return p1? p1:root;
}
把循环链表转换成不循环的:
BiNode *deCircle(BiNode *r)
{
BiNode *h = convertToCircle(r);
h->node1->node2 = nullptr;
h->node1 = nullptr;
return h;
}
BiNode *deCircle2(BiNode *r)
{
BiNode *h = convertToCircle2(r);
h->node1->node2 = nullptr;
h->node1 = nullptr;
return h;
}
测试程序:
//helper functions
void printBST(BiNode *r)
{
if (!r) return;
printBST(r->node1);
cout<<r->val<<" ";
printBST(r->node2);
}
void printDouLink(BiNode *d)
{
while (d)
{
cout<<d->val<<" ";
d = d->node2;
}
cout<<endl;
}
int main()
{
BiNode *root;
BiNode r0(0);
BiNode r1(1);
BiNode r2(2);
BiNode r3(3);
BiNode r4(4);
BiNode r5(5);
BiNode r6(6);
r4.node1 = &r2;
r4.node2 = &r5;
r5.node2 = &r6;
r2.node1 = &r1;
r2.node2 = &r3;
r1.node1 = &r0;
root = &r4;
cout<<"Binary Search Tree:";
printBST(root);
cout<<endl;
cout<<"Before convert:";
printDouLink(root);
//solution 1
//NodePair *np = convert(root);
//root = np->head;
//solution 2
//root = convert2(root);
//solution 3
root = deCircle2(root);
cout<<"After converted:";
printDouLink(root);
system("pause");
return 0;
}
运行结果: