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叉树 二叉树是不是简单些)
返回深度
孩子兄弟表示法 待完