搜索二叉树就是如下图:
转换成双向链表,如下图:
有三种解法:
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; }
运行结果: