将邻接表表示的图转换为孩子兄弟法表示的二叉树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WUDAIJUN/article/details/8462610
/*
	DJ.W 2012.12.06
	代码功能: 将一个邻接表图转换为孩子兄弟法表示的二叉树
*/
#include <iostream>
#include <string.h>
using namespace std; 

//=====Queue===============================================================================================

//队列结构:基于数组的循环队列
//元素类型:整型

typedef struct _Queue
{
	int *pElemt;	//数据区
	int front;		//指向队列首部元素
	int tail;		//指向当前队列尾部元素的下一个位置
	int max_elemt;	//最大元素个数 由使用者指定
	bool bFull;		//队列满标识
	bool bEmpty;	//队列空标识
}Queue;

//队列初始化 front为出口 tail为入口
void InitQueue(Queue &queue, const int max_element)
{
	queue.pElemt	= new int[max_element];
//	ASSERT(queue.pElemt != NULL);
	queue.front		= 0;
	queue.tail		= 0;
	queue.max_elemt = max_element;
	queue.bFull		= false;
	queue.bEmpty	= true;
}

//入队列 注意判满和标识非空
bool EnQueue(Queue &que, const int elemnt)
{
	if (que.bFull)
		return false;

	que.pElemt[que.tail] = elemnt;
	que.tail = (++que.tail)%que.max_elemt;
	if (que.tail == que.front)
		que.bFull = true;
	que.bEmpty = false;

	return true;
}

//出队列 注意判空并标识非满
bool OutQueue(Queue &que, int& element)
{
	if (que.bEmpty)
		return false;

	element = que.pElemt[que.front];
	que.front = (++que.front)%que.max_elemt;

	if (que.front == que.tail)
		que.bEmpty = true;
	que.bFull = false;

	return true;
}

//判空
bool QueueEmpty(Queue& que)
{
	return que.bEmpty;
}

//销毁队列           ***
void DeQueue(Queue que)
{
	delete []que.pElemt;
	que.pElemt = NULL;
}
//=====Tree=========================================================================================

typedef struct _Tree
{
	string strData;
	struct _Tree* pLeftChild;
	struct _Tree* pRightSib;
}TreeNode;

//以parent(lchild, rchild)的形式递归输出树
void DisplayTree(TreeNode* pNode)
{
	if (pNode)
	{
		cout<<pNode->strData.c_str();
		if (pNode->pLeftChild || pNode->pRightSib)
		{
			cout<<"(";
			DisplayTree(pNode->pLeftChild);
			if (pNode->pRightSib)
			{
				cout<<",";
				DisplayTree(pNode->pRightSib);
			}
			cout<<")";
		}
	}
}

//通过顶点名字得到顶点在树中对应保存节点的指针  ******
TreeNode* GetPtrByName(TreeNode* pNode, const char* name)
{
	TreeNode* pRet = NULL;
	if(pNode)
	{
		if (!pNode->strData.compare(name))
			return pNode;
		else
		{		
			if((pRet = GetPtrByName(pNode->pLeftChild, name))==NULL)
				if((pRet = GetPtrByName(pNode->pRightSib, name))==NULL)
					return NULL;
			return pRet;
		}		
	}
	return NULL;
}

//销毁树
void DestroyTree(TreeNode* pNode)
{
	if (pNode)
	{
		DestroyTree(pNode->pLeftChild);
		DestroyTree(pNode->pRightSib);
		delete pNode;
		pNode = NULL;
	}
}

//=====Graph========================================================================================
/*
	图存储结构: 邻接表
	图类型	  :	默认为无向图,因此用户输入每一条边都是双向(可通过修改AddArc很方便转换为有向)
				无权值
*/

//====图结构声明部分
#define MAX_VETEX_NUM 10

//边
typedef struct _Arc
{
	int vetex_index;
	struct _Arc* pNextArc;
	
}Arc;

//顶点
typedef struct _Node
{
	char vetex_name[MAX_VETEX_NUM];
	Arc* pFirstArc;
	bool bVisited;
}Node;

//图
typedef struct _Graph 
{
	Node *pNode;
	int  nNodeNum;
	int  nArcNum;
}Graph;


//======图函数声明部分

//根据顶点名字name返回顶点在图graph的顶点数组中的索引
//如果不存在该顶点 返回-1
int Location(Graph& graph, char* name);	
		
//根据用户输入初始化图	
void InitGraph(Graph &graph);

//在图graph中添加 aloc---bloc边 aloc,bloc为顶点索引
void AddArc(Graph& graph, int aloc, int bloc);

//销毁图 释放节点和弧信息
void DestroyGraph(Graph &graph);

//递归销毁pArc之后的所有边
void DeleteArc(Arc* pArc);

//输出图信息
void DisplayGraph(Graph& graph);

