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