树的双亲表示法存储结构

我懵逼了,硬生生调试了两个多小时,都是细节呀

以下代码:

#include<stdio.h>
#include<stdlib.h>
#define MAXNUM 10
//双亲表示法
typedef char elem;
typedef struct _ptNode {
	int num;
	elem data;
	int parent;
}PTNode;
PTNode tr[MAXNUM] = {
	{ 0,'A',-1 },
	{ 1,'B',0 },
	{ 2,'C',0 },
	{ 3,'D',0 },
	{ 4,'E',1 },
	{ 5,'F',1 },
	{ 6,'G',2 },
	{ 7,'H',4 },
	{ 8,'I',4 },
	{ 9,'K',5 }
};
typedef struct _tree {
	PTNode tr[MAXNUM];
	int root;//表示根节点的位置
	int n;
}Tree;
//这种方法只能得出每个结点的子节点
void PrintTree(Tree t, int n) {
	for (int i = 0; i < n; i++) {
		if (!i) {
			printf("根结点%c的内部结点为", t.tr[i].data);
			for (int i = 1; i < n; i++) {
				if (t.tr[i].parent == 0)
					printf("%c ", t.tr[i].data);
			}
			printf("\n");
		}
		else {
			printf("结点%c的内部结点为", t.tr[i].data);
			int tag = 0;
			for (int j = 1; j < n; j++) {
				if (t.tr[j].parent == i) {
					tag = 1;
					printf("%c ", t.tr[j].data);
				}
			}
			if (!tag)
				printf("NULL\n");
			else printf("\n");
		}
	}
}
int main() {
	int n;
	printf("请输入树的结点数(最大为10):");
	scanf("%d", &n);
	//在第一个scanf()后,要用getchar()来抵消上面缓冲区产生的'\n'
	//在scanf("%c",&c)的缓冲区中中断接受一个字符,但若用户输入好几个字符,那么ch将只接受一个,
	//而这时大量字符滞留在缓冲区内,下次再调用scanf时,还没有输入东西,缓冲区内的内容直接传到了scanf内,这样就造成了严重的错误。
	getchar();
	Tree tree = {
	  // tree.n = n,tree.root=0    //数组赋值不应该加tree.n=,应该是.n=,否则就默认为给数组最前面的那个数赋值
		{NULL},.root = 0,.n = n
	};
	//tree.n = 5;  //也可以这样逐个赋值
	for (int i = 0; i < n; i++) {
		tree.tr[i] = tr[i];
		//像这种套嵌结构体的话必须逐个赋值
	}

	//结构体和数组只能在声明的时候赋初值,否则只能用表达式赋值
	//对树的结构进行赋值
	for (int i = 1; i < tree.n; i++) {
		printf("请输入第%d个内部结点的数据和双亲:", i);
		tree.tr[i].num = i;
		scanf("%c", &tree.tr[i].data);
		getchar();
		//setbuf(stdin, NULL);//把缓冲区和流关联起来的函数
		scanf("%d", &tree.tr[i].parent);
		getchar();//一定要在后面加上getchar()来吸收掉'\n'
		while (tree.tr[i].parent > i || tree.tr[i].parent < 0) {
			printf("输入的双亲结点有误,请重输:\n");
			scanf("%d", &tree.tr[i].parent);
		}
	}
	PrintTree(tree, n);
	return 0;
}

关于树的学习笔记1:

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 40
//树的定义,概念
//树是有n(n>=0)个结点的有限集,当n=0是为空树。非空树中:有且仅有一个根(Root)结点,
//当n大于1时,其余结点可以分为m个有限集,每个集合本身又可以称为根的子树(SubTree)
//结点拥有的子树数称为结点的度(Degree),树的度取树内各节点度的最大值

//度为0的结点为叶结点(Leaf) 或 终端结点
//度不为0的结点称为分支结点,除根节点外,分支结点也称为内部结点
//结点子树的根称为结点的孩子(Child),该节点称为孩子的双亲(Parent),同一双亲的孩子互称为兄弟(Sibleing)
//结点的祖先是从根到该结点所经分支上的所有结点
//结点的层次(Level)从根起,根为第一层,根的孩子为第二层,树中结点的最大层称为该数的深度(Depth)
//树中结点各子树丛左到右是有次序的,则称该树为有序树,否则为无序树
//森林(Forest)是m棵互不相交的树的集合,对树的每个结点来说,其子树的集合即为森林