//=====函数实现=====

void InitGraph(Graph &graph)
{
	int numNode, numArc;
	cout<<"input num of vetex: "<<endl;
	cin>>numNode;

	cout<<"input num of Arc: "<<endl;
	cin>>numArc;

	//根据用户输入初始化图
	graph.nArcNum = numArc;
	graph.nNodeNum = numNode;
	graph.pNode = new Node[numNode];

	//初始化顶点
	cout<<"input "<<numNode<<" vetex name: (at most "<< MAX_VETEX_NUM-1<<" charactors)"<<endl;
	for (int i=0; i<graph.nNodeNum; i++)
	{
		cin>>graph.pNode[i].vetex_name;
		graph.pNode[i].bVisited = false;
	}

	//初始化边
	for (i=0; i<graph.nNodeNum; i++)
		graph.pNode[i].pFirstArc = NULL;

	char a[MAX_VETEX_NUM], b[MAX_VETEX_NUM];
	int aLoc, bLoc;
	cout<<"input related vetexs (eg: A B):"<<endl;
	for (i=0; i<graph.nArcNum; i++)
	{
		cout<<i+1<<": ";
		cin>>a>>b;

		aLoc = Location(graph, a);
		bLoc = Location(graph, b);
		if (aLoc == -1 || bLoc == -1 || aLoc==bLoc)
		{
			cout<<"error  node"<<endl;
			i--;
			continue;
		}

		AddArc(graph, aLoc, bLoc);
	}
}

//根据顶点名字得到顶点位置
int Location(Graph& graph, char* name)
{
	for (int i=0; i<graph.nNodeNum; i++)
	{
		if(strcmp(graph.pNode[i].vetex_name,name) == 0)
			return i;
	}
	return -1;
}

//添加位置为aloc, bloc的两个顶点组成的一条边
void AddArc(Graph& graph, int aloc, int bloc)
{
	//添加a节点后的弧
	Arc** pLastArc = &graph.pNode[aloc].pFirstArc;
	while (*pLastArc)
	{
		pLastArc = &((*pLastArc)->pNextArc);
	}
	Arc *pTemp = new Arc;
	pTemp->pNextArc = NULL;
	pTemp->vetex_index = bloc;
	*pLastArc = pTemp;

}

//销毁图    
void DestroyGraph(Graph &graph)
{
	Arc* pArcTemp;
	//先销毁各点之后的弧节点
	for (int i=0; i<graph.nNodeNum; i++)
	{
		pArcTemp = graph.pNode[i].pFirstArc;
		DeleteArc(pArcTemp);
		graph.pNode[i].pFirstArc = NULL;
	}

	//销毁顶点
	delete[] graph.pNode;
	graph.pNode = NULL;
	graph.nArcNum = 0;
	graph.nNodeNum = 0;
}

//删除pArc及和它具有相同弧尾的边
void DeleteArc(Arc* pArc)
{
	if (pArc)
	{
		DeleteArc(pArc->pNextArc);
		delete pArc;
	}
}

//输出图信息
void DisplayGraph(Graph& graph)
{
	cout<<"Vetex Information : "<<endl;
	cout<<"			";
	for (int i=0; i<graph.nNodeNum; i++)
	{
		cout<<graph.pNode[i].vetex_name<<" ";
	}
	cout<<endl;

	cout<<"Arc Information : "<<endl;
	Arc* pArc;
	for (i=0; i<graph.nNodeNum; i++)
	{
		cout<<"For Vetex "<<graph.pNode[i].vetex_name<<": "<<endl;
		pArc = graph.pNode[i].pFirstArc;
		while (pArc)
		{
			cout<<"			"<<graph.pNode[i].vetex_name<<"---"\
				<<graph.pNode[pArc->vetex_index].vetex_name<<endl;
			pArc = pArc->pNextArc;
		}
	}
}

//队列功能测试
void QueueTest()
{
	Queue que;
	int size;
	cout<<"input size of queue : "<<endl;
	cin>>size;
	InitQueue(que, size);
	cout<<"function select : "<<endl;
	cout<<"1: EnQueue. 2:OutQueue. 0:Exit"<<endl;
	cout<<"your choice : ";
	int choice;
	int elemTemp;
	cin>>choice;
	while(true)
	{
		switch(choice)
		{
		case 0:			//退出选项在switch外判断 直接break跳出while
			break;

		case 1:
			cout<<"input elem data : ";
			cin>>elemTemp;
			if(!EnQueue(que, elemTemp))
				cout<<"Error : The queue is empty now! "<<endl;
			break;

		case 2:
			if(!OutQueue(que, elemTemp))
			{
				cout<<"Error : The queue is full now!"<<endl;
				break;
			}
			cout<<"Out elem is "<<elemTemp<<endl;	
			break;

		default:
			cout<<"1: EnQueue. 2:OutQueue. 0:Exit. Input 0, 1 or 2"<<endl;
			break;
		}
		if(!choice)
			break;
		cout<<"Next Operation: ";
		cin>>choice;
	}
	DeQueue(que);
}

