1.回顾
1.数据结构
栈
先进后出/后进先出
只操作栈顶
队列
先进先出FIFO
消息队列/等待队列工作队列
入队操作队尾
出队操作队首
单链表
struct node {
数据;
struct node *next;
};
struct list {
struct node head/struct node *head;
struct node tail/struct node *tail;
};
双链表
struct node {
数据;
struct node *next, *prev;
};
//描述单或者双链表的结构体
struct list {
数据;
struct node head/struct node *head;
struct node tail/struct node *tail;
};
2. 掌握Makefile实现过程
3.递归函数(recursive)
特点:自己调用自己,重复相同的事情
void print(void) {
printf("1\n");
print();
printf(‘2\n);
}
int main(void) {
print();
return 0;
}
注意:递归函数要有退出的条件
4.二叉树
4.1 二叉树是一种特殊的树
具有一对多的特点
4.2 二叉树的特点
-
每个节点最多有两个子节点(可以没有,可以有一个)
-
单根性,每个子节点有且只有一个父节点,整颗树只有一个根节点
左子树:根节点左边的子树
右子树:根节点右边的子树
-
二叉树一般用递归函数处理
4.3 有序二叉树(核心)
-
定义:一般来说。当左子树不为空时,左子树的元素值小于根节点
当右子树不为空时,右子树的元素值大于根节点
-
例如:现在有这么一组树(类似礼物),要求按照有序二叉树的特点将这些数挂接到树上
具体参见:有序二叉树.png
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-
-
三种遍历方式:
先序遍历:处理节点自己的数据->处理左节点->处理右节点
中序遍历:处理左节点->处理自己->处理右节点
后序遍历:处理左节点->处理右节点->处理自己
-
有序二叉树应用:
用于搜索和查找数据极其方便(又称二叉查找树)
4.4 有序二叉树结构体
声明描述有序二叉树每个节点的结构体
struct node {
数据;
struct node *left; //保存左节点地址
struct node *right;//保存右节点地址
};
描述树的结构体
struct tree {
struct node *root;//记录数的根节点
int cnt; //记录数的节点个数
};
注意:这里的数不仅是整颗树,也可以说是整颗树的某颗子树
参考代码:文件夹tree
/*************************************************************************
> File Name: tree.h
> Author: 菜小菜
> Mail: 317280271@qq.com
> Created Time: 2021年05月07日 星期五 11时02分47秒
************************************************************************/
/*有序二叉树演示*/
#ifndef __TREE_H
#define __TREE_H
#include <stdio.h>
#include <stdlib.h>
//声明描述节点信息的结构体
typedef struct node {
int data;
struct node *left; // 左节点地址
struct node *right; //右节点地址
} node_t;
//声明描述树信息的结构体
typedef struct tree
{
node_t *root; //根节点
int cnt; //节点个数
} tree_t;
//声明操作函数
extern void tree_travel(tree_t *tree); // 遍历
extern void tree_insert(tree_t *tree, int data); //插入
extern void tree_del(tree_t *tree, int data); // 删除data所在节点
extern void tree_clear(tree_t *tree); //清空
extern void tree_modity(tree_t *tree, int old_data, int new_data); //修改节点数据
#endif
/*有序二叉树演示*/
#include "tree.h"
//定义创建节点和初始化节点的函数
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;
}
/*
tree_insert(NULL, 50)
root = 50;
return
tree_insert(50, 20)
insert(50的L, 20)
50的L=20
tree_insert(50, 70)MarkdownEditing
insert(50的R, 70)
50的R=70
tree_insert(50, 10)
insert(50的L=20, 10)
insert(20的L, 10)
*/
/**插入新节点函数*/
static void insert(node_t **proot, node_t *pnode)
{
//1.递归函数结束的条件:根节点(L,R)为空
if(*proot == NULL) {
*proot = pnode;
return;
}
//2.插入到左子树
if((*proot)->data > pnode->data) {
insert(&(*proot)->left, pnode);
return;
} else {
insert(&(*proot)->right, pnode);
return;
}
}
/*定义向有序二叉树插入新节点的函数*/
void tree_insert(tree_t *tree, int data)
{
//1.创建新节点
node_t *pnode = create_node(data);
//2.调用递归函数将新节点链接到树上
insert(&tree->root, pnode);
//3.更新计数
tree->cnt++;
}
/*定义遍历递归函数*/
static void travel(node_t *proot)
{
if(proot != NULL) {
//中序遍历
travel(proot->left); //处理左节点
printf("%d ", proot->data); //处理自己
travel(proot->right); //处理右节点
/*先序遍历
printf("%d ", proot->data); //处理自己
travel(proot->left); //处理左节点
travel(proot->right); //处理右节点
*/
/*后序遍历
travel(proot->left); //处理左节点
travel(proot->right); //处理右节点
printf("%d ", proot->data); //处理自己
*/
return;
}
return;
}
/*定义遍历函数*/
void tree_travel(tree_t *tree)
{
//调用递归函数
travel(tree->root);
printf("\n");
}
/*
流程:以20,70,20为例
tree_clear()
clear(50)
clear(50左边, 20)
clear(20左边, NULL)
clear(20右边, NULL)
free(20)
return
clear(50右边, 70)
clear(70左边, NULL)
clear(70右边, NULL)
free(70)
return
free(50)
return返回到tree_clear函数
*/
/*定义清除节点的递归函数*/
static void clear(node_t **proot)
{
if(*proot != NULL) {
//清空左子树
clear(&(*proot)->left);
//清空右子树
clear(&(*proot)->right);
//释放节点内存
free(*proot);
*proot = NULL;
return;
}
}
/*定义清空树*/
void tree_clear(tree_t *tree)
{
clear(&tree->root);
tree->cnt = 0;
}
/*
递归顺序:
find_node(50, 5)
find(50, 5)
find(20, 5)
find(10, 5)
find(NULL, 5)
return NULL
return NULL //没找到
find_node(50, 10)
find(50, 10)
find(20, 10)
find(10, 10)
return 10节点的二级指针
return 10的二级指针
find_node(50, 80)
find(50, 80)
find(70, 80)
find(90, 80)
find(80, 80)
return 80节点的二级指针
return 80的二级指针
*/
/*查找节点的递归函数*/
static node_t **find(node_t **pproot, int data)
{
//1.如果为空,停止查找
if(*pproot == NULL)
return pproot; // 返回NULL
//2.比较节点的数字和目标数字,如果相同返回这个节点
if((*pproot)->data == data)
return pproot;
else if(data < (*pproot)->data) {
//3.如果目标值小于节点值,在左节点查找
return find(&(*pproot)->left, data);
}
else{
//4.否则右子树查找
return find(&(*pproot)->right, data);
}
}
/*定义查找要删除节点的函数*/
static node_t **find_node(tree_t *tree, int data)
{
//调用递归函数find从root开始找要删除的节点并且返回这个节点地址的二级指针
return find(&tree->root, data);
}
/*定义删除指定数据所在的节点*/
void tree_del(tree_t *tree, int data)
{
//1.首先找到要删除的节点,返回这个节点的二级指针
node_t **ppnode = find_node(tree, data);
if(NULL == *ppnode) {
printf("要找的节点不存在\n");
return;
}
//2.将要删除节点的左子数合并到右子树
if((*ppnode)->left != NULL) {
insert(&(*ppnode)->right, (*ppnode)->left);
}
//3.将要删除节点的右子树给要删除节点的父子树的左子树
node_t *ptemp = *ppnode; //临时暂存要删除的节点
*ppnode = (*ppnode)->right; //将右子树向上提升一级
//4.释放内存
free(ptemp);
//5.更新计数
tree->cnt--;
}
/*修改数据*/
void tree_modity(tree_t *tree, int old_data, int new_data)
{
//1.先删除
tree_del(tree, old_data);
//2.再插入
tree_insert(tree, new_data);
}
/*************************************************************************
> File Name: main.c
> Author: 菜小菜
> Mail: 317280271@qq.com
> Created Time: 2021年05月07日 星期五 16时04分04秒
************************************************************************/
#include "tree.h"
int main(void)
{
//1.创建一个树
tree_t tree;
//2.初始化树
tree.root = NULL;
tree.cnt = 0;
//3.插入新节点
tree_insert(&tree, 50);
tree_insert(&tree, 20);
tree_insert(&tree, 70);
tree_insert(&tree, 10);
tree_insert(&tree, 30);
tree_insert(&tree, 40);
tree_insert(&tree, 90);
tree_insert(&tree, 60);
tree_insert(&tree, 80);
//4.遍历
tree_travel(&tree);
tree_del(&tree, 40);
tree_del(&tree, 40);
tree_travel(&tree);
//5.清空数
tree_clear(&tree);
tree_travel(&tree);
return 0;
}
注意:删除二叉树节点三步骤
- 找到要删除的节点
- 将要删除的节点 的左子树放到要删除节点的右子树上
- 将要删除的节点的父节点的左子树的指针指向要删除的节点的右子树