数据结构——树|N叉树之孩子双亲表示法——顺序存储结构+链表

N叉树之孩子双亲表示法

在这里插入图片描述
左边是表头结构,相当于一个顺序存储,开始只做了一个顺序结构,发现诸多不便之处,随即开始孩子双亲表示法的学习,这个表示法,需要定义三个结构:

孩子结构
表头结构
树的结构

按理说是应该先有树,再有表头结构,最后才有孩子结构
但树里面包含所有,所以顺序要逆过来

结构体定义

#include<iostream>
using namespace std;
#define MAXTREE 25                           //定义树的最大结点数
//#define NULL 0
typedef char elementtype;                    //
typedef int Status;
//孩子双亲表示法 采取的是顺序表+链表的模式


typedef struct CTnode                         //孩子结点
{
    
    
	int child;                                 //孩子结点的下标
	struct CTnode *next;                       //指向下一个孩子的指针
}*ChildPtr;

typedef struct {
    
    //表头结构指向孩子
	elementtype data;                           //存放在树中的结点的数据
	int parent;                                 //双亲位置的下标
	CTnode *fistchild;                          //指向孩子的指针
}CTBox;

typedef struct{
    
    //树的结构
	CTBox tree[MAXTREE];                        //结点数目 顺序存储 (里面有CTnode *fistchild; (指向孩子还能有数据child ) 能够指向孩子位置)    element data;该位置还有数据
	int r, n;                                  //r是根节点  n是节点数
}CTree;

这里面CTnode *fistchild;struct CTnode *next;很重要分别意味着一个结点的孩子的指针(位于左边起的第一个)和第一个孩子之后可指向下个孩子的指针。

//初始化树 表头结构

//初始化树    表头结构
Status CreatTree(CTree &Tree)
{
    
    
	cout << "输入结点数量" << endl;            //初始化一棵树  首先得知其结点数目
	cin >> Tree.n;
	cout << "输入根结点位置0或-1" << endl;      //其根结点必须存在,所以要得知根节点
	cin >> Tree.r;
	cout << "初始化根的内容" << endl;
	/*for (int i = 0; i < Tree.n; i++)
	{
		cin >> Tree.tree[i].data;//给其根赋值  这里会给所有的根赋值
		Tree.tree[i].fistchild = NULL;//根的孩子不存在
	}*/
	cin >> Tree.tree[Tree.r+1].data;                //给其根赋值
	Tree.tree[Tree.r + 1].parent = Tree.r;          //指向双亲
	Tree.tree[Tree.r + 1].fistchild = NULL;         //其孩子指针不存在
	for (int i = Tree.r + 2; i < Tree.n; i++)
	{
    
    
		Tree.tree[i].data = NULL;                //除了根之外其他结点为空
		Tree.tree[i].fistchild = NULL;
	}
	return 0;
}

这里只定义了根结点且其孩子指针不存在 另外结点的指针也不存在

增加树的内容和结点

Status ADDTree(CTree &Tree)//初始化一棵树之后加结点内容&&扩大树
{
    
    
	int i, j, k,p,q;
	cout << "请输入后续结点内容和其双亲" << endl;
	for (i = Tree.r + 1; i < Tree.n - 1; i++)//从根结点开始0 1 2 3
	{
    
              //-1   i=0开始
		//Tree.tree[i].fistchild = new CTnode;
		
		//先输入后续结点的内容和双亲
		cin >> Tree.tree[i + 1].data>> Tree.tree[i + 1].parent;
		
		//fflush(stdin);
		//新增一个孩子指针
		CTnode *p = new CTnode;//开创空间
		p->child = i + 1;//孩子结点的下标
		p->next = NULL;//下一个为空

		j = Tree.tree[i + 1].parent;//双亲位置
		
		if (!Tree.tree[j].fistchild)//判断双亲的孩子指针是否存在
			Tree.tree[j].fistchild = p;//不存在   则为它的孩子开辟一个空间 使得指向孩子指针的指针完成
		else
		{
    
    
			//否之  其第一个孩子存在的话  那么建立兄弟指针
			CTnode *temp = Tree.tree[j].fistchild;
			while (temp->next)//找到最后一个孩子指针
				temp = temp->next;
			temp->next = p;//使得新增的结点内容是它最右边的孩子
		}

		//Tree.tree[i].fistchild->child = i + 1;
		//Tree.tree[i].fistchild->next = NULL;
	}
	cout << "树已经满,请问是否需要扩大树?" << endl<<"如果需要请输入1,否之输入0"<<endl;
	cin >> j;
	
	if (j == 1)
	{
    
    
		cout << "请输入扩大多少结点:" << endl;
		cin >> k;
		cout << "请输入后续结点内容和其双亲" << endl;
		for (i = Tree.n; i < Tree.n + k; i++)
		{
    
    
			/*cout << "输入双亲位置:" << endl;
			cin >>p;
			//Tree.tree[p].fistchild = new CTnode;
			cout << "请输入后续结点内容" << endl;
			cin >> Tree.tree[i].data;
			Tree.tree[i].parent = p;*/

			
			cin >> Tree.tree[i].data; cin >> Tree.tree[i].parent;//内容和双亲
			q=Tree.tree[i].parent;
			Tree.tree[i].fistchild = NULL;

			CTnode *q = new CTnode;
			q->child = i;//新增结点孩子下标
			q->next = NULL;

			j = Tree.tree[i].parent;
			//Tree.tree[p].fistchild = q;
			if (!Tree.tree[j].fistchild)
				Tree.tree[j].fistchild = q;
			else
			{
    
    
				CTnode *tem = Tree.tree[j].fistchild;
				while (tem->next)
					tem = tem->next;
				tem->next = q;
			}

		}
		Tree.n = Tree.n + k;
	}
	return 0;
}

