数据结构 – 二叉树
一、定义
说明:
- 是一种特殊的树,每个节点最多有两个子节点(左节点、右节点)。
- 有序二叉树(最核心的二叉树),需要掌握。
下图为有序二叉树,从根节点开始,大小左右排列:
二、三种遍历方式
-
先序遍历:
处理节点自己的数据 -> 处理左节点 -> 处理右节点 -
中序遍历(重点掌握):
处理左节点 -> 处理节点自己的数据 -> 处理右节点 -
后序遍历:
处理左节点 -> 处理右节点 -> 处理自己的节点
三、数据结构
- 定义 Tree 结构,由两部分组成:
定义描述节点的数据结构:
typedef struct node{
int data;//数据
struct node *left;//左子树
struct node *right;//右子树
}node_t;
声明整棵树的数据结构:
typedef struct tree{
node_t *root;//根节点地址
int cnt;//节点个数
}tree_t;
三、功能函数
- create_node:创建新的节点,malloc 动态创建:
static node_t *create_node(int data)
{
//分配内存
node_t *pnode=(node_t *)malloc(sizeof(node_t));
//初始化节点
pnode->data=data;
pnode->left=NULL;
pnode->right=NULL;
return pnode;
}
- insert_data:向根节点 Tree 中插入新节点 pnode:
定义子函数:proot为根节点,pnode为新节点;
/* 请查看二级指针定义,否则是内存空间上的复制 */
/* 需要针对tree进行操作 */
/* 过程中使用迭代操作,请留意 */
void insert(node_t **proot,node_t *pnode)
{
//递归函数的退出条件:根节点为空(root,L,R)
if(NULL==*proot){ //当树只有根节点时
*proot=pnode;
return ;
}
//插入到左子树中
if((*proot)->data > pnode->data){
insert(&(*proot)->left,pnode);
return;
}else { //插入到到右子树
insert(&(*proot)->right,pnode);
return;
}
}
向有序二叉树创建新的节点;
void insert_data(tree_t *tree,int data)
{
//创建新的节点
node_t *pnode=create_node(data);
//调用递归函数进行插入,注意递归法则
insert(&tree->root,pnode);
//更新个数
tree->cnt++;
}
- travel_data:中序遍历
定义子函数 : travel
void travel(node_t *proot)
{
//递归函数的结束判断
if(proot != NULL){
/*中序遍历*/
travel(proot->left);//左节点
printf("%d",proot->data);//处理节点自己
travel(proot->right); //右节点
}
定义调用函数:
void travel_data(tree_t *tree)
{
//调用递归函数遍历
travel(tree->root);
printf("\n");
}
注意 ---- 该遍历过程中需要重点理解递归的流程:
- clear_data:清空二叉树
定义子函数:清除节点的递归函数
void clear(node_t **proot)
{
if(*proot != NULL)
{
//清空左子树
clear(&(*proot)->left);
//清空右子树
clear(&(*proot)->right);
//释放节点内存
free(*proot);
*proot=NULL;
}
}
定义调用函数:
void clear_data(tree_t *tree)
{
clear(&tree->root);
tree->cnt=0;
}
注意----该遍历过程中需要重点理解递归的流程:
- del_data:删除某个节点
定义 find 子函数:在根节点下查找数据
说明:寻找方法同样使用递归,递推查找。
node_t **find(node_t **pproot,int data)
{
//1. 如果树为空,直接返回
if(NULL == *pproot){
return pproot;
}
//2. 比较节点和目标值,若相等返回节点
if(data == (*pproot)->data){
return pproot;
}
//3. 如果目标值小于节点的值,左子树找寻
if(data < (*pproot->data)){
return find(&(*pproot)->left,data);
}
else {
return find(&(*pproot)->right,data); //右子树找
}
}
定义 find_data 调用函数:
说明: 传递tree树节点中root的值,root为树节点指向下一节点的节点地址,使用 & 和前面类似,避免内存空间的复制,对root的改变会做用到 本tree;
注意:返回值为 ** 二级指针,表明找到data后,data这个节点就是新的树节点。
// 传递整个树
node_t **find_data(tree_t *tree,int data)
{
//调用递归函数从root开始查找要删除的节点并返回这个节点的地址
return find(&tree->root,data); //传递树的首节点
}
定义 del_data 删除节点函数:
删除节点的第四点好好体会,其实就是做暂存,然后free掉;
void del_data(tree_t *tree,int data)
{
//1. 首先要找到删除的节点,返回这个节点的首地址
node_t **ppnode=find_data(tree,data);
if (NULL == *ppnode){
printf("要寻找的目标节点不存在");
return;
}
//2. 如果找到要删除的节点,将节点的左子树插入到右子树
if((*ppnode)->left != NULL){
insert(&(*ppnode)->right,(*ppnode)->left);
}
//3. 将要删除节点的右子树给到要删除节点的父节点的左子树
node_t *ptemp = *ppnode; //临时暂存
*ppnode=(*ppnode)->right;
//4. 释放内存
free(*ptemp);
*ptemp = NULL; //好习惯
//5. 更新计数
tree->cnt--;
}
- modify_data:修改节点数据;
/*修改元素*/
void modify_data(tree_t *tree,int old_data,int new_data)
{
//删除旧结点
del_data(tree,old_data);
//插入新的节点
insert_data(tree,new_data);
}
- main 函数:功能函数写好之后咱们看看调用;
int main(void)
{
//1. 种树
tree_t tree;
//2. 初始化=NULL
tree.root=NULL
tree.cnt=0
//3. 添加新节点
insert_data(&tree,50);
//采用中序遍历方式
travel_data(&tree);
//删除40
del_data(&tree,40);
//将10修改成250
modify_data(&tree,10,250);
return 0;
}
总结
数据结构形式不在多,在于深入理解他在做什么,好好理解树tree