目录
修改内容
新增二叉树非递归遍历的几种方法
准备工作(结构体以及头文件):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>//栈
#include<queue>//队列
using namespace std;
#define MAX_STACK 100 //顺序存储栈的最大空间数
typedef char Type;//自定义类型
//二叉树节点结构体
typedef struct _BiTNode
{
Type data;//节点值
struct _BiTNode *lchild;//指向左孩子节点
struct _BiTNode *rchild;//指向右孩子节点
int flag; //用于非递归遍历 flag == 0表示没被抛出flag == 1表示被抛出过 //这个flag会被重写!!!所以只能连续非递归第二种遍历方式一次,或者重新初始化下
} BiTNode,*BiTree;
**不理解为什么标题是C语言实现的请看我的这篇文章 **
https://blog.csdn.net/CRAJA/article/details/115357997
初始化空二叉树:
之后创建需要用到,索性直接弄了个二级指针T(因为传参数进函数是在函数内新建一个变量存你传进去变量所存的内容)
/*
作用:二叉树的空树创建
参数:无
返回值:返回指向根节点指针的二级指针--空树默认为空节点,所以二级指针指向的一级指针应该指向NULL
*/
BiTree* Creat()
{
BiTree* T = (BiTree*)malloc(sizeof(BiTree));
*T = NULL;
return T;
}
二叉树的创建
用了三种递归的创建方式(应该可以用非递归的,之后实现下)
/*
作用:二叉树的先序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_ProCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*) 改变一级指针的指向(改变其内部存储的地址)需要使用二级指针
(*T)->data = data;
(*T)->flag = 0;
Tree_ProCreat(&(*T)->lchild);
Tree_ProCreat(&(*T)->rchild);
}
}
/*
作用:二叉树的中序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_MidCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
Tree_MidCreat(&(*T)->lchild);
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*)
(*T)->data = data;
Tree_MidCreat(&(*T)->rchild);
}
}
/*
作用:二叉树的后序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_BackCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
Tree_BackCreat(&(*T)->lchild);
Tree_BackCreat(&(*T)->rchild);
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*)
(*T)->data = data;
(*T)->flag = 0;
}
}
这里每一种方法都会初始化标记点flag
先序遍历
用了三种方法
/*
作用:二叉树的先序递归遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_ProPrint(BiTree root)
{
if(root == NULL)
return;
printf("%c ",root->data);
Tree_ProPrint(root->lchild);
Tree_ProPrint(root->rchild);
}
/*
作用:二叉树的先序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_ProPrint_No(BiTree root)
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)//只要根节点不空且栈没pop完就一直循环,因为要通过栈中元素来转到右孩子,root和stack为空说明遍历完了
{
while(root)//这个循环作用就是疯狂遍历左孩子 //第一次遍历
{
printf("%c ",root->data);//先序就是第一次把它压进栈就printf它
stack[++top] = root;
root = root->lchild;
}
if(top)//这个是第二次遇见节点,往右孩子尝试一下,看看有没有右孩子,然后pop掉工具人节点//第二次访问
{
root = stack[top--]->rchild;//这个元素左节点已经被访问了,只要继续访问它的右节点就可以了,它本身已经没有利用价值了,可以弹出了(老工具人了..)
}
}
}
//二叉树非递归先序遍历
void Tree_ProPrint_No2(BiTree root)//先序遍历的原理就是 压入根节点打印并弹出 , 再压入左节点 , 再压入右节点 根左右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
printf("%c ",root->data);//展示 核心就是这三步的位置!
Stack.pop();//抛出
root->flag = 1;//标记被抛出
if(root->rchild && root->rchild->flag==0)//先压入右孩子 这里一定要注意!先 push 右节点 再 push 左节点以至于之后每次获取到的节点都优先左节点...一直往左节点递归...
Stack.push(root->rchild);
if(root->lchild && root->lchild->flag==0)//再压入左孩子
Stack.push(root->lchild);
}
}
我将需要说明的都放到了注释里,可以的话请复制到电脑上仔细阅读
中序遍历
使用三种方法
/*
作用:二叉树的中序递归遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_MidPrint(BiTree root)
{
if(root == NULL)
return;
Tree_MidPrint(root->lchild);
printf("%c ",root->data);
Tree_MidPrint(root->rchild);
}
/*
作用:二叉树的中序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_MidPrint_No(BiTree root)//这个和前序很像
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)
{
while(root)
{
stack[++top] = root;
root = root->lchild;
}
if(top)
{
printf("%c ",stack[top]->data);//中序就是在访问它第二次时(即要访问它的右孩子时)就可以printf它了
root = stack[top--]->rchild;
}
}
}
//二叉树非递归中序遍历
void Tree_MidPrint_No2(BiTree root)//根据原理 压入左节点, 再显示并弹出根节点, 再压入右节点 左根右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先遍历左孩子
{
Stack.push(root->lchild);
}
else
{
printf("%c ",root->data);//标记并弹出
Stack.pop();//抛出
root->flag = 1;//标记
if(root->rchild && root->rchild->flag==0)//再遍历右孩子
Stack.push(root->rchild);
}
}
}
后序遍历
用了三种方法
/*
作用:二叉树的后序遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_BackPrint(BiTree root)
{
if(root == NULL)
return;
Tree_BackPrint(root->lchild);
Tree_BackPrint(root->rchild);
printf("%c ",root->data);
}
/*
作用:二叉树的后序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_BackPrint_No(BiTree root)//这个和上面两种非递归的方法很不同,因为它是在第三次访问时才printf所以不能在第二次访问时就pop了,需要在第三次访问时pop并且printf,但如果pop了就需要标记下它已经被pop了不然会被它的双亲又给push进去了...
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)
{
while(root && root->flag == 0)//如果这个节点是第一次访问就push进栈,遍历它的左孩子//第一次遍历
{
stack[++top] = root;
root = root->lchild;
}
if(top)
{
if(stack[top]->rchild && stack[top]->rchild->flag == 0)//如果这个节点有左孩子而且左孩子是第一次遍历就去遍历左孩子//第二次遍历
root = stack[top]->rchild;
else
{
printf("%c ",stack[top]->data);//第三次遍历printf然后pop
stack[top--]->flag = 1;//表示已经被弹出过,就不会被双亲再push进来了
}
}
}
}
//二叉树非递归后序遍历
void Tree_BackPrint_No2(BiTree root)//根据原理 先压入左节点 再压入右节点 再打印并弹出根节点 左右根
{
if(!root)
return;
stack<BiTree>Stack;
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先压入左孩子
Stack.push(root->lchild);
else if(root->rchild && root->rchild->flag==0)//再压入右孩子
Stack.push(root->rchild);
else
{
printf("%c ",root->data); //打印并弹出
root->flag = 1;//标记被抛出
Stack.pop();//抛出
}
}
}
话真的都在注释里(呜呜呜 )
层次遍历
//层序遍历
void Tree_LevelPrint(BiTree root)
{
if(root==NULL)
return;
// BiTNode* stack[100];// 存放 BiTNode* 的数组 区别于 BiTNode (*stack)[100] : stack是个指针,指向着存着 BiTNode 的数组
queue<BiTree> Queue;
Queue.push(root);//将现在的根节点放入队列尾部
while(!Queue.empty())
{
root = Queue.front();//获取队列头节点
if(root->lchild)
Queue.push(root->lchild);//压入右孩子
if(root->rchild)
Queue.push(root->rchild);//压入左孩子
printf("%c ",root->data);//打印双亲节点并让双亲节点出栈
Queue.pop();
}
}
销毁整棵树
/*
作用:销毁整棵树
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_Destory(BiTree root)//新建了一个一级指针指向根节点
{
if(root == NULL)
return;
Tree_Destory(root->lchild);
Tree_Destory(root->rchild);
free(root);
root = NULL;
}
二叉树的判空
/*
作用:二叉树的判空
参数:(BiTree)root:指向根节点的 指针
返回值:若二叉树为空则返回 1,否则返回 0
*/
int Tree_Empty(BiTree root)
{
if(root == NULL)
return 1;
return 0;
}
/*
计算二叉树的深度
/*
作用:计算二叉树的深度
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的深度,空树返回0
*/
int Tree_Depth(BiTree root)//返回左右子树中较高的那颗的高度
{
if(root==NULL)
return 0;
int m = Tree_Depth(root->lchild);
int n = Tree_Depth(root->rchild);
return (m>n?m:n)+1;//加1是加根节点自身高度
}
统计二叉树的节点数
/*
作用:统计二叉树的节点数
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的节点总数
*/
int Tree_NodeCount(BiTree root)
{
if(root == NULL)
return 0;//空节点返回0
return Tree_NodeCount(root->lchild) + Tree_NodeCount(root->rchild) + 1;//加上自身节点
}
统计二叉树叶子节点数
/*
作用:统计二叉树的叶子节点数
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的叶子节点总数
*/
int Tree_LeafCount(BiTree root)
{
if(root==NULL)
return 0;
if(root->lchild==NULL&&root->rchild==NULL)//如果是叶子节点就返回自身 1
return 1;
return Tree_LeafCount(root->lchild)+Tree_LeafCount(root->rchild);//否则就继续搜索
}
/*
遍历二叉树的叶子节点
用了三种方法
/*
作用:先序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafProPrint(BiTree root)
{
if(root == NULL)
return;
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
else
{
Tree_LeafProPrint(root->lchild);
Tree_LeafProPrint(root->rchild);
}
}
/*
作用:中序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafMidPrint(BiTree root)
{
if(root == NULL)
return;
Tree_LeafMidPrint(root->lchild);
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
else
Tree_LeafMidPrint(root->rchild);
}
/*
作用:后序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafBackPrint(BiTree root)
{
if(root == NULL)
return;
Tree_LeafBackPrint(root->lchild);
Tree_LeafBackPrint(root->rchild);
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
}
统计二叉树度为1的节点数
/*
作用:计算二叉树度为一的节点的数目
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
int Tree_Leaf_One_Count(BiTree root)
{
if(root==NULL)//空返回0
return 0;
if(root->lchild==NULL&&root->rchild||root->rchild==NULL&&root->lchild)
return Tree_Leaf_One_Count(root->lchild) + Tree_Leaf_One_Count(root->rchild) + 1;//存在一个叶子节点就加个1,再继续找
else
return Tree_Leaf_One_Count(root->lchild) + Tree_Leaf_One_Count(root->rchild);
}
二叉树镜像化
/*
作用:二叉树镜像化
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_Mirror(BiTree root)
{
if(root == NULL)
return;
BiTree temp;//交换左右孩子节点
temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
Tree_Mirror(root->lchild);
Tree_Mirror(root->rchild);
}
二叉树的复制
/*
作用:二叉树单节点复制
参数:(BiTree)node:指向 二叉树节点的 指针
返回值:(BiTree)newnode 返回复制出的节点指针
*/
BiTree Tree_NodeCopy(BiTree node)
{
BiTree newnode = (BiTree)malloc(sizeof(BiTNode));
newnode->data = node->data;
newnode->lchild = NULL;
newnode->rchild = NULL;
return newnode;
}
/*
作用:二叉树复制
参数:(BiTree)node:指向根节点的 指针
返回值:(BiTree)newroot 指向根节点的指针
*/
BiTree Tree_Copy(BiTree root)
{
if(root == NULL)
return NULL;
BiTree newroot= Tree_NodeCopy(root);
newroot->lchild = Tree_Copy(root->lchild);
newroot->rchild = Tree_Copy(root->rchild);
return newroot;
}
统计二叉树第k层的节点数
/*
作用:计算二叉树第k层的节点数
参数: (BiTree)node:指向根节点的 指针 (int)k:二叉树的第k层
返回值:返回第k层的节点数
*/
int Tree_LevelCount(BiTree root,int k)
{
if(root == NULL)
return 0;
if(k == 1)
return 1;
return Tree_LevelCount(root->lchild,k-1) + Tree_LevelCount(root->rchild,k-1);//没找到就往下找并且层数-1
}
在二叉树中寻找给定节点的双亲
/*
作用:在以 root为根的二叉树中寻找 节点(BiTree)cur的双亲
参数: (BiTree )root:指向根节点的 指针 (BiTree)cur:指向 需要寻找双亲的节点的 指针
返回值:若找到双亲则返回指向其双亲的指针,否则返回NULL
*/
BiTree Tree_Parent(BiTree root,BiTree cur)
{
if(root == NULL)//如果该节点为空返回空
return NULL;
if(root->lchild==cur||root->rchild==cur)//如果此节点为双亲则返回次节点
return root;
BiTree p = Tree_Parent(root->lchild,cur);//此节点不是双亲就在左子树中寻找
if(p)
return p;//如果找到了双亲(非NULL)就返回双亲
else
return Tree_Parent(root->rchild,cur);//在左子树中没找到双亲就去右子树寻找
}
判断两颗二叉树是否相似(结构相似)
/*
作用:判断两颗二叉树 T1 和 T2是否相似
参数:(BiTree)T1:指向一颗二叉树头节点的指针,(BiTree)T2:指向另一颗二叉树头节点的指针
返回值:若两棵子树相似则返回1,不相似则返回0
*/
int Tree_IsSimilar(BiTree root1,BiTree root2)
{
if(root1 == NULL&&root2 == NULL)
return 1;
if(root1 == NULL||root2 == NULL)
return 0;
int similar1 = Tree_IsSimilar(root1->lchild,root2->lchild);//判断左子树
int similar2 = Tree_IsSimilar(root1->rchild,root2->rchild);//判断右子树
return (similar1 && similar2);//左右子树形状都需要相似才认为二叉树相似
}
垂直打印树(这是某位大佬的思路…)
https://blog.csdn.net/u010909667/article/details/54972495
这位大佬提出了两种二叉树的打印方法(C++的)
/*
作用:垂直打印树
参数:(BiTree)Parent:指向一颗二叉树头节点的二级指针,(BiTree)root:指向同一颗二叉树头节点的二级指针
返回值:无
*/
void Tree_Show(BiTree parent,BiTree root,char *prefix)
{
strcat(prefix,"|");//遇到一个 根 就往字符串后面加一个'|' 意味这一层打印的是上一层的孩子
if(root)
{
printf("%s--%c\n",prefix,root->data);//如果此时的*root不是空的就打印次节点的 值
if(parent == root||parent->rchild == root)//意思是如果是最初指在一块时或者是打印完两个孩子了就取消掉这一层创建的 '关系柱子' --> '|'
{
int len = strlen(prefix);
prefix[len-1] = ' ';
}
char p1[100],p2[100];
strcpy(p1,prefix);
strcat(p1," ");
strcpy(p2,prefix);
strcat(p2," ");
Tree_Show(root,root->lchild,p1);
Tree_Show(root,root->rchild,p2);
}
else
{
if(parent->lchild||parent->rchild)//如果此时的parent只要有一个节点就要打印'#',体现出左右孩子的结构
printf("%s--#\n",prefix);
}
}
展示二叉树所有叶子节点到根节点的路径
/*
作用:展示二叉树所有叶子节点到根节点的路径
参数:((BiTree)T:指向二叉树头节点的指针,(char *)stack:存放依次经过的节点的值,(int)top:最后一个存放在stack中的节点的位置
返回值:无
*/
void Tree_PrintLeafPath(BiTree root,char *stack,int top)
{
if(root == NULL)
return;
stack[top] = root->data;//将此节点放到stack中
if(root->lchild==NULL&&root->rchild==NULL)//如果此节点使叶子节点就全部输出
{
for(int i = top; i>=0; i--)
printf("%c ",stack[i]);
putchar('\n');
return;
}
Tree_PrintLeafPath(root->lchild,stack,top+1);
Tree_PrintLeafPath(root->rchild,stack,top+1);
}
判断二叉树是否镜像对称(完全对称)
来自力扣 101. 对称二叉树
https://leetcode-cn.com/problems/symmetric-tree/
int dfs_Symmetric(BiTree left,BiTree right)
{
if(left == NULL && right == NULL)
return 1;
if(left==NULL&&right||left&&right==NULL)
return 0;
if(left->data!=right->data)
return 0;
return dfs_Symmetric(left->rchild,right->lchild) && dfs_Symmetric(left->lchild,right->rchild);
}
/*
作用:判断二叉树是否镜像对称(值也对称)
参数:(BiTree)root:指向二叉树根节点的指针
返回值:若该二叉树镜像对称则返回1,否则返回0
*/
int Tree_IsSymmetric(BiTree root)
{
if(root==NULL)
return 0;
return dfs_Symmetric(root->lchild,root->rchild);
}
根节点到叶子节点 的路径求和
来自力扣的 112. 路径总和
https://leetcode-cn.com/problems/path-sum/
int dfs_SumPath(BiTree root,int sum,int num)
{
if(root==NULL)
return 0;
if(root->lchild==NULL&&root->rchild==NULL)
{
if(sum==num)
return 1;
return 0;
}
return dfs_SumPath(root->lchild,sum+root->data,num)||dfs_SumPath(root->rchild,sum+root->data,num);
}
/*
作用:判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 num
参数:(BiTree)root:指向二叉树根节点的指针,(int)num:目标和
返回值:若存在节点和等于num则返回1,否则返回0
*/
int Is_SumPath(BiTree root,int num)
{
if(root==NULL)
return 0;
return dfs_SumPath(root,0,num);
}
效果图
总结
二叉树真的很神奇,这篇总结也真的蛮用心的写了,因为我也是初学者,可能会有很多地方没弄好,有什么建议请您讲出,我一定洗耳恭听!
完整代码
/*
名称:二叉树链式结构的实现
作者:Raja
时间:2021-3-31
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stack>//栈
#include<queue>//队列
using namespace std;
#define MAX_STACK 100 //顺序存储栈的最大空间数
typedef char Type;//自定义类型
//二叉树节点结构体
typedef struct _BiTNode
{
Type data;//节点值
struct _BiTNode *lchild;//指向左孩子节点
struct _BiTNode *rchild;//指向右孩子节点
int flag; //用于非递归遍历 flag == 0表示没被抛出flag == 1表示被抛出过 //这个flag会被重写!!!所以只能连续非递归第二种遍历方式一次,或者重新初始化下
} BiTNode,*BiTree;
/*
作用:二叉树的空树创建
参数:无
返回值:返回指向根节点指针的二级指针--空树默认为空节点,所以二级指针指向的一级指针应该指向NULL
*/
BiTree* Creat()
{
BiTree* T = (BiTree*)malloc(sizeof(BiTree));
*T = NULL;
return T;
}
/*
作用:二叉树的先序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_ProCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*) 改变一级指针的指向(改变其内部存储的地址)需要使用二级指针
(*T)->data = data;
(*T)->flag = 0;
Tree_ProCreat(&(*T)->lchild);
Tree_ProCreat(&(*T)->rchild);
}
}
/*
作用:二叉树的中序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_MidCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
Tree_MidCreat(&(*T)->lchild);
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*)
(*T)->data = data;
Tree_MidCreat(&(*T)->rchild);
}
}
/*
作用:二叉树的后序创建: #代表该节点为空
参数:(BiTree *)T:指向根节点的 二级指针
返回值:无
*/
void Tree_BackCreat(BiTree *T)
{
Type data = getchar();
if(data == '#')
*T = NULL;
else
{
Tree_BackCreat(&(*T)->lchild);
Tree_BackCreat(&(*T)->rchild);
(*T) = (BiTree)malloc(sizeof(BiTNode));//*T 相当于 lchild 或者 rchild (BiTNode*)
(*T)->data = data;
(*T)->flag = 0;
}
}
/*
作用:二叉树的先序遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_ProPrint(BiTree root)
{
if(root == NULL)
return;
printf("%c ",root->data);
Tree_ProPrint(root->lchild);
Tree_ProPrint(root->rchild);
}
/*
作用:二叉树的先序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_ProPrint_No(BiTree root)
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)//只要根节点不空且栈没pop完就一直循环,因为要通过栈中元素来转到右孩子,root和stack为空说明遍历完了
{
while(root)//这个循环作用就是疯狂遍历左孩子 //第一次遍历
{
printf("%c ",root->data);//先序就是第一次把它压进栈就printf它
stack[++top] = root;
root = root->lchild;
}
if(top)//这个是第二次遇见节点,往右孩子尝试一下,看看有没有右孩子,然后pop掉工具人节点//第二次访问
{
root = stack[top--]->rchild;//这个元素左节点已经被访问了,只要继续访问它的右节点就可以了,它本身已经没有利用价值了,可以弹出了(老工具人了..)
}
}
}
//二叉树非递归先序遍历
void Tree_ProPrint_No2(BiTree root)//先序遍历的原理就是 压入根节点打印并弹出 , 再压入左节点 , 再压入右节点 根左右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
printf("%c ",root->data);//展示 核心就是这三步的位置!
Stack.pop();//抛出
root->flag = 1;//标记被抛出
if(root->rchild && root->rchild->flag==0)//先压入右孩子 这里一定要注意!先 push 右节点 再 push 左节点以至于之后每次获取到的节点都优先左节点...一直往左节点递归...
Stack.push(root->rchild);
if(root->lchild && root->lchild->flag==0)//再压入左孩子
Stack.push(root->lchild);
}
}
/*
作用:二叉树的中序遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_MidPrint(BiTree root)
{
if(root == NULL)
return;
Tree_MidPrint(root->lchild);
printf("%c ",root->data);
Tree_MidPrint(root->rchild);
}
/*
作用:二叉树的中序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_MidPrint_No(BiTree root)//这个和前序很像
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)
{
while(root)
{
stack[++top] = root;
root = root->lchild;
}
if(top)
{
printf("%c ",stack[top]->data);//中序就是在访问它第二次时(即要访问它的右孩子时)就可以printf它了
root = stack[top--]->rchild;
}
}
}
//二叉树非递归中序遍历
void Tree_MidPrint_No2(BiTree root)//根据原理 压入左节点, 再显示并弹出根节点, 再压入右节点 左根右
{
if(!root)
return;
stack <BiTree> Stack;//栈
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先遍历左孩子
{
Stack.push(root->lchild);
}
else
{
printf("%c ",root->data);//标记并弹出
Stack.pop();//抛出
root->flag = 1;//标记
if(root->rchild && root->rchild->flag==0)//再遍历右孩子
Stack.push(root->rchild);
}
}
}
/*
作用:二叉树的后序遍历
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_BackPrint(BiTree root)
{
if(root == NULL)
return;
Tree_BackPrint(root->lchild);
Tree_BackPrint(root->rchild);
printf("%c ",root->data);
}
/*
作用:二叉树的后序遍历--非递归
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_BackPrint_No(BiTree root)//这个和上面两种非递归的方法很不同,因为它是在第三次访问时才printf所以不能在第二次访问时就pop了,需要在第三次访问时pop并且printf,但如果pop了就需要标记下它已经被pop了不然会被它的双亲又给push进去了...
{
if(!root)
return;
BiTree stack[MAX_STACK];
int top = 0;
while(root||top)
{
while(root && root->flag == 0)//如果这个节点是第一次访问就push进栈,遍历它的左孩子//第一次遍历
{
stack[++top] = root;
root = root->lchild;
}
if(top)
{
if(stack[top]->rchild && stack[top]->rchild->flag == 0)//如果这个节点有左孩子而且左孩子是第一次遍历就去遍历左孩子//第二次遍历
{
root = stack[top]->rchild;
}
else
{
printf("%c ",stack[top]->data);//第三次遍历printf然后pop
stack[top--]->flag = 1;//表示已经被弹出过,就不会被双亲再push进来了
}
}
}
}
//二叉树非递归后序遍历
void Tree_BackPrint_No2(BiTree root)//根据原理 先压入左节点 再压入右节点 再打印并弹出根节点 左右根
{
if(!root)
return;
stack<BiTree>Stack;
Stack.push(root);//把根入栈
while(!Stack.empty())//栈不空就行
{
root = Stack.top();//获取栈顶节点
if(root->lchild && root->lchild->flag==0)//先压入左孩子
Stack.push(root->lchild);
else if(root->rchild && root->rchild->flag==0)//再压入右孩子
Stack.push(root->rchild);
else
{
printf("%c ",root->data); //打印并弹出
root->flag = 1;//标记被抛出
Stack.pop();//抛出
}
}
}
//层序遍历
void Tree_LevelPrint(BiTree root)
{
if(root==NULL)
return;
// BiTNode* stack[100];// 存放 BiTNode* 的数组 区别于 BiTNode (*stack)[100] : stack是个指针,指向着存着 BiTNode 的数组
queue<BiTree> Queue;
Queue.push(root);//将现在的根节点放入队列尾部
while(!Queue.empty())
{
root = Queue.front();//获取队列头节点
if(root->lchild)
Queue.push(root->lchild);//压入右孩子
if(root->rchild)
Queue.push(root->rchild);//压入左孩子
printf("%c ",root->data);//打印双亲节点并让双亲节点出栈
Queue.pop();
}
}
/*
作用:销毁整棵树
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_Destory(BiTree root)//新建了一个一级指针指向根节点
{
if(root == NULL)
return;
Tree_Destory(root->lchild);
Tree_Destory(root->rchild);
free(root);
root = NULL;
}
/*
作用:二叉树的判空
参数:(BiTree)root:指向根节点的 指针
返回值:若二叉树为空则返回 1,否则返回 0
*/
int Tree_Empty(BiTree root)
{
if(root == NULL)
return 1;
return 0;
}
/*
作用:计算二叉树的深度
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的深度,空树返回0
*/
int Tree_Depth(BiTree root)//返回左右子树中较高的那颗的高度
{
if(root==NULL)
return 0;
int m = Tree_Depth(root->lchild);
int n = Tree_Depth(root->rchild);
return (m>n?m:n)+1;//加1是加根节点自身高度
}
/*
前提:二叉树不为空
作用:找到二叉树的根节点
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树根节点的值
*/
Type Tree_Root(BiTree root)
{
return root->data;
}
/*
作用:统计二叉树的节点数
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的节点总数
*/
int Tree_NodeCount(BiTree root)
{
if(root == NULL)
return 0;//空节点返回0
return Tree_NodeCount(root->lchild) + Tree_NodeCount(root->rchild) + 1;//加上自身节点
}
/*
作用:统计二叉树的叶子节点数
参数:(BiTree)root:指向根节点的 指针
返回值:返回二叉树的叶子节点总数
*/
int Tree_LeafCount(BiTree root)
{
if(root==NULL)
return 0;
if(root->lchild==NULL&&root->rchild==NULL)//如果是叶子节点就返回自身 1
return 1;
return Tree_LeafCount(root->lchild)+Tree_LeafCount(root->rchild);//否则就继续搜索
}
/*
作用:先序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafProPrint(BiTree root)
{
if(root == NULL)
return;
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
else
{
Tree_LeafProPrint(root->lchild);
Tree_LeafProPrint(root->rchild);
}
}
/*
作用:中序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafMidPrint(BiTree root)
{
if(root == NULL)
return;
Tree_LeafMidPrint(root->lchild);
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
else
Tree_LeafMidPrint(root->rchild);
}
/*
作用:后序遍历二叉树的叶子节点
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_LeafBackPrint(BiTree root)
{
if(root == NULL)
return;
Tree_LeafBackPrint(root->lchild);
Tree_LeafBackPrint(root->rchild);
if(root->lchild==NULL&&root->rchild==NULL)
printf("%c ",root->data);
}
/*
作用:计算二叉树度为一的节点的数目
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
int Tree_Leaf_One_Count(BiTree root)
{
if(root==NULL)//空返回0
return 0;
if(root->lchild==NULL&&root->rchild||root->rchild==NULL&&root->lchild)
return Tree_Leaf_One_Count(root->lchild) + Tree_Leaf_One_Count(root->rchild) + 1;//存在一个叶子节点就加个1,再继续找
else
return Tree_Leaf_One_Count(root->lchild) + Tree_Leaf_One_Count(root->rchild);
}
/*
作用:二叉树镜像化
参数:(BiTree)root:指向根节点的 指针
返回值:无
*/
void Tree_Mirror(BiTree root)
{
if(root == NULL)
return;
BiTree temp;//交换左右孩子节点
temp = root->lchild;
root->lchild = root->rchild;
root->rchild = temp;
Tree_Mirror(root->lchild);
Tree_Mirror(root->rchild);
}
/*
作用:二叉树单节点复制
参数:(BiTree)node:指向 二叉树节点的 指针
返回值:(BiTree)newnode 返回复制出的节点指针
*/
BiTree Tree_NodeCopy(BiTree node)
{
BiTree newnode = (BiTree)malloc(sizeof(BiTNode));
newnode->data = node->data;
newnode->lchild = NULL;
newnode->rchild = NULL;
return newnode;
}
/*
作用:二叉树复制
参数:(BiTree)node:指向根节点的 指针
返回值:(BiTree)newroot 指向根节点的指针
*/
BiTree Tree_Copy(BiTree root)
{
if(root == NULL)
return NULL;
BiTree newroot= Tree_NodeCopy(root);
newroot->lchild = Tree_Copy(root->lchild);
newroot->rchild = Tree_Copy(root->rchild);
return newroot;
}
/*
作用:计算二叉树第k层的节点数
参数: (BiTree)node:指向根节点的 指针 (int)k:二叉树的第k层
返回值:返回第k层的节点数
*/
int Tree_LevelCount(BiTree root,int k)
{
if(root == NULL)
return 0;
if(k == 1)
return 1;
return Tree_LevelCount(root->lchild,k-1) + Tree_LevelCount(root->rchild,k-1);//没找到就往下找并且层数-1
}
/*
作用:在以 root为根的二叉树中寻找 节点(BiTree)cur的双亲
参数: (BiTree )root:指向根节点的 指针 (BiTree)cur:指向 需要寻找双亲的节点的 指针
返回值:若找到双亲则返回指向其双亲的指针,否则返回NULL
*/
BiTree Tree_Parent(BiTree root,BiTree cur)
{
if(root == NULL)//如果该节点为空返回空
return NULL;
if(root->lchild==cur||root->rchild==cur)//如果此节点为双亲则返回次节点
return root;
BiTree p = Tree_Parent(root->lchild,cur);//此节点不是双亲就在左子树中寻找
if(p)
return p;//如果找到了双亲(非NULL)就返回双亲
else
return Tree_Parent(root->rchild,cur);//在左子树中没找到双亲就去右子树寻找
}
/*
作用:判断两颗二叉树 T1 和 T2是否相似
参数:(BiTree)T1:指向一颗二叉树头节点的指针,(BiTree)T2:指向另一颗二叉树头节点的指针
返回值:若两棵子树相似则返回1,不相似则返回0
*/
int Tree_IsSimilar(BiTree root1,BiTree root2)
{
if(root1 == NULL&&root2 == NULL)
return 1;
if(root1 == NULL||root2 == NULL)
return 0;
int similar1 = Tree_IsSimilar(root1->lchild,root2->lchild);//判断左子树
int similar2 = Tree_IsSimilar(root1->rchild,root2->rchild);//判断右子树
return (similar1 && similar2);//左右子树形状都需要相似才认为二叉树相似
}
/*
作用:垂直打印树
参数:(BiTree)Parent:指向一颗二叉树头节点的二级指针,(BiTree)root:指向同一颗二叉树头节点的二级指针
返回值:无
*/
void Tree_Show(BiTree parent,BiTree root,char *prefix)
{
strcat(prefix,"|");//遇到一个 根 就往字符串后面加一个'|' 意味这一层打印的是上一层的孩子
if(root)
{
printf("%s--%c\n",prefix,root->data);//如果此时的*root不是空的就打印次节点的 值
if(parent == root||parent->rchild == root)//意思是如果是最初指在一块时或者是打印完两个孩子了就取消掉这一层创建的 '关系柱子' --> '|'
{
int len = strlen(prefix);
prefix[len-1] = ' ';
}
char p1[100],p2[100];
strcpy(p1,prefix);
strcat(p1," ");
strcpy(p2,prefix);
strcat(p2," ");
Tree_Show(root,root->lchild,p1);
Tree_Show(root,root->rchild,p2);
}
else
{
if(parent->lchild||parent->rchild)//如果此时的parent只要有一个节点就要打印'#',体现出左右孩子的结构
printf("%s--#\n",prefix);
}
}
/*
作用:展示二叉树所有叶子节点到根节点的路径
参数:((BiTree)T:指向二叉树头节点的指针,(char *)stack:存放依次经过的节点的值,(int)top:最后一个存放在stack中的节点的位置
返回值:无
*/
void Tree_PrintLeafPath(BiTree root,char *stack,int top)
{
if(root == NULL)
return;
stack[top] = root->data;//将此节点放到stack中
if(root->lchild==NULL&&root->rchild==NULL)//如果此节点使叶子节点就全部输出
{
for(int i = top; i>=0; i--)
printf("%c ",stack[i]);
putchar('\n');
return;
}
Tree_PrintLeafPath(root->lchild,stack,top+1);
Tree_PrintLeafPath(root->rchild,stack,top+1);
}
int dfs_Symmetric(BiTree left,BiTree right)
{
if(left == NULL && right == NULL)
return 1;
if(left==NULL&&right||left&&right==NULL)
return 0;
if(left->data!=right->data)
return 0;
return dfs_Symmetric(left->rchild,right->lchild) && dfs_Symmetric(left->lchild,right->rchild);
}
/*
作用:判断二叉树是否镜像对称(值也对称)
参数:(BiTree)root:指向二叉树根节点的指针
返回值:若该二叉树镜像对称则返回1,否则返回0
*/
int Tree_IsSymmetric(BiTree root)
{
if(root==NULL)
return 0;
return dfs_Symmetric(root->lchild,root->rchild);
}
int dfs_SumPath(BiTree root,int sum,int num)
{
if(root==NULL)
return 0;
if(root->lchild==NULL&&root->rchild==NULL)
{
if(sum==num)
return 1;
return 0;
}
return dfs_SumPath(root->lchild,sum+root->data,num)||dfs_SumPath(root->rchild,sum+root->data,num);
}
/*
作用:判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 num
参数:(BiTree)root:指向二叉树根节点的指针,(int)num:目标和
返回值:若存在节点和等于num则返回1,否则返回0
*/
int Is_SumPath(BiTree root,int num)
{
if(root==NULL)
return 0;
return dfs_SumPath(root,0,num);
}
int main()
{
char* s1 = (char*)malloc(sizeof(char)*100);
char stack[100];
// BiTree p = NULL;
// BiTree *T = &p;
BiTree *T = Creat();
printf("树的先序创建(#代表该节点为空):");
Tree_ProCreat(T);
printf("二叉树T已创建");
printf("\nT树的先序遍历:\n");
// Tree_ProPrint_No2(*T);//非递归先序遍历第二种方法
// putchar('\n');
Tree_ProPrint_No(*T);
putchar('\n');
Tree_ProPrint(*T);
printf("\nT树的中序遍历:\n");
// Tree_MidPrint_No2(*T);
// putchar('\n');
Tree_MidPrint_No(*T);
putchar('\n');
Tree_MidPrint(*T);
printf("\nT树的后序遍历:\n");
// Tree_BackPrint_No2(*T);
// putchar('\n');
Tree_BackPrint_No(*T);
putchar('\n');
Tree_BackPrint(*T);
printf("\nT树的层次遍历:\n");
Tree_LevelPrint(*T);
if(Tree_Empty(*T))
{
printf("\nT树为空树\n");
}
else
{
printf("\nT树的根节点为:%c",Tree_Root(*T));
}
printf("\nT树的深度为:%d",Tree_Depth(*T));
printf("\nT树的节点个数为:%d",Tree_NodeCount(*T));
printf("\nT树的叶子节点总数为%d个 ",Tree_LeafCount(*T));
printf("\n先序遍历T树叶子节点为:");
Tree_LeafProPrint(*T);
printf("\n中序遍历T树叶子节点为:");
Tree_LeafMidPrint(*T);
printf("\n后序遍历T树叶子节点为:");
Tree_LeafBackPrint(*T);
printf("\nT树所有叶子节点到根节点的路径分别是\n");
Tree_PrintLeafPath(*T,stack,0);
int k = 2;
printf("\nT树第%d层的节点为%d个",k,Tree_LevelCount(*T,k));
printf("\nT树的形状是:\n");
s1[0] = '\0';
Tree_Show(*T,*T,s1);
printf("\n复制原二叉树");
BiTree newroot = Tree_Copy(*T);
printf("\n新二叉树newT已创建");
BiTree *newT = &newroot;
printf("\nnewT树与T树是否相似(1.相似,0不相似):%d",Tree_IsSimilar(*T,*newT));
Tree_Mirror(*newT);
printf("\nnewT树镜像化成功");
printf("\nnewT树的先序遍历:");
Tree_ProPrint(*newT);
printf("\nnewT树的中序遍历:");
Tree_MidPrint(*newT);
printf("\nnewT树的后序遍历:");
Tree_BackPrint(*newT);
printf("\nnewT树的层次遍历:");
Tree_LevelPrint(*newT);
printf("\nnewT树与T树是否相似(1.相似,0不相似):%d",Tree_IsSimilar(*T,*newT));
printf("\nnewT树的形状是:\n");
s1[0] = '\0';
Tree_Show(*newT,*newT,s1);
printf("\nnewT树所有叶子节点到根节点的路径是\n");
Tree_PrintLeafPath(*newT,stack,0);
Tree_Destory(*newT);
printf("\nnewT树销毁成功");
*newT = NULL;
if(Tree_Empty(*newT))
{
printf("\nnewT树为空树");
}
return 0;
}