这里很重要,把这一块理解弄懂了,那后面就好写多了
大体上就是 依次在非根结点的结点上增加内容 和注明其双亲结点
然后再使得其双亲结点的孩子指针指向这个结点
如果其双亲有孩子,那么使得该孩子为最右边那个孩子。

输出一棵树

Status OutputTree(CTree &Tree)
{
    
    
	cout << "index" << "\t" << "data" << "\t" << "parent" << endl;
	if (Tree.n > 0)
	{
    
    
		for (int i = 0; i < Tree.n; i++)
		{
    
    
			cout << i << "\t" << Tree.tree[i].data << "\t" << Tree.tree[i].parent << "\t" << endl;
			//cout << Tree.tree[i].data << endl;
		}
		/*cout << "shuchu" << endl;
		for (int i = 0; i < Tree.n; i++)
		{

			cout << i << "\t" << Tree.tree[i].fistchild->child << "\t" << Tree.tree[i].fistchild->next << "\t" << endl;  这个是左边
		}*/
	}
	else
		return 1;
	return 0;
}

查找孩子结点

Status Findkids(CTree &Tree)
{
    
    
	int i, f;
	cout << "请输入需要寻找其孩子的结点位置" << endl;
	cin >> f;
	if (!Tree.tree[f].fistchild)
		cout << "这个结点没有叶子无孩子" << endl;
	else
	{
    
    
		cout << Tree.tree[f].data << "结点的孩子的下标为:" << endl;
		CTnode *q = Tree.tree[f].fistchild;
		while (q)
		{
    
    
			cout << q->child << endl;
			q = q->next;//原因在于 前面初始化和添加时没有涉及孩子坐标
		}

	}
	return 0;
}

查找双亲结点

// 查找双亲结点
Status Fidparent(CTree &Tree)
{
    
    
	int i, f;
	cout << "请输入需要寻找其双亲的结点位置" << endl;
	//cin >> f;
	//cout << f << endl;
	scanf("%d", &f);
	if (f < 0 && f >= Tree.n && !Tree.tree[f].parent)
	{
    
    
		cout << "其双亲不在" << endl;
	}
	else
	{
    
    
		cout << Tree.tree[f].data << "其双亲结点位置为:" << endl;
		cout << Tree.tree[f].parent << endl;

	}
	return 0;

}

查找兄弟结点

//返回右兄弟位置
Status Fid_R_bro(CTree &Tree,int bro)
{
    
    
	int i, b,bp;
	//cout << "请输入需要寻找其兄弟的结点位置" << endl;
	//scanf("%d", &b);
	if (!Tree.tree[bro].data)
		cout << "此结点无元素" << endl;
	else
	{
    
    
		if (Tree.tree[bro].parent)
		{
    
    
			bp = Tree.tree[bro].parent;
			CTnode *b = Tree.tree[bp].fistchild;
			cout << Tree.tree[bro].data << "结点的兄弟的下标为:" << endl;
			b->child;
			b=b->next;
			while (b)
			{
    
    
				cout << b->child << endl;
				b = b->next;

			}

		}
	}
	return 0;
}

因为在定义的时候是从左起第一个孩子开始查找,所以返回的是其右边的兄弟
当然 如果任意一个结点的兄弟也好找,思路:找到其双亲位置(根结点没有兄弟)——再从左边孩子开始找——输出时判断是否和选择的结点位置i相同,然后输出其他兄弟即可

清空树

