赞
踩
不得不说,第一次接触数据结构,二叉树的这一章让我多少有点觉得吃力,里面的代码实现,琢磨了几天才做出来,今天做一个整体代码总结,希望能帮助到大家。
我们知道,一个满二叉树或完全二叉树,我们是完全可以使用顺序存储结构的。
因为我们满二叉树或者完全二叉树的结点个数较为完整,因为完全二叉树接近满二叉树,满二叉树是完全二叉树的特例,所以我们可以使用满二叉树的形式存储。
对于完全二叉树中有些结点无法与满二叉树完全对应,所以我们适当添加一些空结点来对应满二叉树。
但是如果是一个普通二叉树,仍然采用顺序存储结构的话,就会非常浪费空间,当只有右单分支的情况下,我们需要分配2的h次方-1的空结点,所以为了存储空间的节约,我们采用链式存储结构存储一棵普通二叉树。
二叉树的遍历分为递归和非递归遍历。
递归遍历分为先序遍历,后序遍历,中序遍历
创建一棵二叉树
这里建议使用getChar(),因为getChar()可以避免空格和回车字符,scanf必须在占位符前面加上一个空格才能避免把回车当作一个字符
- #include <stdio.h>
- #include <stdlib.h>
- #include <malloc.h>
- #include <stdbool.h>
- #define MAXSIZE 100
-
- typedef struct TreeNode{
- char data;
- struct TreeNode *lchild;
- struct TreeNode *rchild;
- }TreeNode;
-
- TreeNode* create(TreeNode *treeList){
- char value;
- printf("请输入一个字符\n");
- scanf(" %c",&value);//注意啊,回车也会被当作一个字符
- if(value=='#'){
- treeList=NULL;
- }else{
- treeList=(TreeNode *)malloc(sizeof(TreeNode));
- treeList->data=value;
- //由于每一层都有返回值,
- //所以我们必须保存每次递归后的左右节点的值,否则链表就会连接不起来
- treeList->lchild=create(treeList->lchild);
- treeList->rchild=create(treeList->rchild);
- }
- return treeList;
-
- }
先序递归遍历
先输出根结点,然后遍历每个结点的左结点,直到左结点为空,我们在循环遍历右结点,以此类推直到遍历整个二叉树
- void PreOrderOut(TreeNode *treeList){//先序输出
- if(treeList!=NULL){
- printf("先根结点:%c\n",treeList->data);//输出根结点
- PreOrderOut(treeList->lchild);//遍历左结点,直到左结点为空
- PreOrderOut(treeList->rchild);//遍历右结点,直到右结点为空
- }
- }
中序和后序递归遍历也是再这个基础之上进行变换,是不是很简单,三句话就解决问题,只要你逻辑够清楚,就很好理解了。
- void InOrderOut(TreeNode *treeList){//中序输出
- if(treeList!=NULL){
- InOrderOut(treeList->lchild);
- printf("中序根结点:%c\n",treeList->data);
- InOrderOut(treeList->rchild);
- }
- }
- void LastOrderOut(TreeNode *treeList){//后序输出
- if(treeList!=NULL){
- LastOrderOut(treeList->lchild);
- LastOrderOut(treeList->rchild);
- printf("后续根结点:%c\n",treeList->data);
- }
- }
层次遍历
所谓层次遍历,就是从根结点开始,从上到下,从左到右依次遍历二叉树每个结点
这里为了方便,我们采用循环队列来存储结点,队列的特点是先进先出,刚好满足我们的层次遍历的特点。依次从根节点开始,将第一个结点存储到队列中,队列的队尾下标加1,然后再输出,然后再保存左结点和右节点,之后再输出左节点,保存左节点的孩子结点,队列的队头下标加1,然后再输出右节点,然后保存右节点的孩子节点,以保证每次循环不丢失下一个结点的位置,直到队头队尾位置相等了,就说明此时已经遍历完毕了。
- typedef struct TreeNode{//二叉树存储结构
- char data;
- struct TreeNode *lchild;
- struct TreeNode *rchild;
- }TreeNode;
-
- typedef struct QueueNode{//队列存储结构
- TreeNode *data[MAXSIZE];
- int top;
- int base;
- }QueueNode;
-
- void push1(QueueNode *queuenode,TreeNode *treeList){//入队方法
- printf("进队列:%c\n",treeList->data);
- if(treeList!=NULL&&((queuenode->top+1)%MAXSIZE!=queuenode->base)){
- queuenode->data[queuenode->top]=treeList;
- //printf("打印一下:%c,%d\n",queuenode->data[queuenode->top]->data,queuenode->top);
- queuenode->top=(queuenode->top+1)%MAXSIZE;
-
- }
- }
-
- TreeNode* pop1(QueueNode *queuenode){//出队方法
- TreeNode *treenode;
- if(queuenode->top!=queuenode->base){//只要队头不等于队尾,就代表当前队列还不为空,还有结点
- treenode=queuenode->data[queuenode->base];
- //printf("输出值:%c,%d\n",treenode->data,queuenode->base);
- queuenode->base=(queuenode->base+1)%MAXSIZE;//这里我们采用循环队列来实现存储,这样可以更有效的利用空间
-
- }
- return treenode;
- }
-
- void levelOrderOut(TreeNode *treeList){//层次遍历
- QueueNode *queuenode;
- TreeNode *p;
- queuenode=(QueueNode*)malloc(sizeof(QueueNode));
- queuenode->top=queuenode->base=0;
- if(treeList!=NULL)
- push1(queuenode,treeList);
- while(queuenode->top!=queuenode->base){
- p=pop1(queuenode);
- if(p->lchild!=NULL)//注意细节啊,在完整执行一次后,如果自己估算的没问题,就可能时变量什么的错了
- push1(queuenode,p->lchild);
- if(p->rchild!=NULL)
- push1(queuenode,p->rchild);
- }
-
- }
非递归的实现就稍微复杂一点了
先序非递归二叉树
非递归二叉树的遍历方式和递归二叉树的区别在于,我们需要保存其左右结点,以保证每个结点能正确被访问到,而递归层层嵌套,每一层执行完毕会回到上一层,所以我们可以不必保存每个结点。
我们可以使用栈来保存遍历的结点,栈的特点是后进先出,先保存当前根结点,然后输出,再保存右结点,栈顶位置加1,保存左节点,后进先出,所以先输出左节点,栈顶位置减1,然后再保存左节点的孩子节点,再输出右结点,保存右结点孩子结点,以此类推,直到二叉树遍历完毕。
- typedef struct StackNode{//栈存储结构
- TreeNode *top[MAXSIZE];
- int length;
- }StackNode;
-
-
- typedef struct TreeNode{//二叉树存储结构
- char data;
- struct TreeNode *lchild;
- struct TreeNode *rchild;
- }TreeNode;
-
- void push(StackNode *stacknode,TreeNode *treeList){//入栈方法
- if(treeList!=NULL){
- stacknode->top[stacknode->length]=treeList;
- stacknode->length++;
- }
-
- }
-
- TreeNode* pop(StackNode *stacknode){//出栈方法
- TreeNode *node;
- if(stacknode->length!=0)
- stacknode->length--;
- node=stacknode->top[stacknode->length];
- printf("出栈一个结点:%c\n",node->data);
- return node;
- }
-
-
- void PreOrderIn(TreeNode *treeList){//先序非递归遍历
- StackNode *stacknode;
- TreeNode *node;
- stacknode=(StackNode*)malloc(sizeof(StackNode));
- stacknode->length=0;
- if(treeList!=NULL){
- push(stacknode,treeList);
- while(stacknode->length !=NULL){
- node=pop(stacknode);
- if(node->rchild!=NULL){
- push(stacknode,node->rchild);
- }
- if(node->lchild!=NULL){
- push(stacknode,node->lchild);
- }
- }
- }
- }
中序非递归二叉树
中序非递归二叉树的特点是先入栈所有的左结点,然后再依次输出左节点,保存其右节点,然后再输出右结点,再以同样的方式遍历输出,直到整个二叉树遍历完毕。
- typedef struct StackNode{//栈存储结构
- TreeNode *top[MAXSIZE];
- int length;
- }StackNode;
-
- typedef struct TreeNode{//二叉树存储结构
- char data;
- struct TreeNode *lchild;
- struct TreeNode *rchild;
- }TreeNode;
-
- void push(StackNode *stacknode,TreeNode *treeList){//入栈方法
- if(treeList!=NULL){
- stacknode->top[stacknode->length]=treeList;
- stacknode->length++;
- }
-
- }
-
- TreeNode* pop(StackNode *stacknode){//出栈方法
- TreeNode *node;
- if(stacknode->length!=0)
- stacknode->length--;
- node=stacknode->top[stacknode->length];
- printf("出栈一个结点:%c\n",node->data);
- return node;
- }
-
-
- void InOrderIn(TreeNode *treeList){//中序非递归遍历
- StackNode *stacknode;
- TreeNode *p;
- TreeNode *node;
- p=treeList;
- stacknode=(StackNode*)malloc(sizeof(StackNode));
- stacknode->length=0;
- while(p!=NULL||stacknode->length!=0){//栈不为空或者树不为空则执行
- while(p!=NULL){
- push(stacknode,p);//让所有左孩子进栈
- p=p->lchild;
- }
- if(stacknode->length!=0){
- node=pop(stacknode);
- push(stacknode,node->rchild);//每个结点的右孩子进栈
- }
- }
- }
后序非递归遍历
后序非递归与前面不同的是,这里采用了do-while循环,由于我们的栈初始化是为空的,所以不能直接使用while循环,而是先执行一次再判断,后序遍历的规则是左结点,右节点,根节点,所以我们同样遍历所有左结点并保存,然后我们利用flag来表示我们当前在操作栈顶元素,如果当前flag为false,代表当前我们没有在操作栈顶元素,而是在操作未入栈的元素,因此我们将当前未入栈的右节点入栈,再重新按照一开始的遍历方式从栈顶元素开始操作,然后我们获取当前的栈顶元素,判断此时的结点的右孩子是否为空或者是已经遍历过的结点,我们用r保存,如果是,就更新当前变量r的结点,如果不是,就移动到右节点位置,保存右节点到栈中,再重新将所有变量置空,读取当前的右节点,并且出栈,并且将当前t置空,以保证每次操作的都是一个新的结点,而不是已经操作过的旧结点,就这样依次类推,完成遍历。
- void push(StackNode *stacknode,TreeNode *treeList){
- if(treeList!=NULL){
- stacknode->top[stacknode->length]=treeList;
- stacknode->length++;
- }
-
- }
-
- TreeNode* pop(StackNode *stacknode){
- TreeNode *node;
- if(stacknode->length!=0)
- stacknode->length--;
- node=stacknode->top[stacknode->length];
- printf("出栈一个结点:%c\n",node->data);
- return node;
- }
-
- TreeNode* GetTop(StackNode *stacknode,TreeNode *p){//获取栈顶结点
- if(p!=NULL){
- return p;
- }else{
- p=stacknode->top[stacknode->length-1];
- }
- return p;
- }
-
-
- void LastOrderIn(TreeNode *treeList){//后序非递归
- StackNode *stacknode;
- stacknode=(StackNode*)malloc(sizeof(StackNode));
- stacknode->length=0;
- TreeNode *p;
- TreeNode *t;
- TreeNode *r;
- bool flag;
- p=treeList;
- do{
- while(p!=NULL){
- push(stacknode,p);
- p=p->lchild;
- }
- r=NULL;
- t=NULL;
- flag=true;
- while(flag&&stacknode->length!=0){
- t=GetTop(stacknode,t);
- if(t->rchild==r){
- r=pop(stacknode);
- t=NULL;
- }else{
- t=t->rchild;
- push(stacknode,t);
- flag=false;
- }
- }
- } while(stacknode->length!=0);
- }
后序非递归这一块解释有一点绕可能(我讲的不够好,望各位见谅),如果各位看不懂或者还有什么不明白可以直接评论,我再给你们解释,这些代码都是我实战一遍之后才放出来给大家看的,所以执行上有什么问题可以找我。
虽然看起来复杂,但是我相信天下无难事,只怕有心人。加油!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。