(一).二叉树是度为2的树,最大的度不能超过2,如果使用链式存储,二叉树的遍历通常是通过递归进行实现,二叉树的形状也符合递归的使用,使用递归需要有递归限制的条件,并且每一次的递归都要越来越接近限制条件,使用递归的时候切记不能使用大脑模拟,根本想不出的,如果使用一个函数的时候需要用到递归,就假设已经有这个函数了,拿来使用就可以了。
1.定义一个结构体类型,里面有三个值,分别是数据域,和指向左右孩子的指针,初始时候已经将左右孩子指向空指针了,所以每个叶子节点下面其实是有节点的,只不过是空节点。
typedef int BTDataType;//把int重新命名为BTDataType
typedef struct BTNode
{
BTDataType data;//数据域
struct BTNode* leftchild;//左右孩子
struct BTNode* rightchild;
}BTNode,*BTree;//第一个是节点的名称,第二个是二叉树的名称
2.用结构体创建一个结构体变量,并在结构体中赋值,为了接下来的运算,创建一个函数将分配一个二叉树节点的大小,并赋值,返回这个指针,从树根节点以次向后链接你所设想的二叉树的样子
//申请节点
BTNode* GetNode(BTDataType elem)
{
BTNode* s = (BTNode*)malloc(sizeof(BTNode));
if (s == NULL)
exit(1);
s->leftchild = s->rightchild = NULL;
s->data = elem;
return s;
}
//二叉树的构建
BTree FoundBT()
{
BTNode* n1 = GetNode(1);
BTNode* n2 = GetNode(2);
BTNode* n3 = GetNode(3);
BTNode* n4 = GetNode(4);
BTNode* n5 = GetNode(5);
BTNode* n6 = GetNode(6);
BTNode* n7 = GetNode(7);
n1->leftchild = n2;
n1->rightchild = n3;
n2->leftchild = n4;
n2->rightchild = n5;
n4->leftchild = n6;
n4->rightchild = n7;
return n1;
}
3.二叉树的前序遍历,将二叉树按照根节点,左孩子,右孩子以次遍历,在遍历的过程中就假设已经有了这个函数,这个函数可以帮我实现前序遍历,首先设置边界条件,root==NULL就停止递归,然后访问根节点,接着访问左子树,然后访问右子树,用脑袋想不出来是怎么实现的。就假设有这个函数。
void PreOrder(BTree root)
{
if (root == NULL)
return ;
printf("%d ", root->data);
PreOrder(root->leftchild);
PreOrder(root->rightchild);
}
二叉树的中序遍历
//中序遍历
void InOrder(BTree root)
{
if (root == NULL)
return;
InOrder(root->leftchild);
printf("%d ", root->data);//这里面的root就是根节点的意思
InOrder(root->rightchild);
}
二叉树的后序遍历,以上三种方式是很相似的,只是访问根节点的顺序不一样
//后序遍历
void PostOrder(BTree root)
{
if (root == NULL)
return;
PostOrder(root->leftchild);
PostOrder(root->rightchild);
printf("%d ", root->data);
}
4.二叉树的层次遍历,层次遍历需要通过一个队列进行辅助,其主要的思想就是将头节点入队,入队的是二叉树的节点指针,这个不浪费空间,然后访问出队的这个元素的数据域,判断出队的这个节点的左孩子和右孩子是不是空指针,如果不是空指针就入队,如果不是空指针就不入队,由于入队一次可以入两个,而出队只能一次出一个,所以外层循环就是队列为空就不循环。
定义一个队列的结构体
typedef struct LinkNode
{
BTNode* data;//这里是二叉树指针类型
struct LinkNode*next;
}LinkNode;
typedef struct Queue
{
LinkNode* rear, * front;//两个指针
}Queue;
初始化队列,采取带头节点的队列
//队列的初始化
void InitQueue(Queue* pq)
{
LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
if (p == NULL)
return;
p->next = NULL;
pq->front = p;
pq->rear = p;
}
队列的判空
//判断一个队列是不是为空
int Is_empty(Queue Q)
{
if (Q.front == Q.rear)
return 1;//为空
return 0;//非空
}
入队操作
//队列入队
int EnQueue(Queue* p, BTNode* elem)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
if (s == NULL)
return 1;
s->data = elem;
p->rear->next = s;
s->next = NULL;
p->rear = s;
return 0;
}
出队操作
//队列出队
BTNode* Delete(Queue* p)
{
if (Is_empty(*p))
return NULL;
LinkNode* ptr = p->front->next;
BTNode* temp = ptr->data;
if (ptr == p->rear)
{
p->rear = p->front;
}
p->front->next = ptr->next;
free(ptr);
return temp;
}
二叉树层次遍历代码
//层次遍历
void LevelOrder(BTree root)
{
Queue Q;
InitQueue(&Q);
EnQueue(&Q, root);
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
printf("%d ", front->data);
if (front->leftchild != NULL)
EnQueue(&Q, front->leftchild);
if (front->rightchild != NULL)
EnQueue(&Q, front->rightchild);
}
free(Q.front);
Q.front = NULL;
}
5.统计二叉树节点的个数,需要统计二叉树节点的个数是不是可以分解成统计二叉树左边二叉树的个数加上右边二叉树的个数,这样一直想下去就会无限套下去,先计算左边的个数,在计算右边的个数,最后返回左边的个数加上右边的个数不就可以了,就假设系统中已经有了这样的函数给你用,直接用就行,每个函数处理的都是相同的问题。
int BTNodeSize(BTree root)
{
if (root == NULL)
return 0;
int left = BTNodeSize(root->leftchild);
int right = BTNodeSize(root->rightchild);
return 1 + right + left;/*+ BTNodeSize(root->leftchild) + BTNodeSize(root->rightchild);*/
}
6.统计叶子节点的个数不也就是统计左边二叉树和右边二叉树有结果左边孩子和右边孩子都是空指针,递归的限制条件改了,如果是空指针就返回0,如果左右孩子都是空指针就返回1,计算左右树的叶子节点加一起就是总共的叶子节点数量
//二叉树中叶子节点的个数
int BTLeafSize(BTree root)
{
if (root == NULL)
return 0;
if (root->leftchild == NULL && root->rightchild == NULL)
return 1;
return BTLeafSize(root->leftchild) + BTLeafSize(root->rightchild);
}
7.统计第k层上有几个叶子节点,这个时候的判断条件又更改了,只有k满足一定的条件才满足,那就让k-1,一直减到限制条件,递归是从上到下递归的。当k等于1的时候就是限制条件,可以带入第一层和第二层验证一下
int BTKSize(BTree root,int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BTKSize(root->leftchild, k - 1) + BTKSize(root->rightchild, k - 1);
}
8,二叉树的深度,还是判断左边二叉树的深度和右边二叉树的深度的最大值,因为这个判断的是左右子树,所以还是需要再加上1,得到整棵树的最大深度,这个时候代码就好写了,就假象已经有了这个函数,这个函数可以帮我们实现这个功能
//二叉树的高度
int Depth(BTree root)
{
if (NULL == root)
return 0;
int left = Depth(root->leftchild);
int right = Depth(root->rightchild);
return left > right ? (left + 1 ):( right + 1);
}
9,查找节点的值,这个时候判断条件换成,如果是空指针就返回NULL,如果找到了就返回这个指针,也是分别遍历左右子树,看返回值是不是空指针,如果是空指针说明找不到,不是空指针就直接返回
//查找树节点的的值
BTNode* BTFind(BTree root, int n)
{
if (root == NULL)
return NULL;
if (root->data == n)
return root;//递归停止的条件
BTNode* left = BTFind(root->leftchild, n);//左孩子看有没有相同的
if (left != NULL)
return left;
BTNode* right = BTFind(root->rightchild, n);//
if (right != NULL)
return right;
}
10.判断一颗二叉树是不是完全二叉树,完全二叉树的定义就是除了最后一个层,其他层都是满的指针,没有空指针,和进行层次遍历一样,不过这次需要把空指针也放入队列中,当队列出来一个空指针的时候,把判断指针接下来所以的元素,如果出现了节点,那就不是完全二叉树,如果从这个二叉树往下都是空指针,那这个二叉树就是完全二叉树
//判断一课二叉树是不是完全二叉树
int BTComplete(BTree root)
{
Queue Q;
InitQueue(&Q);
EnQueue(&Q, root);
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
if (front == NULL)
break;
EnQueue(&Q,front->leftchild);
EnQueue(&Q,front->rightchild);
}
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
if (front != NULL)
return 1;//说明遇到节点了,为假的
}
return 0;
}
11.销毁一棵树,由于销毁需要更改二叉树的值,所以传递的是指针,同样的从左右子树开始销毁,最后再销毁树根节点,递归递归,分为两种方式,先递出去然后再归回来,这里的销毁是从最高层以次向低层销毁的
//销毁一个树
void Destory(BTree* proot)//传递指针才能够修改值
{
if ((*proot) == NULL)
return;
Destory(&(*proot)->leftchild);
Destory(&(*proot)->rightchild);
free(*proot);
*proot = NULL;
}
12.整体代码
#define _CRT_SECURE_NO_WARNINGS
//二叉树的一些运算方法
#include<stdio.h>
#include<stdlib.h>
typedef int BTDataType;//把int重新命名为BTDataType
typedef struct BTNode
{
BTDataType data;//数据域
struct BTNode* leftchild;//左右孩子
struct BTNode* rightchild;
}BTNode,*BTree;//第一个是节点的名称,第二个是二叉树的名称
typedef struct LinkNode
{
BTNode* data;//这里是二叉树指针类型
struct LinkNode*next;
}LinkNode;
typedef struct Queue
{
LinkNode* rear, * front;//两个指针
}Queue;
//申请节点
BTNode* GetNode(BTDataType elem)
{
BTNode* s = (BTNode*)malloc(sizeof(BTNode));
if (s == NULL)
exit(1);
s->leftchild = s->rightchild = NULL;
s->data = elem;
return s;
}
//二叉树的构建
BTree FoundBT()
{
BTNode* n1 = GetNode(1);
BTNode* n2 = GetNode(2);
BTNode* n3 = GetNode(3);
BTNode* n4 = GetNode(4);
BTNode* n5 = GetNode(5);
BTNode* n6 = GetNode(6);
BTNode* n7 = GetNode(7);
n1->leftchild = n2;
n1->rightchild = n3;
n2->leftchild = n4;
n2->rightchild = n5;
n4->leftchild = n6;
n4->rightchild = n7;
return n1;
}
//前序遍历
void PreOrder(BTree root)
{
if (root == NULL)
return ;
printf("%d ", root->data);
PreOrder(root->leftchild);
PreOrder(root->rightchild);
}
//中序遍历
void InOrder(BTree root)
{
if (root == NULL)
return;
InOrder(root->leftchild);
printf("%d ", root->data);//这里面的root就是根节点的意思
InOrder(root->rightchild);
}
//后序遍历
void PostOrder(BTree root)
{
if (root == NULL)
return;
PostOrder(root->leftchild);
PostOrder(root->rightchild);
printf("%d ", root->data);
}
//队列的初始化
void InitQueue(Queue* pq)
{
LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
if (p == NULL)
return;
p->next = NULL;
pq->front = p;
pq->rear = p;
}
//判断一个队列是不是为空
int Is_empty(Queue Q)
{
if (Q.front == Q.rear)
return 1;//为空
return 0;//非空
}
//队列入队
int EnQueue(Queue* p, BTNode* elem)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
if (s == NULL)
return 1;
s->data = elem;
p->rear->next = s;
s->next = NULL;
p->rear = s;
return 0;
}
//队列出队
BTNode* Delete(Queue* p)
{
if (Is_empty(*p))
return NULL;
LinkNode* ptr = p->front->next;
BTNode* temp = ptr->data;
if (ptr == p->rear)
{
p->rear = p->front;
}
p->front->next = ptr->next;
free(ptr);
return temp;
}
//层次遍历
void LevelOrder(BTree root)
{
Queue Q;
InitQueue(&Q);
EnQueue(&Q, root);
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
printf("%d ", front->data);
if (front->leftchild != NULL)
EnQueue(&Q, front->leftchild);
if (front->rightchild != NULL)
EnQueue(&Q, front->rightchild);
}
free(Q.front);
Q.front = NULL;
}
//二叉树中节点的个数
int BTNodeSize(BTree root)
{
if (root == NULL)
return 0;
int left = BTNodeSize(root->leftchild);
int right = BTNodeSize(root->rightchild);
return 1 + right + left;/*+ BTNodeSize(root->leftchild) + BTNodeSize(root->rightchild);*/
}
//二叉树中叶子节点的个数
int BTLeafSize(BTree root)
{
if (root == NULL)
return 0;
if (root->leftchild == NULL && root->rightchild == NULL)
return 1;
return BTLeafSize(root->leftchild) + BTLeafSize(root->rightchild);
}
//第k层节点的个数
int BTKSize(BTree root,int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BTKSize(root->leftchild, k - 1) + BTKSize(root->rightchild, k - 1);
}
//二叉树的高度
int Depth(BTree root)
{
if (NULL == root)
return 0;
int left = Depth(root->leftchild);
int right = Depth(root->rightchild);
return left > right ? (left + 1 ):( right + 1);
}
//查找树节点的的值
BTNode* BTFind(BTree root, int n)
{
if (root == NULL)
return NULL;
if (root->data == n)
return root;//递归停止的条件
BTNode* left = BTFind(root->leftchild, n);//左孩子看有没有相同的
if (left != NULL)
return left;
BTNode* right = BTFind(root->rightchild, n);//
if (right != NULL)
return right;
}
//判断一课二叉树是不是完全二叉树
int BTComplete(BTree root)
{
Queue Q;
InitQueue(&Q);
EnQueue(&Q, root);
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
if (front == NULL)
break;
EnQueue(&Q,front->leftchild);
EnQueue(&Q,front->rightchild);
}
while (!Is_empty(Q))
{
BTNode* front = Delete(&Q);
if (front != NULL)
return 1;//说明遇到节点了,为假的
}
return 0;
}
//销毁一个树
void Destory(BTree* proot)//传递指针才能够修改值
{
if ((*proot) == NULL)
return;
Destory(&(*proot)->leftchild);
Destory(&(*proot)->rightchild);
free(*proot);
*proot = NULL;
}
int main()
{
BTree root = FoundBT();
PreOrder(root);
Destory(&root);
printf("\n");
InOrder(root);
printf("\n");
LevelOrder(root);
printf("\n");
int ret = BTComplete(root);
printf("%d ", ret);
return 0;
}