1、各部分名称
1、根节点
为二叉树最顶端的节点。
2、父节点
父节点拥有1~2个孩子节点,除去最下端的节点,基本都是父节点。
3、子节点
子节点是父节点引出来的节点。
4、叶子节点
叶子节点为最底端的节点。
![](https://i-blog.csdnimg.cn/blog_migrate/acf5a948ceb957f451a828fda6f8fdfa.png)
2、分类
首先,二叉树属于树形结构,二叉树分为几种类别:
1、满二叉树
指叶子节点都在最后一层,而且除叶子节点以外,其他的所有节点都有两个子节点。
2、完全二叉树
去掉最后一层节点后,剩下来的树是满二叉树,还有一个要求,最后一层必须是左右分布
![](https://i-blog.csdnimg.cn/blog_migrate/0d3cfb05eb3236bbde695034be08ec2f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1928b45772a1b39dec455e6a44b78106.png)
3、各个种类的关系
画个韦恩图来理解一下
![](https://i-blog.csdnimg.cn/blog_migrate/f1f5997366ddc4d7d16d15ee85c32699.png)
可以从图中看到,满二叉树在完全二叉树里面,因为它包含了完全二叉树的所有特征 ,所以在完全二叉树里面。
3、二叉树的度量
1、高度
可以从字面意思来理解一下,高度是从0开始,从叶子节点开始,往上一层高度就加一
2、深度
深度与高度相反,也是从0开始,但是从根节点开始,往下一层深度就加一
3、层数
层数最好理解,从1开始,从根节点开始,往上一层层数加一
![](https://i-blog.csdnimg.cn/blog_migrate/9b60879317c23af086e315793497190f.png)
4、 二叉树的一些常用公式
完全二叉树
- 求二叉树节点之上至多节点个数
,i为当前层数
- 求二叉树至多有多少个节点
,n为层数
大概呢,萌新大概只要懂这两条公式就行啦。不要去学什么像求卡兰特数的公式,只要懂这点就阔以啦ヾ(≧ ▽ ≦)ゝ。
5、二叉树的实现
这一部分是最难的部分,也是最后一个部分,赶紧冲!
1、存储
存储当然是用结构体啦,结构体保存着:自己的值,左孩子,右孩子
例:
typedef struct treeNode
{
char data; //数据域字符表示
struct treeNode* LChild;//左孩子
struct treeNode* RChild;//右孩子
}TREE,*LPTREE;
肥肠简单,大家应该学会了👌👌👌👌
连接也蛮简单
直接去改结构体里的值就可以了
写个函数传3个参数:父节点,左孩子,右孩子
上例子
void insertNode(LPTREE parentNode, LPTREE LChild, LPTREE RChild)
{
parentNode->LChild = LChild;//连接左孩子
parentNode->RChild = RChild;//连接右孩子
}
看了这些,大家应该会了链式存储了吧。
2、遍历
1、前序
前序遍历顺序为:根—>左—>右。
意思就是我先遍历父节点,再遍历左孩子,最后遍历右孩子
以最上面的满二叉树为例,前序遍历顺序为:
1—>2—>4—>5—>3—>6—>7
2、中序
中序遍历顺序为:左—>根—>右
意思呢,应该不用多讲
还是以最上面的满二叉树为例,中序遍历顺序为:
4—>2—>5—>1—>6—>3—>7
3、后序
中序遍历顺序为:左—>右—>根
意思呢,应该不用多讲
还是以最上面的满二叉树为例,后序遍历顺序为:
4—>5—>2—>6—>7—>3—>1、
讲完了遍历,最后献上完整代码:
#include <stdio.h>
#include <stdlib.h>
/*
* 1.了解二叉树
* 1.1基本概念
* 根结点
* 右子树指针
* 左子树指针
* 孩子结点
* 父结点
* 兄弟结点
* 姐妹结点
* 2.二叉树的遍历
*/
//描述单一个体
typedef struct treeNode
{
char data; //数据域字符表示
struct treeNode* LChild;//左孩子
struct treeNode* RChild;//右孩子
}TREE,*LPTREE;
//别名中LP一般表示指针别名
LPTREE createNode(char data)
{
LPTREE newNode = (LPTREE)malloc(sizeof(TREE));
newNode->data = data;//把自己赋值一下
newNode->LChild = NULL;//左孩子设为空
newNode->RChild = NULL;//右孩子设为空
return newNode;
}
//没有规律的树
void insertNode(LPTREE parentNode, LPTREE LChild, LPTREE RChild)
{
parentNode->LChild = LChild;//连接左孩子
parentNode->RChild = RChild;//连接右孩子
}
//打印当前结点中的元素
void printCurNodeData(LPTREE curData)
{
printf("%c\t", curData->data);
}
//递归法:领悟
//先序:根左右
void preOrder(LPTREE root)
{
if (root != NULL)
{
printCurNodeData(root);//输出自己,也就是根
preOrder(root->LChild);//然后递归左孩子,因为它们也有自己的左孩子与右孩子
preOrder(root->RChild);//最后递归右孩子,跟上面一样的道理
}
}
void preOrderBystack(LPTREE root)
{
if (root == NULL)
return;
//准备栈
struct treeNode* stack[10];//存储每次打印节点的位置
int stackTop = -1;//栈顶标记
//stackTop=50
//栈容量的是0-50
LPTREE pMove = root;//从根节点开始打印
while (stackTop!=-1||pMove)
{
//根 左 右
//找到最左边
while (pMove)
{
//把路径入栈+打印走过的节点
printf("%c\t", pMove->data);
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
//无路可走的处理
if (stackTop != -1)
{
pMove = stack[stackTop];//获取栈顶元素
stackTop--;//然后处理栈顶标记
pMove = pMove->RChild;
}
}
}
//中序:左根右
void midOrder(LPTREE root)
{
if (root != NULL)
{
midOrder(root->LChild);//左孩子
printCurNodeData(root);//根节点
midOrder(root->RChild);//右孩子
}
}
void midOrderBystack(LPTREE root)
{
if (root == NULL)
{
return;
}
//栈的准备操作
struct treeNode* stack[10];
int stackTop = -1;
//定义移动的指针
LPTREE pMove = root;
while (stackTop != -1 || pMove)
{
//走到最左边,把走过的节点入栈
while (pMove)
{
stack[++stackTop]=pMove;
pMove = pMove->LChild;
}
//出栈
if (stackTop != -1)
{
pMove = stack[stackTop--];
printf("%c\t",pMove->data);
pMove = pMove->RChild;
}
}
}
//后序:左右根
void lastOrder(LPTREE root)
{
if (root != NULL)
{
lastOrder(root->LChild);//左孩子
lastOrder(root->RChild);//右孩子
printCurNodeData(root);//根节点
}
}
void lastOrderBysatck(LPTREE root)
{
if (root == NULL)
return;
struct treeNode* stack[10];
int stackTop = -1;
struct treeNode* pMove = root;
struct treeNode* pLastVisit = NULL;//访问标记
//左右根
while (pMove)
{
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
while (stackTop != -1)
{
pMove = stack[stackTop--];
//当前节点左右是否被访问
if (pMove->RChild == NULL || pMove->RChild == pLastVisit)
{
//如果被访问就可以打印当前节点数据
printf("%c\t", pMove->data);
pLastVisit = pMove;
}
else
{
//右边没有被访问访问
stack[++stackTop] = pMove;
pMove = pMove->RChild;
while (pMove)
{
stack[++stackTop] = pMove;
pMove = pMove->LChild;
}
}
}
}
int main()
{
//死板的创建过程,无实际作用
LPTREE A = createNode('A');
LPTREE B = createNode('B');
LPTREE C = createNode('C');
LPTREE D = createNode('D');
LPTREE E = createNode('E');
LPTREE F = createNode('F');
LPTREE G = createNode('G');
insertNode(A, B, C);
insertNode(B, D, NULL);
insertNode(D, NULL, G);
insertNode(C, E, F);
printf("先序递归:\n");
preOrder(A);
printf("\n先序非递归\n");
preOrderBystack(A);
printf("\n中序递归:\n");
midOrder(A);
printf("\n中序非递归:\n");
midOrderBystack(A);
printf("\n后序递归:\n");
midOrder(A);
printf("\n后序非递归:\n");
midOrderBystack(A);
printf("\n");
system("pause");
return 0;
}
最后,该告别了,拜拜!👋👋👋