C语言提高篇-数据结构~二叉树
1、 基本概念
栈/队列/链表 - 属于逻辑结构中的线性结构
二叉树 - 属于逻辑结构中的树形结构
在计算机主中,二叉树就是指每个节点最多有两个子节点的树形结构,叫做二叉树
其中最起始的节点叫做根结点,整棵树只有一个根结点,除根结点之外,每个节点有且只有一个父节点
其中没有任何子节点的节点叫做叶子节点,叶子节点有父节点没有子节点
除了根结点和叶子节点之外,剩下的节点叫做枝节点,枝节点有父节点也有子节点
如果该二叉树中每层节点个数均达到最大值,并且每个枝节点都有两个子节点,这样的二叉树叫做满二叉树
如果该二叉树中除了最下面的一层之外,其他各层的节点树都达到了最大值,并且最下面一层的节点都连续集中在左侧,这样的二叉树叫做完全二叉树
2、基本特征
二叉树具有递归嵌套式的空间结构,因此一般采用递归的方法处理二叉树比较简单,处理方式如下:
处理(二叉树)
{
if(如果二叉树为空) 直接处理;
else
{
处理左子树; => 使用递归处理
处理根结点;
处理右子树; => 使用递归处理
}
}
3、二叉树存储结构
(1)采用顺序结构进行存储
一般来说,从上到下,从左到右依次进行存储
对于非完全二叉树来说,需要采用虚节点占位
(2)采用链式结构进行存储
一般来说,每个节点的内部有3个成员:一个用于存放数据元素本身;一个记录左子节点的地址;一个用于记录右子节点的地址,节点的数据类型如下:
typedef struct Node
{
int data;//记录数据元素本身
struct Node* left;//记录左子节点的地址
struct Node* right;//记录右子节点的地址
}Node;
4、 二叉树的基本操作
创建、增加节点、遍历、删除节点、查找节点、修改指定的节点、判空判满、销毁、计算节点的个数、以及获取根结点的元素值
5、二叉树的遍历方法
(1)先序遍历(DLR -> Data Left Right)
先遍历根结点 => 左子树 => 右子树
又叫做先根遍历
(2)中序遍历(LDR -> Left Data Right 重点)
先遍历左子树 => 根结点 => 右子树
又叫做中根遍历
(3)后序遍历(LRD -> Left Right Data)
先遍历左子树 => 右子树 => 根结点
又叫做后根遍历
6、有序二叉树
满足以下三个条件的非空二叉树,叫做有序二叉树
(1)如果左子树非空,则左子树中所有节点的值都小于等于根结点的元素值
(2)如果右子树非空,则右子树中所有节点的值都大于等于根结点的元素值
(3)左右子树也分别为有序二叉树
7、示例代码
//编程实现有序二叉树的各种操作
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//定义节点的数据类型
typedef struct Node
{
int data;//记录数据元素本身
struct Node* left;//记录左子节点的地址
struct Node* right;//记录右子节点的地址
}Node;
//定义有序二叉树的数据类型
typedef struct
{
Node* root;//记录根结点的地址
int cnt;//记录节点的个数
}Tree;
//实现插入一个新元素
void insert_data(Tree* pt,int data);
//实现插入的递归函数
void insert(Node** pRoot,Node* pn);
//实现遍历二叉树中所有元素
void travel_data(Tree* pt);
//中序遍历的递归函数
void travel(Node* pn);
//清空二叉树中所有的节点
void clear_data(Tree* pt);
//清空的递归函数
void clear(Node** pRoot);
int main(void)
{
//有序二叉树的创建和初始化
Tree tree;
tree.root = NULL;
tree.cnt = 0;
insert_data(&tree,50);
travel_data(&tree);//50
insert_data(&tree,70);
travel_data(&tree);//50 70
insert_data(&tree,20);
travel_data(&tree);//20 50 70
insert_data(&tree,60);
travel_data(&tree);//20 50 60 70
printf("----------------------\n");
clear_data(&tree);
travel_data(&tree); //啥也没有
return 0;
}
//清空的递归函数
void clear(Node** pRoot)
{
//二叉树不为空才需要清空
if(*pRoot != NULL)
{
//1.清空左子树,使用递归
clear(&(*pRoot)->left);
//2.清空右子树,使用递归
clear(&(*pRoot)->right);
//3.清空根结点
free(*pRoot);
*pRoot = NULL;
}
}
//清空二叉树中所有的节点
void clear_data(Tree* pt)
{
//调用递归函数实现清空
clear(&pt->root);
//节点的个数为0
pt->cnt = 0;
}
//中序遍历的递归函数
void travel(Node* pn)
{
//当有序二叉树中有节点时需要遍历
if(pn != NULL)
{
//1.遍历左子树,使用递归
travel(pn->left);
//2.遍历根结点
printf("%d ",pn->data);
//3.遍历右子树,使用递归
travel(pn->right);
}
}
//实现遍历二叉树中所有元素
void travel_data(Tree* pt)
{
printf("有序二叉树中的元素有:");
//采用中序遍历的方法,调用递归函数
travel(pt->root);
printf("\n");
}
//实现插入的递归函数
void insert(Node** pRoot,Node* pn)
{
//Node** pRoot = &pt->root;
// Node** pRoot; pRoot = &pt->root;
// *pRoot = *(&pt->root) = pt->root;
//1.如果二叉树为空,则直接插入
if(NULL == *pRoot)
{
//根指针指向新结点
*pRoot = pn;
return;
}
//2.如果二叉树不为空,并且新结点元素值小于根结点元素值,则把新结点插入左子树
else if(pn->data < (*pRoot)->data)
{
insert(&(*pRoot)->left,pn);
}
//3.如果新结点元素值大于等于根结点元素值,则把新结点插入右子树中
else
{
insert(&(*pRoot)->right,pn);
}
}
//实现插入一个新元素
void insert_data(Tree* pt,int data)
{
//1.创建新结点和初始化
Node* pn = (Node*)malloc(sizeof(Node));
if(NULL == pn)
{
printf("创建新结点失败\n");
return;
}
pn->data = data;
pn->left = NULL;
pn->right = NULL;
//2.寻找合适的位置进行插入,递归函数
insert(&pt->root,pn);//址传递
//3.节点元素的个数 +1
++pt->cnt;
}