//清空树
Status ClearTree(CTree &Tree)
{
    
    
	int i;
	if (Tree.n >= 0) {
    
    
		for (i = 0; i <= Tree.n; i++)
		{
    
    
			Tree.tree[i].data = ' ';
		}
		cout << "树清空" << endl;
	}
	else
	{
    
    
		cout << "树原本不存在" << endl;
	}
	return 0;
}

——————————————————————————
整体输出看看

#include<iostream>
using namespace std;
#define MAXTREE 25                           //定义树的最大结点数
//#define NULL 0
typedef char elementtype;                    //
typedef int Status;
//孩子双亲表示法 采取的是顺序表+链表的模式


typedef struct CTnode                         //孩子结点
{
    
    
	int child;                                 //孩子结点的下标
	struct CTnode *next;                       //指向下一个孩子的指针
}*ChildPtr;

typedef struct {
    
    //表头结构指向孩子
	elementtype data;                           //存放在树中的结点的数据
	int parent;                                 //双亲位置的下标
	CTnode *fistchild;                          //指向孩子的指针
}CTBox;

typedef struct{
    
    //树的结构
	CTBox tree[MAXTREE];                        //结点数目 顺序存储 (里面有CTnode *fistchild; (指向孩子还能有数据child ) 能够指向孩子位置)    element data;该位置还有数据
	int r, n;                                  //r是根节点  n是节点数
}CTree;

//初始化树    表头结构
Status CreatTree(CTree &Tree)
{
    
    
	cout << "输入结点数量" << endl;            //初始化一棵树  首先得知其结点数目
	cin >> Tree.n;
	cout << "输入根结点位置0或-1" << endl;      //其根结点必须存在,所以要得知根节点
	cin >> Tree.r;
	cout << "初始化根的内容" << endl;
	/*for (int i = 0; i < Tree.n; i++)
	{
		cin >> Tree.tree[i].data;//给其根赋值  这里会给所有的根赋值
		Tree.tree[i].fistchild = NULL;//根的孩子不存在
	}*/
	cin >> Tree.tree[Tree.r+1].data;                //给其根赋值
	Tree.tree[Tree.r + 1].parent = Tree.r;          //指向双亲
	Tree.tree[Tree.r + 1].fistchild = NULL;         //其孩子指针不存在
	for (int i = Tree.r + 2; i < Tree.n; i++)
	{
    
    
		Tree.tree[i].data = NULL;                //除了根之外其他结点为空
		Tree.tree[i].fistchild = NULL;
	}
	return 0;
}

Status ADDTree(CTree &Tree)//初始化一棵树之后加结点内容&&扩大树
{
    
    
	int i, j, k,p,q;
	cout << "请输入后续结点内容和其双亲" << endl;
	for (i = Tree.r + 1; i < Tree.n - 1; i++)//从根结点开始0 1 2 3
	{
    
              //-1   i=0开始
		//Tree.tree[i].fistchild = new CTnode;
		//先输入后续结点的内容和双亲
		
		cin >> Tree.tree[i + 1].data>> Tree.tree[i + 1].parent;
		//fflush(stdin);
		//新增一个孩子指针
		CTnode *p = new CTnode;//开创空间
		p->child = i + 1;//孩子结点的下标
		p->next = NULL;//下一个为空

		j = Tree.tree[i + 1].parent;//双亲位置



		if (!Tree.tree[j].fistchild)//判断双亲的孩子指针是否存在
			Tree.tree[j].fistchild = p;//不存在   则为它的孩子开辟一个空间
		else
		{
    
    
			//否之  其孩子存在的话
			CTnode *temp = Tree.tree[j].fistchild;
			while (temp->next)//找到最后一个孩子指针
				temp = temp->next;
			temp->next = p;//为其开一个孩子结点
		}

		//Tree.tree[i].fistchild->child = i + 1;
		//Tree.tree[i].fistchild->next = NULL;
	}
	cout << "树已经满,请问是否需要扩大树?" << endl<<"如果需要请输入1,否之输入0"<<endl;
	cin >> j;
	
	if (j == 1)
	{
    
    
		cout << "请输入扩大多少结点:" << endl;
		cin >> k;
		cout << "请输入后续结点内容和其双亲" << endl;
		for (i = Tree.n; i < Tree.n + k; i++)
		{
    
    
			/*cout << "输入双亲位置:" << endl;
			cin >>p;
			//Tree.tree[p].fistchild = new CTnode;
			cout << "请输入后续结点内容" << endl;
			cin >> Tree.tree[i].data;
			Tree.tree[i].parent = p;*/

			
			cin >> Tree.tree[i].data; cin >> Tree.tree[i].parent;//内容和双亲
			q=Tree.tree[i].parent;
			Tree.tree[i].fistchild = NULL;

			CTnode *q = new CTnode;
			q->child = i;//新增结点孩子下标
			q->next = NULL;

			j = Tree.tree[i].parent;
			//Tree.tree[p].fistchild = q;
			if (!Tree.tree[j].fistchild)
				Tree.tree[j].fistchild = q;
			else
			{
    
    
				CTnode *tem = Tree.tree[j].fistchild;
				while (tem->next)
					tem = tem->next;
				tem->next = q;
			}

		}
		Tree.n = Tree.n + k;
	}
	return 0;
}