//图功能测试
void GraphTest()
{
	Graph graph;
	InitGraph(graph);
	DisplayGraph(graph);
	DestroyGraph(graph);
}

/*
	通过广度优先遍历的方式将图转换为二叉树
*/
void GraphToTree_BFS(Graph& graph, TreeNode* &pt)
{
	//建立队列
	Queue que;
	InitQueue(que, graph.nNodeNum+1);

	//这两个指针用于跟踪当前活动的左子树和右子树
	TreeNode* pCurLeftChild = pt, *pCurRightSib = pt;
	Arc* pArc = NULL;
	for (int i=0; i<graph.nNodeNum; i++)
	{
		if (graph.pNode[i].bVisited)
			continue;

		//跟踪建立树
		if (i == 0)
		{
			//第一个顶点 即为根节点
			pt = new TreeNode;
			pt->strData = graph.pNode[i].vetex_name;
			pt->pLeftChild = NULL;
			pt->pRightSib = NULL;
			pCurLeftChild = pt;
			pCurRightSib  = pt;
		}
		else
		{
			//其他顶点 插在第一个顶点即根节点之后
			pCurRightSib->pRightSib = new TreeNode;
			pCurRightSib->pRightSib->strData = graph.pNode[i].vetex_name;
			pCurRightSib->pRightSib->pLeftChild = NULL;
			pCurRightSib->pRightSib->pRightSib = NULL;
			pCurRightSib = pCurRightSib->pRightSib;
		}

		//顶点入队  标记
		graph.pNode[i].bVisited = true;
		EnQueue(que, i);

		int nextnode;
		//依次弹出队列中各个已经访问过的顶点 遍历访问他们的邻接点
		while(!QueueEmpty(que))
		{
			//根据队列中弹出的元素 修改当前活跃的左子树和右子树(pCurLeftChild pCurRightSib)
			OutQueue(que, nextnode);
			TreeNode* pTemp = GetPtrByName(pt, graph.pNode[nextnode].vetex_name);                     
			pCurLeftChild = pTemp;
			pCurRightSib  = pTemp;

			//遍历该顶点的边。并插到pCurLeftChild的左子树(如果是第一个顶点)或pCurRightSib的右子树(如果是其他顶点)
			bool bFirstNode = true;
			for(Arc* pArc=graph.pNode[nextnode].pFirstArc; \
				pArc; pArc=pArc->pNextArc)
			{
				int ArcIndex = pArc->vetex_index;
				if (!graph.pNode[ArcIndex].bVisited)
				{
					if (bFirstNode)
					{
						//插到当前活跃的左子树左孩子
						pCurLeftChild->pLeftChild = new TreeNode;
						pCurLeftChild->pLeftChild->strData = graph.pNode[ArcIndex].vetex_name;
						pCurLeftChild->pLeftChild->pLeftChild = NULL;
						pCurLeftChild->pLeftChild->pRightSib = NULL;
						pCurLeftChild = pCurLeftChild->pLeftChild;
						bFirstNode = false;
						//修改当前活跃的右子树(因为之后的顶点均为pCurLeftChild的兄弟节点 应放在pCurLeftChild的右孩子)
						pCurRightSib = pCurLeftChild;
					}
					else
					{

						//插到当前活跃右子树的右孩子
						pCurRightSib->pRightSib = new TreeNode;
						pCurRightSib->pRightSib->strData = graph.pNode[ArcIndex].vetex_name;
						pCurRightSib->pRightSib->pLeftChild = NULL;
						pCurRightSib->pRightSib->pRightSib = NULL;
						pCurRightSib = pCurRightSib->pRightSib;
					}
					//入队 并标记
					graph.pNode[ArcIndex].bVisited = true;
					EnQueue(que, ArcIndex);
				}
			}
		}
		//当栈为空时。说明一个连通分支遍历完毕 此时应该恢复pCurLeftChild和pCurRightSib到根节点
		pCurLeftChild = pt;
		pCurRightSib = pt;
	}

	//销毁队列
	DeQueue(que);
}
int main()
{
	//GraphTest();
	//QueueTest();
	TreeNode* pt = NULL;
	Graph graph;
	InitGraph(graph);
	GraphToTree_BFS(graph, pt);
	cout<<endl;
	cout<<"============RESULT=================="<<endl;
	DisplayTree(pt);
	cout<<endl;
	cout<<"===================================="<<endl;

	DestroyGraph(graph);
	DestroyTree(pt);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/WUDAIJUN/article/details/8462610