程序员面试金典——解题总结: 9.17中等难题 17.13将二叉查找树转换为双向链表

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>

using namespace std;

/*
问题:有个简单的类似结点的数据结构BiNode,包含两个指向其他结点的指针。数据结构BiNode可以用来表示二叉树(其中node1为左子结点,node2为右子节点)
      或双向链表(其中node1表示前驱结点,node2位后继结点)。编写一个方法,将二叉查找树(用BiNode实现)转换为双向链表。要求所有数值的排序不变,
	  转换操作不得引入其他数据结构(即直接操作原先的数据结构)。
分析:1 此题本质上修改二叉查找树中结点指向,使其符合双向链表的特征
      2 求双向链表的前驱,后继指向 = 求二叉查找树中每个结点的前驱和后继结点
	  对于后继分析:
	  假设当前结点为A,
	  A有右孩子,根据中序:左根右的特点,A的后继结点为A的右子树中的最左边的结点
	  A没有右孩子,则回溯到A的父节点P,若P有左孩子,则P为A的后继(等同于 A为P的左孩子,根据左根右,当前结点为左,父节点为根,则后继为根);
	               若P没有左孩子,继续重复上述操作。
	  根据比如找到关系A的后继为B,那么B的前驱为A;找到所有结点的后继关系,则前驱关系也确定。
	  但该方法需要一个父节点指向。不符合题目要求。

书上解法:凡是和树相关的操作,第一个要想到递归。
      树的左右两半在双向链表中分别形成自己的子部分,递归将左子树和右子树转换成双向链表,再讲左子树的末尾指向根节点,将根节点
	  指向右子树的根节点即可

输入:
7 (节点个数n,下面第一行表示n个结点的值,接下来下面有n行,第i行代表第i个结点的左右孩子,d表示有两个孩子,紧跟左右孩子结点编号;
                                                                                        r表示只有右孩子,紧跟右孩子结点编号
																						l表示只有左孩子,紧跟左孩子结点编号
																						z表示没有孩子结点)
4 2 5 1 3 6 0
d 2 3
d 4 5
r 6
l 7
z
z
z
输出:
0 1 2 3 4 5 6(双向链表从头到尾的遍历结果)

关键:
1 凡是和树相关的操作,第一个要想到递归。
      树的左右两半在双向链表中分别形成自己的子部分,递归将左子树和右子树转换成双向链表,再讲左子树的末尾指向根节点,将根节点
	  指向右子树的根节点即可
2 
BiNode* turnToList(BiNode* root)
{
	if(NULL == root)
	{
		return NULL;
	}
	BiNode* leftPart = turnToList(root->node1);
	BiNode* rightPart = turnToList(root->node2);
	//如果左子树不空,拼接:左子树的尾结点和根节点
	if(NULL != leftPart)
	{
		concact( getTail(leftPart) , root);
	}

	//拼接:根节点和右子树的头结点
	if(NULL != rightPart)
	{
		concact(root , rightPart);
	}

	//拼接好了之后,需要返回真正的根节点:如果左边不空,返回做部分,否则返回根节点
	return leftPart != NULL ? leftPart : root;
}
*/

typedef struct BiNode
{
	BiNode* node1;//左孩子,前驱结点
	BiNode* node2;//右孩子,后继结点
	int value;
}BiNode;
const int MAXSIZE = 10000;
int gIndex;
BiNode gNodeArray[MAXSIZE];
BiNode* createNode()
{
	++gIndex;
	gNodeArray[gIndex].node1 = gNodeArray[gIndex].node2 = NULL;
	return &gNodeArray[gIndex];
}


//寻找二叉查找树的尾结点,其实就是最右边的结点
BiNode* getTail(BiNode* root)
{
	if(NULL == root)
	{
		return NULL;
	}
	while(root->node2)
	{
		root = root->node2;
	}
	return root;
}

void concact(BiNode* frontNode , BiNode* backNode)
{
	//同时对前驱和后继进行赋值
	frontNode->node2 = backNode;
	backNode->node1 = frontNode;
}

BiNode* turnToList(BiNode* root)
{
	if(NULL == root)
	{
		return NULL;
	}
	BiNode* leftPart = turnToList(root->node1);
	BiNode* rightPart = turnToList(root->node2);
	//如果左子树不空,拼接:左子树的尾结点和根节点
	if(NULL != leftPart)
	{
		concact( getTail(leftPart) , root);
	}

	//拼接:根节点和右子树的头结点
	if(NULL != rightPart)
	{
		concact(root , rightPart);
	}

	//拼接好了之后,需要返回真正的根节点:如果左边不空,返回做部分,否则返回根节点
	return leftPart != NULL ? leftPart : root;
}

void print(BiNode* root)
{
	if(NULL == root)
	{
		cout << "List is null" << endl;
		return;
	}
	BiNode* node = root;
	while(node)
	{
		cout << node->value << " ";
		node = node->node2;
	}
	cout << endl;
}

void process()
{
	int nodeNum;
	int value;
	string childFlag;
	int leftIndex;
	int rightIndex;
	while(cin >> nodeNum)
	{
		gIndex = 0;
		for(int i = 0 ; i < nodeNum ; i++)
		{
			cin >> value;
			BiNode* node = createNode();
			node->value = value;
		}
		//构建结点指向
		for(int i = 1 ; i <= nodeNum ; i++)
		{
			cin >> childFlag;
			if("d" == childFlag)
			{
				cin >> leftIndex >> rightIndex;
				gNodeArray[i].node1 = &gNodeArray[leftIndex];
				gNodeArray[i].node2 = &gNodeArray[rightIndex];
			}
			else if("l" == childFlag)
			{
				cin >> leftIndex;
				gNodeArray[i].node1 = &gNodeArray[leftIndex];
			}
			else if("r" == childFlag)
			{
				cin >> rightIndex;
				gNodeArray[i].node2 = &gNodeArray[rightIndex];
			}
		}

		//结点指向构建好了之后,下面转换为双向链表
		BiNode* root = &gNodeArray[1];
		BiNode* newHead = turnToList(root);
		print(newHead);
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值