二叉树的构建与遍历(C语言)

目录

一、二叉树的存储结构

二、二叉树的遍历

三.节点个数以及高度

四、构建二叉树

五:完整代码


一、二叉树的存储结构

1.顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空 间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树

2.链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是 链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。 

下面主要来讲 链式存储 的二叉树。所以得先用结构体来表示二叉树单个节点的结构:
typedef int DataType;
typedef struct BinaryTreeNode{
	DataType data;
	struct BinaryTreeNode* left;//左孩子节点的地址
	struct BinaryTreeNode* right;//右孩子节点的地址
}BTNode;

二、二叉树的遍历

用递归的方式创建二叉树的过程比较抽象,所以先来讲二叉树的遍历(前、中、后)序。现在有下图的二叉树,我们依次来遍历此二叉树 :

1. 前序遍历 —— 访问根结点的操作发生在遍历其左右子树之前。
在下面代码中打印出节点的data值就是遍历该节点,前序遍历代码如下:
void PreOrder(BTNode* root){//前序遍历
	if (root == NULL){//空树就返回
		return;
	}
	printf("%d ", root->data);//打印当前节点的data值
	PreOrder(root->left);//遍历左子树
	PreOrder(root->right);//遍历右子树
}
上面的代码中,root为指向根节点的指针(根节点是值为1的节点)。下面继续用图像的形式来直观的上述代码的执行流程:

 所以前序遍历的结果为:1,2,3,4,5,6

