我懵逼了,硬生生调试了两个多小时,都是细节呀
以下代码:
#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;
}