二叉树的基本操作(C语言版)
今天走进数据结构之二叉树
二叉树的基本操作(C 语言版)
1 二叉树的定义
二叉树的图长这样:
二叉树是每个结点最多有两个子树的树结构,常被用于实现二叉查找树和二叉堆。二叉树是链式存储结构,用的是二叉链,本质上是链表。二叉树通常以结构体的形式定义,如下,结构体内容包括三部分:本节点所存储的值、左孩子节点的指针、右孩子节点的指针。
struct TreeNode {//树的结点
int data;//数据域
struct TreeNode* lchild;//指向左孩子节点
struct TreeNode* rchild;//指向右孩子节点
};
当然,我们也可以为我们的的树节点结构体重新定义一下名字,使用 C 语言中的 typedef 方法就可以了。
struct TreeNode {//树的结点
int data;//数据域
struct TreeNode* lchild;//指向左孩子节点
struct TreeNode* rchild;//指向右孩子节点
} BiNode, *BiTree;
2 二叉树的建立
二叉树的操作通常使用递归方法,二叉树的操作可以分为两类,一类是需要改变二叉树的结构的,比如二叉树的创建、节点删除等等,这类操作,传入的二叉树的节点参数为二叉树指针的地址,这种参入传入,便于更改二叉树结构体的指针(即地址)。
如下是二叉数创建的函数,这里我们规定,节点值必须为大于 0 的数值,如果不是大于 0 的数,则表示结束继续往下创建子节点的操作。然后我们使用递归的方法以此创建左子树和右子树。
比如说,建立这个二叉树:
5
/ \
3 8
/ / \
2 6 9
首先根据这个二叉树,我们先模拟一下:
先序输入:5 3 2 0 0 0 8 6 0 0 9 0 0
先序遍历输出:5 3 2 8 6 9
中序遍历输出:2 3 5 6 8 9
后序遍历输出:2 3 6 9 8 5
层次遍历输出:5 3 8 2 6 9
下面通过先序的方式建立二叉树:
- 第一种建立二叉树:使用一级指针
//先序建立二叉树
BiTree CreateTree() {
int data;
scanf("%d", &data);//根节点数据
BiTree root;
if (data <= 0) {
return NULL;
} else {
root = (BiTree)malloc(sizeof(BiNode));
root->data = data;
root->lchild = CreateTree();
root->rchild = CreateTree();
}
return root;
}
测试使用:
//测试
int main() {
//BiTree root;
//CreateTree(&root);
BiTree root = NULL;
root = CreateTree();//创建树
PreOrderTraverse(root);//先序遍历输出
return 0;
}
- 第二种建立二叉树:使用二级指针
//先序建立二叉树
void CreateTree(BiTree* root) {
int data;
scanf("%d", &data);//根节点数据
if (data <= 0) {
*root = NULL;
} else {
(*root) = (BiTree)malloc(sizeof(BiNode));
(*root)->data = data;
CreateTree(&((*root)->lchild));
CreateTree(&((*root)->rchild));
}
}
测试使用:
//测试
int main() {
BiTree root;
CreateTree(&root);
//BiTree root = NULL;
//root = CreateTree();//创建树
PreOrderTraverse(root);//先序遍历输出
return 0;
}
如果没有要求的话,我比较倾向于第一种!
3 二叉树的遍历
3.1 先序遍历
先序遍历的思路:
先序遍历的过程是首先访问根结点,然后先序遍历根的左子树,最后先序遍历根的右子树。对于根的左子树和右子树,遍历的过程相同。
方案一:递归
- 采用递归的方式来实现:
//先序遍历二叉树:递归实现
void PreOrderTraverse(BiTree root) {
if (root) {
printf("%d ", root->data);
PreOrderTraverse(root->lchild);
PreOrderTraverse(root->rchild);
}
}
方案二:非递归
- 非递归实现:引入辅助栈
//先序遍历二叉树:非递归实现
void PreOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
//根节点入栈
top++;
stack[top] = root;
//栈不空时循环
while (top > -1) {
//出栈并访问该节点
p = stack[top];
top--;
printf("%d ", p->data);
//右孩子入栈
if (p->rchild != NULL) {
top++;
stack[top] = p->rchild;
}
//左孩子入栈
if (p->lchild != NULL) {
top++;
stack[top] = p->lchild;
}
}
}
}
3.2 中序遍历
中序遍历的思路
中序遍历的过程是首先中序遍历左子树,然后访问根结点,最后中序遍历根的右子树。对于根的左子树和右子树,遍历的过程相同。
方案一:递归
- 采用递归的方式来实现:
//中序遍历二叉树:递归实现
void InOrderTraverse(BiTree root) {
if (root) {
InOrderTraverse(root->lchild);
printf("%d ", root->data);
InOrderTraverse(root->rchild);
}
}
方案二:非递归
- 非递归实现:引入辅助栈
//中序遍历二叉树:非递归实现
void InOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
p = root;
while (top > -1 || p != NULL) {
//扫描p的所有左节点并入栈
while (p != NULL) {
top++;
stack[top] = p;
p = p->lchild;
}
if (top > -1) {
//出栈并访问节点
p = stack[top];
top--;
printf("%d ", p->data);
//扫描右孩子
p = p->rchild;
}
}
}
}
3.3 后序遍历
后序遍历的思路
后序遍历的过程是首先后序遍历左子树,然后后序遍历根的右子树,最后访问根结点。
方案一:递归
- 采用递归的方式来实现:
//后序遍历二叉树:递归实现
void PostOrderTraverse(BiTree root) {
if (root) {
PostOrderTraverse(root->lchild);
PostOrderTraverse(root->rchild);
printf("%d ", root->data);
}
}
方案二:非递归
- 非递归实现:引入辅助栈
//后序遍历二叉树:非递归实现
void PostOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
int sign;
if (root != NULL) {
do {
//root节点入栈
while (root != NULL) {
top++;
stack[top] = root;
root = root->lchild;
}
//p指向栈顶前一个已访问节点
p = NULL;
//置root为已访问
sign = 1;
while (top != -1 && sign) {
//取出栈顶节点
root = stack[top];
//右孩子不存在或右孩子已访问则访问root
if (root->rchild == p) {
printf("%d ", root->data);
top--;
//p指向被访问的节点
p = root;
} else {
//root指向右孩子节点
root = root->rchild;
//置未访问标记
sign = 0;
}
}
} while (top != -1);
}
}
3.4 层次遍历
层次遍历的思路:
思路:在进行层次遍历时,对一层结点访问完后再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层地进行,先遇到的结点先访问,这棵二叉树的层次遍历序列为 5 3 8 2 6 9,先上到下,先左到右。实现层次遍历用队列比较方便,因为是先进先出(FIFO)。首先把 5 入队,然后再输出队首元素,并且把队首元素的左结点和右结点入队(如果有的话),以此类推,输出的序列就是层次遍历啦
- 采用非递归方式实现:引入队列
//层次遍历:非递归实现
void LevelOrderTraverseNonRec(BiTree root) {
BiTree p;
Push(root);
while (!empty()) {//empty()判断队列是否为空
p = Pop();//出队
printf("%d ", p->data);//输出队首结点
if (p->lchild) {//把Pop掉的结点的左子结点加入队列
Push(p->lchild);
}
if (p->rchild) {//把Pop掉的结点的右子结点加入队列
Push(p->rchild);
}
}
}
附队列部分代码:
//队列结构体
typedef struct queue {
struct TreeNode* numQueue[MaxSize];
int front;
int rear;
} Queue;
Queue queue;//声明全局变量
//初始化队列
void initQueue() {
queue.front = 0;
queue.rear = 0;
}
//入队
void Push(BiTree root) {
queue.numQueue[++queue.rear] = root;
}
//出队
BiTree Pop() {
return queue.numQueue[++queue.front];
}
//判断队列是否为空
int empty() {
return queue.rear == queue.front;
}
4 求二叉树的最大深度
一棵树的最大深度,左子树和右子树的最大深度 + 1 即可.
- 采用递归的方式来实现:
//二叉树的最大深度
int maxDepth(BiTree root) {
if (root) {
int maxLeft = maxDepth(root->lchild);
int maxRight = maxDepth(root->rchild);
if (maxLeft > maxRight) {
return maxLeft + 1;
} else {
return maxRight + 1;
}
}
return 0;
}
5 求二叉树的高度
- 采用递归的方式来实现
//二叉树高度
int BiTreeHeight(BiTree root) {
if (root) {
int leftHeight = BiTreeHeight(root->lchild);
int rightHeight = BiTreeHeight(root->rchild);
return (leftHeight > rightHeight) ? (leftHeight + 1) : (rightHeight + 1);
}
return 0;
}
6 求二叉树叶子节点的个数
一个节点的度就是一个节点的分支数,二叉树中的节点按照度来分类的话,分为三类,度分别为 0、1、2 的节点,我们将其数量表示为 n0、n1、n2,且我们将一棵树的总结点数量用 N 来表示。那么一个数的叶子节点的数量即为 n0,且有 N = n0 + n1 + n2。
如果我们按照一棵树的子节点数来计算一棵树的总结点数,那么一棵二叉树树的总结点数 N = 2 * n2 + n1 + 1,最后一个 1 表示树的根节点。我们将关于 N 的两个等式合并,则有结论:n0 = n2 + 1。
- 采用递归的方式来实现
//叶子节点
int LeafNodeNum(BiTree root) {
if (root == NULL) {
return 0;
}
if (root->lchild == NULL && root->rchild == NULL) {
return 1;
} else {
return LeafNodeNum(root->lchild) + LeafNodeNum(root->rchild);
}
}
7 求第 k 层节点的个数
- 采用递归的方式来实现:
//求第k层节点个数
int LevelNodeNum(BiTree root, int k) {
if (root == NULL || k < 1) {
return 0;
}
if (k == 1) {
return 1;
}
return LevelNodeNum(root->lchild, k - 1) + LevelNodeNum(root->rchild, k - 1);
}
8 求二叉树总节点个数
- 采用递归的方式来实现:
//求二叉树总节点个数
int CountNode(BiTree root) {
if (root) {
if ((root->lchild == NULL) && (root->rchild == NULL)) {
return 1;
} else {
return CountNode(root->lchild) + CountNode(root->rchild) + 1;
}
}
return 0;
}
9 查找元素为 x 的节点
- 采用递归的方式来实现:
//查找元素为 x 的节点
BiTree SearchNode(BiTree root, int x) {
if (root) {
if (root->data == x) {
return root;
} else {
BiTree p;
p = SearchNode(root->lchild, x);
if (!p) {
p = SearchNode(root->rchild, x);
}
return p;
}
}
return NULL;
}
10 二叉树的操作完整代码
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
//树的结构
typedef struct TreeNode {
int data;//数据域
struct TreeNode* lchild;//指向左孩子节点
struct TreeNode* rchild;//指向右孩子节点
} BiNode, *BiTree;
//队列结构体
typedef struct queue {
struct TreeNode* numQueue[MaxSize];
int front;
int rear;
} Queue;
Queue queue;//声明全局变量
//初始化队列
void initQueue() {
queue.front = 0;
queue.rear = 0;
}
//入队
void Push(BiTree root) {
queue.numQueue[++queue.rear] = root;
}
//出队
BiTree Pop() {
return queue.numQueue[++queue.front];
}
//判断队列是否为空
int empty() {
return queue.rear == queue.front;
}
//构造二叉树
BiTree CreateTree() {
int data;
scanf("%d", &data);//根节点数据
BiTree root;
if (data <= 0) {
return NULL;
} else {
root = (BiTree)malloc(sizeof(BiNode));
root->data = data;
//printf("请输入%d的左子树:", root->data);
root->lchild = CreateTree();
//printf("请输入%d的右子树:", root->data);
root->rchild = CreateTree();
}
return root;
}
//先序遍历二叉树:递归实现
void PreOrderTraverse(BiTree root) {
if (root) {
printf("%d ", root->data);
PreOrderTraverse(root->lchild);
PreOrderTraverse(root->rchild);
}
}
//先序遍历二叉树:非递归实现
void PreOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
//根节点入栈
top++;
stack[top] = root;
//栈不空时循环
while (top > -1) {
//出栈并访问该节点
p = stack[top];
top--;
printf("%d ", p->data);
//右孩子入栈
if (p->rchild != NULL) {
top++;
stack[top] = p->rchild;
}
//左孩子入栈
if (p->lchild != NULL) {
top++;
stack[top] = p->lchild;
}
}
}
}
//中序遍历二叉树:递归实现
void InOrderTraverse(BiTree root) {
if (root) {
InOrderTraverse(root->lchild);
printf("%d ", root->data);
InOrderTraverse(root->rchild);
}
}
//中序遍历二叉树:非递归实现
void InOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
p = root;
while (top > -1 || p != NULL) {
//扫描p的所有左节点并入栈
while (p != NULL) {
top++;
stack[top] = p;
p = p->lchild;
}
if (top > -1) {
//出栈并访问节点
p = stack[top];
top--;
printf("%d ", p->data);
//扫描右孩子
p = p->rchild;
}
}
}
}
//后序遍历二叉树:递归实现
void PostOrderTraverse(BiTree root) {
if (root) {
PostOrderTraverse(root->lchild);
PostOrderTraverse(root->rchild);
printf("%d ", root->data);
}
}
//后序遍历二叉树:非递归实现
void PostOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
int sign;
if (root != NULL) {
do {
//root节点入栈
while (root != NULL) {
top++;
stack[top] = root;
root = root->lchild;
}
//p指向栈顶前一个已访问节点
p = NULL;
//置root为已访问
sign = 1;
while (top != -1 && sign) {
//取出栈顶节点
root = stack[top];
//右孩子不存在或右孩子已访问则访问root
if (root->rchild == p) {
printf("%d ", root->data);
top--;
//p指向被访问的节点
p = root;
} else {
//root指向右孩子节点
root = root->rchild;
//置未访问标记
sign = 0;
}
}
} while (top != -1);
}
}
//层次遍历:非递归实现
void LevelOrderTraverseNonRec(BiTree root) {
BiTree p;
Push(root);
while (!empty()) {//empty()判断队列是否为空
p = Pop();//出队
printf("%d ", p->data);//输出队首结点
if (p->lchild) {//把Pop掉的结点的左子结点加入队列
Push(p->lchild);
}
if (p->rchild) {//把Pop掉的结点的右子结点加入队列
Push(p->rchild);
}
}
}
//二叉树的最大深度
int maxDepth(BiTree root) {
if (root) {
int maxLeft = maxDepth(root->lchild);
int maxRight = maxDepth(root->rchild);
if (maxLeft > maxRight) {
return maxLeft + 1;
} else {
return maxRight + 1;
}
}
return 0;
}
//二叉树高度
int BiTreeHeight(BiTree root) {
if (root) {
int leftHeight = BiTreeHeight(root->lchild);
int rightHeight = BiTreeHeight(root->rchild);
return (leftHeight > rightHeight) ? (leftHeight + 1) : (rightHeight + 1);
}
return 0;
}
//叶子节点
int LeafNodeNum(BiTree root) {
if (root == NULL) {
return 0;
}
if (root->lchild == NULL && root->rchild == NULL) {
return 1;
} else {
return LeafNodeNum(root->lchild) + LeafNodeNum(root->rchild);
}
}
//求第k层节点个数
int LevelNodeNum(BiTree root, int k) {
if (root == NULL || k < 1) {
return 0;
}
if (k == 1) {
return 1;
}
return LevelNodeNum(root->lchild, k - 1) + LevelNodeNum(root->rchild, k - 1);
}
//求二叉树总节点个数
int CountNode(BiTree root) {
if (root) {
if ((root->lchild == NULL) && (root->rchild == NULL)) {
return 1;
} else {
return CountNode(root->lchild) + CountNode(root->rchild) + 1;
}
}
return 0;
}
//查找元素为 x 的节点
BiTree SearchNode(BiTree root, int x) {
if (root) {
if (root->data == x) {
return root;
} else {
BiTree p;
p = SearchNode(root->lchild, x);
if (!p) {
p = SearchNode(root->rchild, x);
}
return p;
}
}
return NULL;
}
//测试
int main() {
//测试数据:5 3 2 0 0 0 8 6 0 0 9 0 0
//BiTree root;
//CreateTree(&root);
BiTree root = NULL;
root = CreateTree();//创建树
printf("先序非递归遍历:");
PreOrderTraverseNonRec(root);
printf("\n中序非递归遍历:");
InOrderTraverseNonRec(root);
printf("\n后序非递归遍历:");
PostOrderTraverseNonRec(root);
printf("\n先序递归遍历:");
PreOrderTraverse(root);//先序遍历输出
printf("\n中序递归遍历:");
InOrderTraverse(root);//中序遍历输出
printf("\n后序递归遍历:");
PostOrderTraverse(root);//中序遍历输出
printf("\n层次非递归遍历:");
LevelOrderTraverseNonRec(root);//层次遍历输出
printf("\n二叉树的深度为:%d",maxDepth(root));
printf("\n二叉树的高度为:%d",BiTreeHeight(root));
printf("\n叶子节点为:%d",LeafNodeNum(root));
printf("\n总节点为:%d", CountNode(root));
printf("\n第3层节点个数为:%d",LevelNodeNum(root, 3));
BiTree q;
q = SearchNode(root, 9);
if (q) {
printf("\n查找到了 :%d", q->data);
} else {
printf("\n没有查找到 9 ");
}
return 0;
}
二叉树的基本操作(C语言版)的更多相关文章
- 求二叉树的宽度C语言版
/*层次遍历二叉树,每一层遍历完成以后都重新插入特定的指针 (比如本例使用的特殊指针是数据元素为#,左右儿子为空的指针), 这样在每次访问到所指向数据为#的队列中的结点指针是就知道该指针是这层的末尾, ...
- C语言实现二叉树的基本操作
二叉树是一种非常重要的数据结构.本文总结了二叉树的常见操作:二叉树的构建,查找,删除,二叉树的遍历(包括前序遍历.中序遍历.后序遍历.层次遍历),二叉搜索树的构造等. 1. 二叉树的构建 二叉树的基本 ...
- 实现二叉树的基本操作(Java版)
近期研究了一下二叉树,试着用Java语言实现了二叉树的基本操作,下面分享一下实现代码: package com.sf.test; import java.util.ArrayDeque; import ...
- c语言描述的二叉树的基本操作(层序遍历,递归,非递归遍历)
#include<stdio.h> #include<stdlib.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define ...
- 数据结构(C语言版)---二叉树
1.二叉树:任意一个结点的子结点个数最多两个,且子结点的位置不可更改,二叉树的子树有左右之分. 1)分类:(1)一般二叉树(2)满二叉树:在不增加树的层数的前提下,无法再多添加一个结点的二叉树就是满二 ...
- 顺序栈的基本操作(C语言)
由于现在只学了C语言所以就写这个C语言版的栈的基本操作 这里说一下 :网上和书上都有这种写法 int InitStack(SqStack &p) &p是取地址 但是这种用法好像C并不 ...
- 数据结构(c语言版)代码
第1章 绪论 文档中源码及测试数据存放目录:数据结构\▲课本算法实现\▲01 绪论 概述 第一章作为绪论,主要介绍了数据结构与算法中的一些基本概念和术语.对于这些概念术语 ...
- 插曲一--记《数据结构与问题求解(Java语言版)(第4版)》翻译问题
在该书的527页中18.6理论题中,书中这样写道"完全结点是指每个结点都有两个孩子.证明,完全二叉树的结点数加1等于叶子树." 初看此题目,本人觉得很纳闷,再细细想之,发现似乎是个 ...
- c++学习书籍推荐《清华大学计算机系列教材:数据结构(C++语言版)(第3版)》下载
百度云及其他网盘下载地址:点我 编辑推荐 <清华大学计算机系列教材:数据结构(C++语言版)(第3版)>习题解析涵盖验证型.拓展型.反思型.实践型和研究型习题,总计290余道大题.525道 ...
随机推荐
- Java 单引号 与 双引号 区别
双引号,用来引用字符串, 单引号用来表示单个字符.
- JVM调优-1
JVM运行参数 在jvm中有很多的参数可以进行设置,这样可以让jvm在各种环境中都能够高效的运行.绝大部分的参数保持默认即可. 三种参数类型 标准参数 -help -version -X参数(非标准参 ...
- 【Java】枚举类
文章目录 枚举类的使用 如何定义枚举类 方式一:jdk5.0之前,自定义枚举类 方式二:jdk5.0,可以使用enum关键字定义枚举类 Enum类的主要方法 toString() values() v ...
- Python函数与lambda 表达式(匿名函数)
Python函数 一.函数的作用 函数是组织好的,可重复使用的,用来实现单一或相关联功能的代码段 函数能提高应用的模块性和代码的重复利用率 python 内置函数:https://docs.pytho ...
- manjaro20安装teamviewer出现sudo teamviewer –daemon start无响应
问题 https://www.randomhacks.co.uk/the-teamviewer-daemon-is-not-running-please-start-the-daemon-ubuntu ...
- 云互联(http://www.yunone.com/)淘宝店铺名[格子窝]垃圾皮包骗子公司分析
首先先给大家说明下我写这篇文章的初衷,本来也不想费神写这个的,可是忍无可忍,无需再忍,别人不犯贱,咱们何必跟人家较真呢? 在做渭南电脑维修网的同时,遇到了很多问题,使我受益匪浅,尤其是SEO方面的提升 ...
- List子接口
简介 特点: 有序, 有下标, 元素可以重复 常用方法 boolean add(E e) 将指定的元素追加到此列表的末尾(可选操作). void add(int index, E element) 将 ...
- gin框架中多种数据格式返回请求结果
返回四种格式的数据:1. []byte.string 2. json格式 3. html模板渲染 4. 静态资源设置 package main import ( "github.com ...
- 学习JAVAWEB第十一天
今天以及明天做登录案例,复习所学知识.
- springboot 配置mybatis 配置mapper.xml
# 插件 进行配置 也可以用yml # 1. 配置 Tomcat 修改端口号 server.port=8848 server.context-path=/zxf #2.配置数据源 spring.dat ...