面试题:搜索二叉树转双向链表

搜索二叉树就是如下图:

转换成双向链表,如下图:

有三种解法:

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;
}


运行结果:

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值