》》如何用C语言构建一颗二叉树?
第一种方法:
- ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));
- A->data = { 'A' };
- A->ltag = 0;
- A->rtag = 0;
- A->lchild = NULL;
- A->rchild = NULL;
-
- ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));
- B->data = { 'B' };
- A->lchild = B;
- B->ltag = 0;
- B->rtag = 0;
- B->lchild = NULL;
- B->rchild = NULL;
-
- ThreadTree C = (ThreadTree)malloc(sizeof(ThreadNode));
- C->data = { 'C' };
- A->rchild = C;
- C->lchild = NULL;
- C->rchild = NULL;
- C->ltag = 0;
- C->rtag = 0;
-
- ThreadTree D = (ThreadTree)malloc(sizeof(ThreadNode));
- D->data = { 'D' };
- B->lchild = D;
- D->lchild = NULL;
- D->rchild = NULL;
- D->ltag = 0;
- D->rtag = 0;
-
- ThreadTree E = (ThreadTree)malloc(sizeof(ThreadNode));
- E->data = { 'E' };
- B->rchild = E;
- E->lchild = NULL;
- E->rchild = NULL;
- E->ltag = 0;
- E->rtag = 0;
-
- ThreadTree F = (ThreadTree)malloc(sizeof(ThreadNode));
- F->data = { 'F' };
- C->lchild = F;
- F->lchild = NULL;
- F->rchild = NULL;
- F->ltag = 0;
- F->rtag = 0;
-
- ThreadTree G = (ThreadTree)malloc(sizeof(ThreadNode));
- G->data = { 'G' };
- D->rchild = G;
- G->lchild = NULL;
- G->rchild = NULL;
- G->ltag = 0;
- G->rtag = 0;
第二种方法:
》》了解为什么要利用线索的先前知识
关键:n个结点的二叉树,有n+1个空链域!可用来记录前驱、后继的信息
》》线索二叉树的存储结构
》》中序线索二叉树
》》中序线索二叉树的存储
》》中序线索化
》》中序线索二叉树找中序后继
》》中序线索二叉树找中序前驱
》》完整代码:
- #include
- #include
- #include
- using namespace std;
-
- typedef int ElemType;
- //线索二叉树结点
- typedef struct ThreadNode {
- ElemType data;
- struct ThreadNode* lchild, * rchild;
- int ltag, rtag;//左、右线索标志
- }ThreadNode, * ThreadTree;
-
- //创建二叉树
- ThreadNode* Create(ThreadNode* bt) {
- static int i = 0;
- char ch;
- //string str = "AB#D##C##";
- string str = "ABD#G##E##CF###";
- ch = str[i++];
- if (ch == '#')bt = NULL;//建立一棵空树
- else {
- bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
- bt->lchild = Create(bt->lchild);//递归建立左子树
- bt->rchild = Create(bt->rchild);//递归建立右子树
- bt->ltag = 0; bt->rtag = 0;
- }
- return bt;
- }
-
- //中序遍历
- void OrdinaryInOrder(ThreadNode* T) {
- if (T != NULL) {
- OrdinaryInOrder(T->lchild);//中序遍历左子树
- printf("%c ", T->data);//访问根节点的数据域
- OrdinaryInOrder(T->rchild);//中序遍历右子树
- }
- }
-
- //中序线索化
- void InThread(ThreadTree p,ThreadTree &pre) {
- if (p != NULL) {
- InThread(p->lchild,pre);//递归,线索化左子树
-
- if (p->lchild == NULL) {//左子树为空,建立前驱线索
- p->lchild = pre;
- p->ltag = 1;
- }
- if (pre != NULL && pre->rchild == NULL) {
- pre->rchild = p;//建立前驱结点的后继线索
- pre->rtag = 1;
- }
- pre = p;
-
- InThread(p->rchild,pre);//中序遍历右子树
- }
- }
-
- //中序线索化二叉树
- void CreateInThread(ThreadTree T) {
- ThreadTree pre = NULL;//pre 初始为NULL
- if (T != NULL) {//非空二叉树才能线索化
- InThread(T,pre);//中序线索化二叉树
- pre->rchild = NULL;//处理遍历的最后一个结点
- pre->rtag = 1;
- }
- }
-
- /*
- 在中序线索二叉树中找到指定结点 *p的中序后继next
- ①若p->rtag == 1,则next = p->rchild
- ②p->rtag == 0(p必定有右孩子)
- 中序遍历--左 根 右
- 左 根 (左 根 右)
- 左 根 ((左 根 右) 根 右)
- */
- //找到以P为根的子树中,第一个被中序遍历的结点
- ThreadNode* FirstNode(ThreadNode* p) {
- //循环找到最左下结点(不一定是叶结点)
- while (p->ltag == 0)
- p = p->lchild;
- return p;
- }
- //在中序线索二叉树中找到结点p的后继结点
- ThreadNode* NextNode(ThreadNode* p) {
- //右子树中最左下结点
- if (p->rtag == 0)
- return FirstNode(p->rchild);
- else
- return p->rchild;//rtag==1直接返回后继线索
- }
-
- //中序线索二叉树的中序遍历
- void InOrder(ThreadNode* T) {
- for (ThreadNode* p = FirstNode(T); p != NULL; p = NextNode(p)) {
- printf("%c ", p->data);//访问根节点的数据域
- }
- printf("\n");
- }
-
- /*
- 在中序线索二叉树中找到指定结点*p的中序前驱pre
- ①若p->ltag == 1,则pre = p->lchild;
- ②若p->ltag == 0(必有左孩子)
- 中序遍历--左 根 右
- (左 根 右) 根 右
- ((左 根 右) 根 右) 根 右
- */
-
- //找到以P为根的子树中,最后一个被中序遍历的结点
- ThreadNode* LastNode(ThreadNode* p) {
- //循环找到最右下结点(不一定是叶结点)
- while (p->rtag == 0)
- p = p->rchild;
- return p;
- }
-
- //在中序线索二叉树中找到结点p的前驱结点
- ThreadNode* PreNode(ThreadNode* p) {
- //左子树中最右下结点
- if (p->ltag == 0)
- return LastNode(p->lchild);
- else return p->lchild;//ltag == 1直接返回前驱线索
- }
-
- //对中序线索二叉树进行逆向中序遍历
- void RevInOrder(ThreadNode* T) {
- for (ThreadNode* p = LastNode(T); p != NULL; p = PreNode(p)) {
- printf("%c ", p->data);//访问根节点的数据域
- }
- printf("\n");
- }
-
- //寻找指定值的结点
- ThreadNode* findNode(ThreadTree t, int NodeData) {
- if (t == NULL) {
- return NULL;
- }
- if (t->data == NodeData) {
- return t;
- }
- ThreadNode* p;
- for (p = FirstNode(t); p != NULL; p = NextNode(p)) {
- if (p->data == NodeData) {
- return p;
- }
- }
- return NULL;
- }
-
- //思考:处理遍历的最后一个结点时,为什么没有判断rchild是否为NULL?
- //答案:中序遍历的最后一个结点右孩子指针必为空
-
- int main() {
- ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
- p->data = 'C';//p指向目标结点
-
- //创建一棵二叉树
- ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
- T = Create(T);
- printf("---普通中序遍历--- \n");//B D A C
- OrdinaryInOrder(T);
- printf("\n");
-
-
- CreateInThread(T);
- printf("---中序线索二叉树找后继的中序遍历--- \n");//B D A C
- InOrder(T);
-
- printf("---中序线索二叉树找前驱的逆向的中序遍历--- \n");//B D A C
- RevInOrder(T);
-
-
- //printf("%c \n", findNode(T, p->data)->data);
- ThreadTree findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
- findCurrentNode = findNode(T, p->data);
-
- if (NextNode(findCurrentNode) != NULL) {
- printf("%c的后继是:%c", findCurrentNode->data, NextNode(findCurrentNode)->data);
- }
- else {
- printf("%c没有后继了", findCurrentNode->data);
- }
- printf("\n");
- if (PreNode(findCurrentNode) != NULL) {
- printf("%c的前驱是:%c", findCurrentNode->data, PreNode(findCurrentNode)->data);
- }
- else {
- printf("%c没有前驱了", findCurrentNode->data);
- }
- printf("\n");
- return 0;
- }
》》实验效果
》》先序线索二叉树
》》先序线索二叉树的存储
》》先序线索化(需要解决“转圈问题”)
》》先序线索二叉树找先序后继
》》先序线索二叉树找先序前驱
》》总结
》》先序线索二叉树找先序后继
》》完整代码
- #include
- #include
- #include
-
- #include
- using namespace std;
-
- typedef int ElemType;
- //线索二叉树结点
- typedef struct ThreadNode {
- ElemType data;
- struct ThreadNode* lchild, * rchild;
- int ltag, rtag;//左、右线索标志
- }ThreadNode, * ThreadTree;
-
- //创建二叉树
- ThreadNode* Create(ThreadNode* bt) {
- static int i = 0;
- char ch;
- //string str = "AB#D##C##";
- string str = "ABD#G##E##CF###";
- ch = str[i++];
- if (ch == '#')bt = NULL;//建立一棵空树
- else {
- bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
- bt->ltag = 0; bt->rtag = 0;
- bt->lchild = Create(bt->lchild);//递归建立左子树
- bt->rchild = Create(bt->rchild);//递归建立右子树
- }
- return bt;
- }
-
- //先序遍历
- void OrdinaryPreOrder(ThreadNode* T) {
- if (T != NULL) {
- printf("%c ", T->data);//访问根节点的数据域
- OrdinaryPreOrder(T->lchild);//先序遍历左子树
- OrdinaryPreOrder(T->rchild);//先序遍历右子树
- }
- }
-
- //先序线索化
- void PreThread(ThreadTree p, ThreadTree& pre) {
- if (p != NULL) {
- if (p->lchild == NULL) {//左子树为空,建立前驱线索
- p->lchild = pre;
- p->ltag = 1;
- }
- if (pre != NULL && pre->rchild == NULL) {
- pre->rchild = p;//建立前驱结点的后继线索
- pre->rtag = 1;
- }
- pre = p;
- //printf("%c", p->data);
- if (p->ltag == 0) {
- PreThread(p->lchild, pre);//递归,线索化左子树
- }
- if (p->rtag == 0) {
- PreThread(p->rchild, pre);
- }
- }
- }
-
- //先序线索化二叉树T
- void CreatePreThread(ThreadTree& T) {
- ThreadTree pre = NULL;//pre 初始为NULL
- if (T != NULL) {//非空二叉树才能线索化
- PreThread(T, pre);//先序线索化二叉树
- if(pre->rchild == NULL)
- pre->rtag = 1;
- }
- }
-
-
- /*
- 在先序线索二叉树中找到指定结点*p的先序后继(next)
- ①若p->rtag == 1,则 next = p->rchild
- ②若p->rtag == 0
- 》》若p有左孩子,则先序后继为左孩子
- 先序遍历---- 根 左 右
- 根 (根 左 右) 右
- 》》若p没有左孩子,则先序后继为右孩子
- 先序遍历---- 根 右
- 根 (根 左 右)
- 先序线索二叉树能否找到先序前驱呢?(提示:【先序遍历:根左右】)
- 答:不能,因为在左右子树中的结点只可能是根的后继,不可能是前驱。
- */
- //在先序线索二叉树中找到结点p的后继结点
- ThreadNode* NextNode(ThreadNode* p) {
- if (p->rtag == 1) {
- return p->rchild;
- }
- else {
- if (p->ltag == 0) //若p有左孩子,则先序后继为左孩子
- return p->lchild;
- else//若p没有左孩子,则先序后继为右孩子
- return p->rchild;
- }
- }
-
- //先序线索二叉树先序遍历
- void PreOrder(ThreadTree T) {
- for (ThreadNode* p = T; p != NULL; p = NextNode(p)) {
- printf("%c ", p->data);//访问根节点的数据域
- }
- printf("\n");
- }
-
- //寻找指定值的结点
- ThreadNode* findNode(ThreadTree T, int NodeData) {
- if (T == NULL) {
- return NULL;
- }
- if (T->data == NodeData) {
- return T;
- }
- ThreadNode* p;
- for (p = T; p != NULL; p = NextNode(p)) {
- if (p->data == NodeData) {
- return p;
- }
- }
- return NULL;
- }
-
-
- int main() {
- ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
- p->data = 'C';//p指向目标结点
-
- //创建一棵二叉树
- ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
- T = Create(T);
-
- /*
- ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));
- A->data = { 'A' };
- A->ltag = 0;
- A->rtag = 0;
- A->lchild = NULL;
- A->rchild = NULL;
- ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));
- B->data = { 'B' };
- A->lchild = B;
- B->ltag = 0;
- B->rtag = 0;
- B->lchild = NULL;
- B->rchild = NULL;
- ThreadTree C = (ThreadTree)malloc(sizeof(ThreadNode));
- C->data = { 'C' };
- A->rchild = C;
- C->lchild = NULL;
- C->rchild = NULL;
- C->ltag = 0;
- C->rtag = 0;
- ThreadTree D = (ThreadTree)malloc(sizeof(ThreadNode));
- D->data = { 'D' };
- B->lchild = D;
- D->lchild = NULL;
- D->rchild = NULL;
- D->ltag = 0;
- D->rtag = 0;
- ThreadTree E = (ThreadTree)malloc(sizeof(ThreadNode));
- E->data = { 'E' };
- B->rchild = E;
- E->lchild = NULL;
- E->rchild = NULL;
- E->ltag = 0;
- E->rtag = 0;
- ThreadTree F = (ThreadTree)malloc(sizeof(ThreadNode));
- F->data = { 'F' };
- C->lchild = F;
- F->lchild = NULL;
- F->rchild = NULL;
- F->ltag = 0;
- F->rtag = 0;
- ThreadTree G = (ThreadTree)malloc(sizeof(ThreadNode));
- G->data = { 'G' };
- D->rchild = G;
- G->lchild = NULL;
- G->rchild = NULL;
- G->ltag = 0;
- G->rtag = 0;
- */
-
- printf("---普通先序遍历--- \n");
- OrdinaryPreOrder(T);
- printf("\n");
-
- //创建先序线索二叉树
- CreatePreThread(T);
-
- printf("---先序线索二叉树找后继的先序遍历--- \n");
- PreOrder(T);
- printf("\n");
-
- //寻找结点
- ThreadTree findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
- findCurrentNode = findNode(T, p->data);
- //寻找结点的后继结点
- if (NextNode(findCurrentNode) != NULL) {
- printf("%c 的后继是:%c", findCurrentNode->data, NextNode(findCurrentNode)->data);
- }
- else {
- printf("%c 没有后继了", findCurrentNode->data);
- }
- printf("\n");
-
- return 0;
- }
》》实验结果
》》后序线索二叉树
》》后序线索二叉树的存储
》》后序线索化
王道的后序线索化,处理遍历的最后一个结点让pre->rchild = NULL,pre->rtag=1;
但好像可以省去这一步。(仅个人看法,因为去掉之后运行也是正常的,如有错误,欢迎指正!!!)
》》后序线索二叉树找后序后继
》》后序线索二叉树找后序前驱
》》总结
》》后序线索二叉树找后序前驱
》》完整代码
- #include
- #include
- #include
-
- #include
- using namespace std;
-
- typedef int ElemType;
- //线索二叉树结点
- typedef struct ThreadNode {
- ElemType data;
- struct ThreadNode* lchild, * rchild;
- int ltag, rtag;//左、右线索标志
- }ThreadNode, * ThreadTree;
-
- ThreadNode* Create(ThreadNode* bt) {
- static int i = 0;
- char ch;
- //string str = "AB#D##C##";
- string str = "ABD#G##E##CF###";
- ch = str[i++];
- if (ch == '#')bt = NULL;//建立一棵空树
- else {
- bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
- bt->ltag = 0; bt->rtag = 0;
- bt->lchild = Create(bt->lchild);//递归建立左子树
- bt->rchild = Create(bt->rchild);//递归建立右子树
- }
- return bt;
- }
-
- //后序遍历
- void OrdinaryPostOrder(ThreadNode* T) {
- if (T != NULL) {
- OrdinaryPostOrder(T->lchild);//后序遍历左子树
- OrdinaryPostOrder(T->rchild);//后序遍历右子树
- printf("%c ", T->data);//访问根节点的数据域
- }
- }
-
- //后序线索化
- void PostThread(ThreadTree p, ThreadTree& pre) {
- if (p != NULL) {
- PostThread(p->lchild, pre);//递归,线索化左子树
- PostThread(p->rchild, pre);//递归,线索化右子树
- if (p->lchild == NULL) {//左子树为空,建立前驱线索
- p->lchild = pre;
- p->ltag = 1;
- }
- if (pre != NULL && pre->rchild == NULL) {
- pre->rchild = p;//建立前驱结点的后继线索
- pre->rtag = 1;
- }
- pre = p;//标记当前结点成为刚刚访问的结点
- }
- }
-
- //后序线索化二叉树
- void CreatePostThread(ThreadTree T) {
- ThreadTree pre = NULL;
- if (T != NULL) {//非空二叉树才能线索化
- PostThread(T, pre);//线索化二叉树
- if(pre->rchild == NULL)//处理遍历的最后一个结点
- pre->rtag = 1;
- }
- }
-
- /*
- 在后序线索二叉树中找到指定结点*p的后序前驱(pre)
- ①若p->ltag == 1,则 pre = p->lchild
- ②若p->ltag == 0
- 》》若p有右孩子,则后序前驱为右孩子
- 先序遍历---- 左 右 根
- 左 (左 右 根) 根
- 》》若p没有右孩子,则后序前驱为左孩子
- 先序遍历---- 左 根
- (左 右 根) 根
- 后序线索二叉树能否找到后序后继呢?(提示:【后序遍历:左右根】)
- 答:不能,因为在左右子树中的结点只可能是根的前驱,不可能是后继。
- */
- //在后序线索二叉树中找到结点p的前驱结点
- ThreadNode* PreNode(ThreadNode* p) {
- if (p->ltag == 1)
- return p->lchild;
- else {
- if (p->rtag == 0) {
- return p->rchild;
- }
- else {
- return p->lchild;
- }
- }
- }
-
- void RevPostOrder(ThreadTree T) {
- for (ThreadNode* p = T; p != NULL; p = PreNode(p)) {
- printf("%c ", p->data);//访问根节点的数据域
- }
- printf("\n");
- }
-
- //寻找指定值的结点
- ThreadNode* findNode(ThreadTree T, int NodeData) {
- if (T == NULL) {
- return NULL;
- }
- if (T->data == NodeData) {
- return T;
- }
- ThreadNode* p;
- for (p = T; p != NULL; p = PreNode(p)) {
- if (p->data == NodeData) {
- return p;
- }
- }
- return NULL;
- }
-
- int main() {
- ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
- p->data = 'A';//p指向目标结点
-
- //创建一棵二叉树
- ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
- T = Create(T);
-
- printf("---普通后序遍历--- \n");
- OrdinaryPostOrder(T);
- printf("\n");
-
- //创建后序线索二叉树
- CreatePostThread(T);
-
- printf("---后序线索二叉树找前驱的逆向的后序遍历--- \n");
- RevPostOrder(T);
- printf("\n");
-
- //寻找结点
- ThreadNode* findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
- findCurrentNode = findNode(T, p->data);
- if (PreNode(findCurrentNode) != NULL) {
- printf("%c的前驱是:%c", findCurrentNode->data, PreNode(findCurrentNode)->data);
- }
- else {
- printf("%c没有前驱了", findCurrentNode->data);
- }
- printf("\n");
- return 0;
- }
》》实验结果
》》三种线索二叉树的对比
本文是根据王道考研的数据结构教学视频中,运用其分析原理思想,再结合自己的一些思路分析和逻辑编程,进行完整的代码的编写。如有不正确的地方,欢迎指正!