//树的存储结构
//一:双亲表示法,以双亲作为索引的关键词
typedef int Elem;
typedef char Elem1;
typedef struct PTnode {
	int num;
	Elem data;//结点数据
	int parent;//双亲位置(根结点的双亲位置一般为-1)
	//根节点的parent的返回值是-1.
	int child1;
	int child2;
}PTnode;
typedef struct {
	PTnode nodes[MAXSIZE];
	int r;			//根的位置(一般是0)
	int n;		//结点数目
};
//二:孩子表示法
//1:根据树的度声明足够的空间存放子树指针的结点,容易造成空间上的浪费
//三:双亲孩子表示法(主流)
//线性表与链表的结合
typedef struct CTnode {//定义孩子结点
	int child;				//孩子结点的下标
	struct CTnode*next;  //指向下一孩子结点的指针
}*ChildPtr;//用链表来表示结点的兄弟
//树的结点结构
typedef struct {
	Elem1 data;			//存放在树中结点的数据
	int parent;			//存放双亲的下标
	ChildPtr firstchild;   //指向第一个孩子的指针
}CTbox;//双亲表示法 用顺序表来表示 每个结点的双亲

//树结构
typedef struct {
	CTbox nodes[MAXSIZE];//结点数组,表示每个结点的序号
	int r;			//根的位置(一般是0),结点数组的下标
	int n;		//结点数目
};

//------------------------------------------------------------
//二叉树(Binary Tree)是n个结点,(一般B代表二进制,H代表16进制),
//其可以为空集合也可以为2以内的两个结点,故二叉树的度最大为2,其左子树和右子树的顺序是不能颠倒的,即使某结点只有一个子树,也有左子树和右子树的区分
//二叉树的5种基本形式   1:空二叉树  2:只有一个根结点  3,4:只有左子树或只有右子树  5:既有左子树又有右子树
//二叉树要区分左右,3个结点的二叉树有5种形态,两层1种,三层4种
//特殊二叉树   1:斜树(一枝流斜下去)  
// 2:满二叉树(所有分支结点都有左子树和右子树,并且所有的叶子都在同一层上(最后一层都为叶子)
//若二叉树按层序编号,对于编号为i(1<=i<=n)的结点与同深度的满二叉树中编号为i的结点位置完全相同,则这棵二叉树称为完全二叉树
//完全二叉树特点:1:叶子结点只能出现在最下两层,最下层叶子在左面的连续结构,倒数第二层叶子在右面连续结构
//若某结点度为1,则其只有左孩子(右孩子的话就忽略了左孩子的序号)。			满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树

//二叉树的性质
//二叉树的第i层上最多有2^(i-1)个结点,深度为k的二叉树最多有2^k-1个结点(和二进制很像)
//对任意一个二叉树,若终端结点(叶子)为n0,度为2的结点数为n2,则n0=n2+1,设度为1的结点为n1,二叉树连接数(连接的竖线数)为n-1,等于n1+2*n2。
//所以n-1=n1+2*n2,n0+n1+n2-1=n1+n2+n2.。所以n0=n2+1.
//具有n个结点的完全二叉树的深度为[log2n]+1(取下限,向下取整),
//因为深度为k的满二叉树结点数为n=2^k-1, 倒推得其深度为k=log2(n+1),对完全二叉树来说,倒数第二层的满二叉树结点数为log2(k-1)
//对一个有n个结点的完全二叉树的结点按层序编号,i等于1则i是二叉树的根,若i>1,则双亲是结点[i/2](取下限)。若2i>n,则结点i无左孩子,否则左孩子为结点2i,如果2i+1>n,则结点无右孩子,否则右孩子是结点2i+1(画图理解)

//二叉树的存储结构(一般采用链式存储结构)
 //二叉链表(国际常用),空结点用^来表示
typedef struct BiTNode {
	Elem data;
	struct BiTNode*lchild, *rchild;
}BiTNode,*BiTree;

//二叉树的遍历
//二叉树的遍历是从根节点出发,按某种 次序 依次 访问二叉树中的所有结点,并使每个结点只被访问一次
//遍历分为 前序遍历,中序遍历,后序遍历,层序遍历
//前序遍历:若树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树(有左子树就访问到左子树上)
//中序遍历:若树为空,则空操作返回,否则从中结点开始(并不先访问根节点),中序遍历根节点下的左子树,然后访问根节点(没有左子树的情况下就默认为其为根结点),再中序遍历右子树
//后序遍历:若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点
//层序遍历:若树为空,则空操作返回,否则从树的根节点开始,从上到下,从左到右对结点 逐层 逐个访问