//输出一棵树
Status OutputTree(CTree &Tree)
{
    
    
	cout << "index" << "\t" << "data" << "\t" << "parent" << endl;
	if (Tree.n > 0)
	{
    
    
		for (int i = 0; i < Tree.n; i++)
		{
    
    
			cout << i << "\t" << Tree.tree[i].data << "\t" << Tree.tree[i].parent << "\t" << endl;
			//cout << Tree.tree[i].data << endl;
		}
		/*cout << "shuchu" << endl;
		for (int i = 0; i < Tree.n; i++)
		{

			cout << i << "\t" << Tree.tree[i].fistchild->child << "\t" << Tree.tree[i].fistchild->next << "\t" << endl;
		}*/
	}
	else
		return 1;
	return 0;
}

//查找孩子结点
Status Findkids(CTree &Tree)
{
    
    
	int i, f;
	cout << "请输入需要寻找其孩子的结点位置" << endl;
	cin >> f;
	if (!Tree.tree[f].fistchild)
		cout << "这个结点没有叶子无孩子" << endl;
	else
	{
    
    
		cout << Tree.tree[f].data << "结点的孩子的下标为:" << endl;
		CTnode *q = Tree.tree[f].fistchild;
		while (q)
		{
    
    
			cout << q->child << endl;
			q = q->next;//原因在于 前面初始化和添加时没有涉及孩子坐标
		}

	}
	return 0;
}
//查找双亲结点
Status Fidparent(CTree &Tree)
{
    
    
	int i, f;
	cout << "请输入需要寻找其双亲的结点位置" << endl;
	//cin >> f;
	//cout << f << endl;
	scanf("%d", &f);
	if (f < 0 && f >= Tree.n && !Tree.tree[f].parent)
	{
    
    
		cout << "其双亲不在" << endl;
	}
	else
	{
    
    
		cout << Tree.tree[f].data << "其双亲结点位置为:" << endl;
		cout << Tree.tree[f].parent << endl;

	}
	return 0;

}

//返回右兄弟位置
Status Fid_R_bro(CTree &Tree,int bro)
{
    
    
	int i, b,bp;
	//cout << "请输入需要寻找其兄弟的结点位置" << endl;
	//scanf("%d", &b);
	if (!Tree.tree[bro].data)
		cout << "此结点无元素" << endl;
	else
	{
    
    
		if (Tree.tree[bro].parent)
		{
    
    
			bp = Tree.tree[bro].parent;
			CTnode *b = Tree.tree[bp].fistchild;
			cout << Tree.tree[bro].data << "结点的兄弟的下标为:" << endl;
			b->child;
			b=b->next;
			while (b)
			{
    
    
				cout << b->child << endl;
				b = b->next;

			}

		}
	}
	return 0;
}

//清空树
Status ClearTree(CTree &Tree)
{
    
    
	int i;
	if (Tree.n >= 0) {
    
    
		for (i = 0; i <= Tree.n; i++)
		{
    
    
			Tree.tree[i].data = ' ';
		}
		cout << "树清空" << endl;
	}
	else
	{
    
    
		cout << "树原本不存在" << endl;
	}
	return 0;
}

int main()
{
    
    
	CTree tree_1;
	int bro_R;
	cout << "初始化一棵树" << endl;
	CreatTree(tree_1);
	ADDTree(tree_1);
	OutputTree(tree_1);


	Findkids(tree_1);
	Findkids(tree_1);
	Findkids(tree_1);

	Fidparent(tree_1);
	Fidparent(tree_1);
	Fidparent(tree_1);


	cout << "请输入需要准找的右兄弟的结点的位置" << endl;
	cin >> bro_R;
	Fid_R_bro(tree_1,bro_R);
	ClearTree(tree_1);
	OutputTree(tree_1);
	return 0;
}

结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

中途发现深度这个还不是很好求,作为下一步的学习(好像先学二叉树之后就做N叉树方便些,也不太清楚 先啃下去N叉树 二叉树是不是简单些)

返回深度
孩子兄弟表示法 待完

猜你喜欢

转载自blog.csdn.net/weixin_46096297/article/details/111810615