2. 中序遍历 —— 访问根结点的操作发生在遍历其左右子树之中(间)。
中序遍历的代码只需要在前序遍历的代码上稍稍修改就好:
void InOrder(BTNode* root){//中序遍历
	if (root == NULL){
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

那么中序遍历的结果为:3,2,1,5,4,6

3. 后序遍历 —— 访问根结点的操作发生在遍历其左右子树之后。
void PostOrder(BTNode* root){//后序遍历
	if (root == NULL){
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

后序遍历的结果为:3,2,5,6,4,1

三.节点个数以及高度

1.二叉树节点个数

int BinaryTreeSize(BTNode* root){//二叉树节点数
	if (root == NULL){//空树直接返回0
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

以上代码也是递归,求出左子树的节点个数和右子树的节点个数,把他们加起来就是最终结果。加1指的是加上当前节点。

2.二叉树叶子节点的个数

int BinaryTreeLeafSize(BTNode* root){//叶子节点数
	if (root == NULL){//空树直接返回0
		return 0;
	}
	if (root->left == NULL&&root->right == NULL){//满足条件,说明遇到了叶子节点,返回1
		return 1;
	}
	return  BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

求叶子节点个数,关键在于判定当前节点是否为叶子节点,如果是则 返回1。

3.第k层节点个数(默认根节点为第一层)

int BinaryTreeLevelKSize(BTNode* root, int k){//第k层节点个数
	if (root == NULL || k < 1){//空树或者k不合法,直接返回0
		return 0;
	}
	if (k == 1){//当前的某一个节点
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

用直观的一幅图来理解上述代码:

 

 当想要求二叉树的第K层节点个数时,可以把问题转化成求其左右子树的第K-1层的节点个数,递归出口为root==NULL或当K==1时返回1,那么就求出了第K层节点个数。

4.二叉树的深度(只有一个节点那么深度是1)

int Depth(BTNode* root){//计算树的深度
	if (root == NULL){//无左右子树直接返回0
		return 0;
	}
	int leftdepth=Depth(root->left) + 1;//求其左子树深度
	int rightdepth = Depth(root->right) + 1;//求其右子树深度
	return leftdepth > rightdepth ? leftdepth : rightdepth;
}

将问题转化为求左右子树的深度,但是函数的返回值只有一个,返回值为左右子树中最深的树的深度。注意递归出。

5.查找某个节点

BTNode* BinaryTreeFind(BTNode* root, DataType x){//查找某个节点
	if (root == NULL){
		return NULL; 
	}
	if (root->data == x){
		return root;
	}
	BTNode* ret=BinaryTreeFind(root->left, x);
	if (ret){
		return ret;//找到了直接返回,就不用在遍历其右子树
	}
	return BinaryTreeFind(root->right, x);
}

用前序遍历的思想来创建二叉树,树为空,直接返回。先判断当前节点的值是否是要查找的值,如果不是,那么遍历其左子树,一旦找到就返回当前节点的地址。注意:在返回其左子树查找的结果时,要判断当前结果是否为空,如果是空,说明没有找到,那么还需要在右子树中去查找。

四、构建二叉树

链式存储的二叉树需要在堆上申请空间,所以先给出申请单个节点的代码:

BTNode* BuyNode(DataType x){
	BTNode* node = (BTNode *)malloc(sizeof(BTNode));
	if (node == NULL){
		perror("malloc");
		exit(0);
	}
	node->left = NULL;
	node->right = NULL;
	node->data = x;
	return node;
}

默认把单个节点的left和right的值置为NULL,返回值为创建好的节点的地址。

构建二叉树时需要用到前序遍历的思想,就是先创建根节点,然后在创建其左右子树。假如要创建如下图的二叉树:

首先会得到二叉树中的值为1 2 3 4 5 6,那么此时需要用数组将这些值保存起来,例如:

DataType arr[] = { 1, 2, 3,4, 5,6};

这时,如果用前序遍历(递归)的思想来创建这颗树,那么会发现根本找不到递归出口,无法创建。那么得找到那个递归的出口,现在可以把要创建的二叉树假想成下图的样子:

 对二叉树进行补充,补充上无效值0,那么保存二叉树值的数组变成:

DataType arr[] = { 1, 2, 3, 0, 0, 0, 4, 5, 0, 0, 6 ,0,0};

此时递归出口就是碰到无效值退出。先上代码:


BTNode* _CreatBinaryTree(DataType* arr, int size, DataType invalid, int *subscript){//创建二叉树
	if (*subscript < size&&arr[*subscript]!=invalid){//当没有越界或者没有碰到无效值就执行下面的代码
		BTNode* root = BuyNode(arr[*subscript]);//创建当前根节点
		(*subscript)++;//相当于数组下标加1
		root->left = _CreatBinaryTree(arr, size, invalid, subscript);//创建左子树
		(*subscript)++;
		root->right = _CreatBinaryTree(arr, size, invalid, subscript);
		return root;
	}
	return NULL;
}

此时代码有四个参数,第一个参数为:数组的首地址,第二个参数为:数组此时包含的元素个数,第三个参数为:数组中的无效值(可更改),第四个参数是一个变量的地址,这个变量初始值为0,代表数组元素的下标。

利用前序遍历的思想,在每次调用函数时就把当前根节点创建好,然后在创建其左右子树,需要注意的是:每次创建左右子树时数组的下标需要加1,所以才有了变量subscript,每次进行传递地址,以保证数组的下标在不断加1(也可以定义一个全局变量,此时就不用传递地址了)。递归结束条件为:碰到无效值0或者下标大于数组合法下标。

五:完整代码

1.BinaryTreeNode.h

#pragma once
#include <stdio.h>
#include <windows.h>
#include <assert.h>
typedef int DataType;
typedef struct BinaryTreeNode{
	DataType data;
	struct BinaryTreeNode* left;//左孩子节点的地址
	struct BinaryTreeNode* right;//右孩子节点的地址
}BTNode;
extern BTNode* CreatBinaryTree(DataType* arr, int size, DataType invalid);
extern void PreOrder(BTNode* root);
extern void InOrder(BTNode* root);
extern void PostOrder(BTNode* root);
extern int BinaryTreeSize(BTNode* root);
extern int BinaryTreeLeafSize(BTNode* root);
extern int BinaryTreeLevelKSize(BTNode* root, int k);
extern BTNode* BinaryTreeFind(BTNode* root, DataType x);
extern int Depth(BTNode* root);
extern void BinaryTreeDestory(BTNode** root);

2.BinaryTreeNode.c

#include "BinaryTreeNode.h"
BTNode* BuyNode(DataType x){
	BTNode* node = (BTNode *)malloc(sizeof(BTNode));
	if (node == NULL){
		perror("malloc");
		exit(0);
	}
	node->left = NULL;
	node->right = NULL;
	node->data = x;
	return node;
}
BTNode* _CreatBinaryTree(DataType* arr, int size, DataType invalid, int *subscript){//创建二叉树
	if (*subscript < size&&arr[*subscript]!=invalid){//当没有越界或者没有碰到无效值就执行下面的代码
		BTNode* root = BuyNode(arr[*subscript]);//创建当前根节点
		(*subscript)++;//相当于数组下标加1
		root->left = _CreatBinaryTree(arr, size, invalid, subscript);//创建左子树
		(*subscript)++;
		root->right = _CreatBinaryTree(arr, size, invalid, subscript);
		return root;
	}
	return NULL;
}
BTNode * CreatBinaryTree(DataType* arr, int size, DataType invalid){//创建二叉树
	int subscript = 0;//可以理解为数组下标
	return _CreatBinaryTree(arr, size, invalid, &subscript);
}
void PreOrder(BTNode* root){//前序遍历
	if (root == NULL){//空树就返回
		return;
	}
	printf("%d ", root->data);//打印当前节点的data值
	PreOrder(root->left);//遍历左子树
	PreOrder(root->right);//遍历右子树
}
void InOrder(BTNode* root){//中序遍历
	if (root == NULL){
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
void PostOrder(BTNode* root){//后序遍历
	if (root == NULL){
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
int BinaryTreeSize(BTNode* root){//二叉树节点数
	if (root == NULL){//空树直接返回0
		return 0;
	}
	return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
int BinaryTreeLeafSize(BTNode* root){//叶子节点数
	if (root == NULL){//空树直接返回0
		return 0;
	}
	if (root->left == NULL&&root->right == NULL){//满足条件,说明遇到了叶子节点,返回1
		return 1;
	}
	return  BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
int BinaryTreeLevelKSize(BTNode* root, int k){//第k层节点个数
	if (root == NULL || k < 1){//空树或者k不合法,直接返回0
		return 0;
	}
	if (k == 1){//当前的某一个节点
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
BTNode* BinaryTreeFind(BTNode* root, DataType x){//查找某个节点
	if (root == NULL){
		return NULL; 
	}
	if (root->data == x){
		return root;
	}
	BTNode* ret=BinaryTreeFind(root->left, x);
	if (ret){
		return ret;//找到了直接返回,就不用在遍历其右子树
	}
	return BinaryTreeFind(root->right, x);
}
int Depth(BTNode* root){//计算树的深度
	if (root == NULL){
		return 0;
	}
	int leftdepth=Depth(root->left) + 1;//求其左子树深度
	int rightdepth = Depth(root->right) + 1;//求其右子树深度
	return leftdepth > rightdepth ? leftdepth : rightdepth;
}
void BinaryTreeDestory(BTNode** root){//销毁二叉树
	assert(root);
	if (*root == NULL){
		return;
	}
	BinaryTreeDestory(&(*root)->left);
	BinaryTreeDestory(&(*root)->right);
	free(*root);
	*root = NULL;
}

3.main.c

#include "BinaryTreeNode.h"
int main(){
	DataType arr[] = { 1, 2, 3, 0, 0, 0, 4, 5, 0, 0, 6 };
	BTNode* root = CreatBinaryTree(arr,sizeof(arr)/sizeof(arr[0]),-1);
	PreOrder(root);
	printf("\n");
	InOrder(root);
	printf("\n");
	PostOrder(root);
	printf("\n");
	printf("二叉树节点个数为:%d\n", BinaryTreeSize(root));
	printf("二叉树叶子节点个数为:%d\n", BinaryTreeLeafSize(root));
	printf("二叉树第3层节点个数为:%d\n", BinaryTreeLevelKSize(root,3));
	printf("二叉树深度为:%d\n", Depth(root));
	BinaryTreeDestory(&root);
	system("pause");
	return 0;
}

  • 7
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
二叉树的层序遍历可以使用队列来实现。具体步骤如下: 1. 定义一个队列,用于存储待访问的节点。 2. 将根节点入队。 3. 循环执行以下操作,直到队列为空: - 弹出队首节点,并访问该节点。 - 如果该节点有左子节点,则将左子节点入队。 - 如果该节点有右子节点,则将右子节点入队。 4. 遍历结束。 下面是一个示例的C语言代码实现: ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树节点结构 typedef struct TreeNode { int val; // 节点值 struct TreeNode* left; // 左子节点指针 struct TreeNode* right; // 右子节点指针 } TreeNode; // 定义队列节点结构 typedef struct QueueNode { struct TreeNode* data; // 队列节点数据 struct QueueNode* next; // 下一个队列节点指针 } QueueNode; // 定义队列结构 typedef struct Queue { QueueNode* front; // 队头指针 QueueNode* rear; // 队尾指针 } Queue; // 初始化队列 void initQueue(Queue* queue) { queue->front = NULL; queue->rear = NULL; } // 入队操作 void enqueue(Queue* queue, TreeNode* data) { QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode)); newNode->data = data; newNode->next = NULL; if (queue->rear == NULL) { queue->front = newNode; queue->rear = newNode; } else { queue->rear->next = newNode; queue->rear = newNode; } } // 出队操作 TreeNode* dequeue(Queue* queue) { if (queue->front == NULL) { return NULL; } QueueNode* temp = queue->front; TreeNode* data = temp->data; queue->front = queue->front->next; if (queue->front == NULL) { queue->rear = NULL; } free(temp); return data; } // 层序遍历 void levelOrderTraversal(TreeNode* root) { if (root == NULL) { return; } Queue queue; initQueue(&queue); enqueue(&queue, root); while (queue.front != NULL) { TreeNode* node = dequeue(&queue); printf("%d ", node->val); if (node->left != NULL) { enqueue(&queue, node->left); } if (node->right != NULL) { enqueue(&queue, node->right); } } } int main() { // 构建二叉树 TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); root->val = 1; TreeNode* node2 = (TreeNode*)malloc(sizeof(TreeNode)); node2->val = 2; TreeNode* node3 = (TreeNode*)malloc(sizeof(TreeNode)); node3->val = 3; TreeNode* node4 = (TreeNode*)malloc(sizeof(TreeNode)); node4->val = 4; TreeNode* node5 = (TreeNode*)malloc(sizeof(TreeNode)); node5->val = 5; root->left = node2; root->right = node3; node2->left = node4; node2->right = NULL; node3->left = NULL; node3->right = node5; node4->left = NULL; node4->right = NULL; node5->left = NULL; node5->right = NULL; // 层序遍历 levelOrderTraversal(root); return 0; } ``` 上述代码中,首先定义了二叉树节点结构和队列节点结构,并初始化了队列。然后,使用队列实现层序遍历的核心部分。最后,在主函数中构建了一个示例二叉树,并调用层序遍历函数进行输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值