//创建一颗二叉树,约定用户用前序遍历的方式输入数据
CreateBiTree(BiTree*T) {
	char c;
	scanf("%c", &c);
	if (' ' == c) {
		*T = 0;
	}//空格代表让其结点为空,让上一个结点为叶子
	else {
		*T = (BiTree)malloc(sizeof(BiTNode));//给结点的空间赋值
		(*T)->data = c;
		CreateBiTree(&((*T)->lchild));
		//注意在这里代入的是树的结点的地址
		CreateBiTree(&((*T)->rchild));
	}
}
//访问二叉树结点后的具体操作
visit(char c,int level) {
	printf("%c位于第%d层\n", c, level);
}
//前序遍历二叉树
PreOrderTraverse(BiTree T,int level) {
	if (T) {
		visit(T->data, level);
		PreOrderTraverse(T->lchild, level+1);
		PreOrderTraverse(T->rchild, level + 1);
		/*中序遍历
		PreOrderTraverse(T->lchild, level + 1);
		visit(T->data, level);
		PreOrderTraverse(T->rchild, level + 1);
		*/

		/*后序遍历
		PreOrderTraverse(T->lchild, level + 1);
		PreOrderTraverse(T->rchild, level + 1);
		visit(T->data, level);
		*/
	}
}

//线索二叉树
//普通二叉树大量浪费空间,每个叶子浪费两个指针的空间,每个一度的结点浪费一个指针的空间
//n个结点的二叉链表含有n+1(2n-(n-1)=n+1)个空指针域,利用其空指针域存放结点在某遍历次序下的前驱和后继
//线索二叉树存储结构:lchild ltag data rtag rchild
//当ltag 为0时指向该节点的左孩子,为1时指向该节点的前驱 。rtag为0时,指向该节点的右孩子,为1时指向后继

//线索存储标志位
//Link(0):表示指向左右孩子的指针
//Thread(1):表示指向前驱后继的元素
typedef enum { Link, Thread }PointerTag;

typedef struct BiThrNode {
	char data;
	struct BiThrNode*lchild, *rchild;
	PointerTag ltag;
	PointerTag rtag;
}BiThrNode,*BiThrTree;
//定义全局变量,指向刚刚访问过的结点
BiThrTree pre;
//创建一颗二叉树,约定用户按前序遍历的方式输入数据
CreateBiThrTree(BiThrTree *T) {
	char c;
	scanf("%c", &c);
	if (' ' == c) {
		*T = 0;
	}
	else {
		*T = (BiThrTree)malloc(sizeof(BiThrNode));
		(*T)->data = c;
		(*T)->ltag = Link;//树默认为指向结点的enum
		(*T)->rtag = Link;

		CreateBiThrTree(&(*T)->lchild);
		CreateBiThrTree(&(*T)->rchild);
	}
}

InThreading(BiThrTree T) {
	if (T) {
		InThreading(T->lchild);//递归左孩子
		//结点处理
		if (!(T->lchild)) {			//如果该节点没有左孩子,设置ltag为Thread,并把lchild指向刚刚访问过的结点
			T->ltag = Thread;
			T->lchild = pre;
		}
		if (!pre->rchild) {
			pre->rtag = Thread;
			pre->rchild = T;
		}
		pre = T;
		InThreading(T->rchild);//递归右孩子线索化
	}
}
InorderThreading(BiThrTree*p,BiThrTree T) {
	*p = (BiThrTree)malloc(sizeof(BiThrNode));
	(*p)->ltag = Link;
	(*p)->rtag = Thread;
	if (!T) {
		(*p)->lchild = *p;
	}
	else {
		(*p)->lchild = T;
		pre = *p;
		InThreading(T);
		pre->rchild = *p;
		pre->rtag = Thread;
		(*p)->rchild = pre;
	}
}
//中序遍历二叉树,非递归
void Visit(char c) {
	printf("%c", c);
}
//用迭代法进行中序访问
void InOrderTraverse(BiThrTree T) {
	BiThrTree p;
	p = T->lchild;
	while (p != T) {
		while (p->ltag == Link) {
			p = p->lchild;
		}
		Visit(p->data);
		while (p->rtag == Thread&&p->rchild != T) {
			p = p->rchild;
			Visit(p->data);
		}
		p = p->rchild;
	}
}
int main() {
	int level = 1;

	BiThrTree p,T = 0;
	CreateBiThrTree(&T);
	InorderThreading(&p, T);
	printf("中序输出结果为:");
	InOrderTraverse(p);
	//CreateBiTree(&T);
	//PreOrderTraverse(T, level);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kawayi90hou/article/details/80056864