当前位置:首页 » 《关注互联网》 » 正文

c语言二叉树的创建与前序、中序、后序遍历(超详细)学习笔记

8 人参与  2024年04月26日 11:20  分类 : 《关注互联网》  评论

点击全文阅读


前言:

我学习数据结构的方式是看书加看视频,视频看的是哔哩哔哩up主的数据结构-二叉树的创建与遍历 我总结并补充他所讲的内容,他的视频适合有c语言基础的看。

我的文章有点长,希望你能够耐心看完,一定一定会有所收获的!

一、创建二叉树结构体

#include<stdio.h>#include<stdlib.h>typedef struct TreeNode{char data;struct TreeNode* lChild;struct TreeNode* rChild;}TreeNode;

二、二叉树的初始化的两种思路(前序顺序根左右)递归方法

1、初始化二叉树简便方法

 TreeNode* creatTree(){ TreeNode* T; char ch; scanf_s("%c", &ch);//通过输入的ch是否是‘#’来判断该节点是否有孩子节点if ( ch == '#') //'#'代表传入的是空结点{//此时为空结点return NULL;}else{//不为空T = (TreeNode*)malloc(sizeof(TreeNode));T->data = ch;//创建左子树,逻辑一致,进行递归T->lChild = creatTree();//创建右子树,逻辑一致,进行递归T->rChild = creatTree();return T;}}

递归思路演示:

输入AB##C##

创建结构体指针t 接收函数递归结束后最终返回的指针

TreeNode* t = creatTree(); 第一次调用creatTree函数 创建结构体指针T,ch = A ,ch != #为其开辟空间 并将A传入data域,T->data = ch;下一步 T->lChild = creatTree() 给A的左孩子B 进行调用creatTree函数,创建结构体指针T   B != #,为其开辟空间,并将B传入data域,T->data ;下一步 给B的左孩子# 进行调用creatTree函数,创建结构体指针T  # == #,返回return NULL 虽然没有data域 但是树节点依旧存在只是为空;此时 返回到上一步 T->lChild = creatTree() 失败,进行执行T->rChild = creatTree()  给B的右孩子# 进行调用creatTree函数, 创建结构体指针T  # == #,返回return NULL 虽然没有data域 但是树节点依旧存在只是为空;此时 继续返回到上一步,左孩子B弄完了,该执行 A的右孩子C了,给A的右孩子C 进行调用creatTree函数,创建结构体指针T   C != #,为其开辟空间,并将C传入data域,T->data; 下一步:给C的左孩子# 进行调用creatTree函数,创建结构体指针T  # == #,返回return NULL 虽然没有data域 但是树节点依旧存在只是为空;此时 返回到上一步 T->lChild = creatTree() 失败,进行执行T->rChild = creatTree()  给C的右孩子#  进行调用creatTree函数, 创建结构体指针T  # == #,返回return NULL 虽然没有data域 但是树节点依旧存在只是为空;此时 继续返回到上一步,A的T->lChild = creatTree()和T->rChild = creatTree()  都已经执行完毕,最终返回指针T 传入指针t

2、初始化二叉树(2级指针方法)

此方法  运用了2级指针 不建议使用,只是一种思路。

目的:将你输入的二叉树的所有结点以(前序顺序)传入其中,等遍历时再将其依次输出。

初始化树,函数的三个形参:树结构体类型的2级指针 Treenode** T,数组的指针, 索引的指针。

//初始化树 void creatTree(TreeNode** T,char *data,int * index) //不想返回 又想改变 指针的地址 加上 二级指针 { char tempData;     //取数组data的元素 tempData = data[*index]; //全局变量 无法进行直接更改,只能通过指针方式进行更改 *index+=1;if ( tempData == '#')//'#'代表传入的是空结点{//此时为空结点*T = NULL;}else{//不为空*T = (TreeNode*)malloc(sizeof(TreeNode));        //先储存根结点(*T)->data = tempData;                //创建左子树,逻辑一致,进行递归creatTree(&((*T)->lChild),data,index);//创建右子树,逻辑一致,进行递归creatTree(&((*T)->rChild),data,index);}}

1.为什么要2级指针? 

因为我不想返回该1级指针,看到后面你就明白了。跟第1中方法不同的是 函数返回类型由TreeNode 变成了 void ,不需要返回该指针了,就不用 创建结构体指针t 接收函数递归结束后最终返回的指针了。这种方法是跟哔哩哔哩up主演示的,也是一种思路吧,看懂了也有好处。

实质:

2级指针:把普通变量的地址传入函数后可以在函数中修改变量的值;把指针的地址传入函数后可以在函数中修改指针的值。

想创建二叉树的新结点,必须为此结点开辟新空间进行malloc申请堆区空间,这跟单链表、双链表、链栈、链队创建新结点是一样的道理,那么若你不想返回该1级指针,只能由2级指针来 改变 此1级指针的地址。

 原理:你得明白C语言中的函数参数传递是按值传递的,如果你只传递一个指针作为参数,函数内部修改这个指针的值不会影响到函数外部的指针。修改1级指针参数是无法影响函数外部的实参指针,除非你返回该指针并赋给其对象的指针。 可以看看这位博主写的C语言函数内部改变指针本身

2.为什么要数组指针?

目的是将你键盘输入的元素传递到数组里,再将数组的元素传递给二叉树结点的data域,传入结点只能一个一个传,所以只能将数组的元素一个一个传递,通过data首地址方式,将data[索引] 依次传递给 树的data域。

3.为什么要用索引指针?

目的是为了可以改变索引的值,因为data[*index ] 每次访问完一个元素,都会将*index的地址进行+1,才能访问下一个元素。 实质:实参 index 是你在函数体外定义的一个全局变量,全局变量无法进行直接修改,只能通过指针的方式进行修改。

三、前序遍历

前序遍历(根左右): ABDECFGH       '#'为空   (一直遵循根左右)

遍历顺序在  (1、初始化二叉树简便方法)的思路演示已经展示过了。

根结点=>左孩子=>左孩子=>左孩子=>......=>最深的左孩子=>最深的右孩子=>上一层的右孩子=>他的左孩子=>他的右孩子=>上上一层的右孩子=>他的左孩子=>他的右孩子......

先打印根结点,再进行找左孩子操作,找到该左孩子办事打印输出(因为在他子树结构里,他是根),再找这个左孩子的左孩子,再继续办事打印输出... 打印输出完最后子树的左孩子,开始找最后子树的右孩子,办事打印(因为在他子树结构里,他是根),在打印最后子树(属于左孩子)同一层次的右孩子,办事打印(因为在他子树结构里,他是根)...

//前序遍历   根 左 右 void preOrder(TreeNode* T) { if (T == NULL) { return; } else {       //先办事 打印输出 printf("%c ", T->data);         //左孩子                                     preOrder(T->lChild); //右孩子                                    preOrder(T->rChild); } }

四、中序遍历

中序遍历(左根右):DBEAFCG (一直遵循左根右)

从最深的左孩子开始遍历

//中序遍历  左 根 右 void inOrder(TreeNode* T) { if (T == NULL) { return; } else  {   //打印左孩子 inOrder(T->lChild); //中办事 打印输出 printf("%c ", T->data); //打印右孩子 inOrder(T->rChild); } }

五、后序遍历

 后序遍历(左右根):DEBFGCA  (一直遵循左右根)

从最深的左孩子开始遍历

 void postOrder(TreeNode* T) { if (T == NULL) { return; } else {   //打印左孩子 postOrder(T->lChild); //打印右孩子postOrder(T->rChild);//后办事 打印输出printf("%c ", T->data); } }

六、完整代码

第一种初始化二叉树的完整代码:

注意:我使用的是vs2022 scanf的形式只能变成 scanf_s 

#include<stdio.h>#include<stdlib.h>typedef struct TreeNode{char data;struct TreeNode* lChild;struct TreeNode* rChild;}TreeNode;//初始化树 TreeNode* creatTree(){ TreeNode* T; char ch; scanf_s("%c", &ch);//通过输入的ch是否是‘#’来判断该节点是否有孩子节点if ( ch == '#')'#'代表传入的是空结点{//此时为空结点return NULL;}else{//不为空T = (TreeNode*)malloc(sizeof(TreeNode));T->data = ch;//创建左子树,逻辑一致,进行递归T->lChild = creatTree();//创建右子树,逻辑一致,进行递归T->rChild = creatTree();return T;}} //前序遍历   根 左 右 void preOrder(TreeNode* T) { if (T == NULL) { return; } else {       //先办事 printf("%c ", T->data);         //左孩子                                     //再使用遍历的方法对Lchild进行打印 打印的是最近的左孩子 左孩子成为根,继续打印它最近的左孩子 preOrder(T->lChild); //右孩子                                    //打印最远的右孩子 preOrder(T->rChild); } } //中序遍历  左 根 右 void inOrder(TreeNode* T) { if (T == NULL) { return; } else  {   //打印左孩子 inOrder(T->lChild); //中办事 printf("%c ", T->data); //打印右孩子 inOrder(T->rChild); } } void postOrder(TreeNode* T) { if (T == NULL) { return; } else {   //打印左孩子 postOrder(T->lChild); //打印右孩子postOrder(T->rChild);//后办事printf("%c ", T->data); } } int main() { TreeNode* T = creatTree(); preOrder(T); printf("\n"); inOrder(T);printf("\n"); postOrder(T); printf("\n"); return 0;  }

第二种初始化二叉树的完整代码:

注意:我使用的是vs2022 scanf的形式只能变成 scanf_s ,我的data[20]的初始大小设置为20。

#include<stdio.h>#include<stdlib.h>typedef struct TreeNode{char data;sturct TreeNode* lChild;struct TreeNode* rChild;}TreeNode;//初始化树 void creatTree(TreeNode** T,char *data,int * index) //不想返回 又想改变 指针的地址 加上 二级指针 { char tempData;     //取数组data的元素 tempData = data[*index]; //全局变量 无法进行直接更改,只能通过指针方式进行更改 *index+=1;if ( tempData == '#'){//此时为空结点*T = NULL;}else{//不为空*T = (TreeNode*)malloc(sizeof(TreeNode));        //先创建根结点(*T)->data = tempData;//创建左子树,逻辑一致,进行递归creatTree(&((*T)->lChild),data,index);//创建右子树,逻辑一致,进行递归creatTree(&((*T)->rChild),data,index);}} //前序遍历   根 左 右 void preOrder(TreeNode* T) { if (T == NULL) { return; } else {       //先办事 打印输出 printf("%c ", T->data);         //左孩子                                     //再使用遍历的方法对Lchild进行打印 打印的是最近的左孩子 左孩子成为根,继续打印它最近的左孩子 preOrder(T->lChild); //右孩子                                    //打印最远的右孩子 preOrder(T->rChild); } } //中序遍历  左 根 右 void inOrder(TreeNode* T) { if (T == NULL) { return; } else  {   //打印左孩子 inOrder(T->lChild); //中办事 打印输出 printf("%c ", T->data); //打印右孩子 inOrder(T->rChild); } } void postOrder(TreeNode* T) { if (T == NULL) { return; } else {   //打印左孩子 postOrder(T->lChild); //打印右孩子postOrder(T->rChild);//后办事 打印输出printf("%c ", T->data); } }int main(){//输入数的长度int n;scanf_s("%d\n", &n);char data[20];//("%s", data);for (int i = 0; i < n; i++){data[i] = getchar();}TreeNode* T;int index = 0;    creatTree(&T, data, &index);preOrder(T);printf("\n");inOrder(T);printf("\n");postOrder(T);return 0;

七、运行结果

第一种初始化二叉树的方法运行结果:

输入AB##C## 输入方式按前序顺序

输入ABD##E##CF##G## 输入方式按前序顺序

第二种初始化二叉树的方法的运行结果:

第一行:输入 7 

第二行:输入AB##C## 输入方式按前序顺序

第一行:输入 15 

第二行:输入ABD##E##CF##G## 输入方式按前序顺序

总结: 

初始化二叉树的顺序是前序顺序,然后有三种遍历方式(前序、中序、后序)。

搞懂第一种初始二叉树的方法就好了;第二种方法 2级指针 看懂了也厉害 这种方法是up主所演示的 感兴趣还可以再看看他的视频数据结构-二叉树的创建与遍历

制作不易,真心想让你懂,还是有不足的地方,望见谅嘞。


点击全文阅读


本文链接:http://zhangshiyu.com/post/100425.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1