Day19、双向链表、树(二叉树、遍历、删除结点)
来源:互联网 发布:php array判断 编辑:程序博客网 时间:2024/03/29 13:25
双向链表
1. p_next + p_prev 不仅可以指向下一个数,也可以指向前一个数
2. 因此,在需要对尾节点进行操作(增加、删除、获得)时,不需要从头进行遍历,从尾部就可以开始,此外,改写函数初始化、增加数字,使其能够灵活地指向前后的数字。
3. 在源文件增加函数,功能遍历打印,因此需要增加指针:p_link->p_cur
4. 在主函数(测试)中,增加从后向前遍历的功能(r_begin)
编写源文件:01link.c
/*
链表演示
*/
#include <stdlib.h>
#include "01link.h"
//初始化函数
void link_init(link *p_link) {
p_link->head.p_next = &(p_link->tail);
p_link->tail.p_next = NULL;
p_link->tail.p_prev = &(p_link->head);
p_link->head.p_prev = NULL;
p_link->p_cur = NULL;
}
//清理函数
void link_deinit(link *p_link) {
while (p_link->head.p_next != &(p_link->tail)) {
node *p_first = &(p_link->head);
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
}
p_link->p_cur = NULL;
}
//获得有效数字个数的函数
int link_size(const link *p_link) {
int cnt = 0;
const node *p_node = NULL;
for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {
const node *p_first = p_node;
const node *p_mid = p_first->p_next;
const node *p_last = p_mid->p_next;
if (p_mid != &(p_link->tail)) {
cnt++;
}
}
return cnt;
}
//在链表头加入数字
void link_add_head(link *p_link, int num) {
node *p_first = NULL, *p_mid = NULL, *p_last = NULL;
node *p_tmp = (node *)malloc(sizeof(node));
if (!p_tmp) {
return ;
}
p_tmp->num = num;
p_tmp->p_next = NULL;
p_tmp->p_prev = NULL;
p_first = &(p_link->head);
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_tmp;
p_tmp->p_prev = p_first;
p_tmp->p_next = p_mid;
p_mid->p_prev = p_tmp;
p_link->p_cur = NULL;
}
//在链表末尾追加数字
void link_append(link *p_link, int num) {
node *p_first = NULL, *p_mid = NULL, *p_last = NULL;
node *p_tmp = (node *)malloc(sizeof(node));
if (!p_tmp) {
return ;
}
p_tmp->num = num;
p_tmp->p_next = NULL;
p_tmp->p_prev = NULL;
p_first = p_link->tail.p_prev;
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_tmp;
p_tmp->p_prev = p_first;
p_tmp->p_next = p_mid;
p_mid->p_prev = p_tmp;
p_link->p_cur = NULL;
}
/*void link_append(link *p_link, int num) {
node *p_tmp = (node *)malloc(sizeof(node));
node *p_node = NULL;
if (!p_tmp) {
return ;
}
p_tmp->num = num;
p_tmp->p_next = NULL;
p_tmp->p_prev = NULL;
for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid == &(p_link->tail)) {
p_first->p_next = p_tmp;
p_tmp->p_prev = p_first;
p_tmp->p_next = p_mid;
p_mid->p_prev = p_tmp;
break;
}
}
p_link->p_cur = NULL;
}*/
//按顺序加入数字的函数
void link_insert_in_order(link *p_link, intnum) {
node *p_node = NULL;
node *p_tmp = (node *)malloc(sizeof(node));
if (!p_tmp) {
return ;
}
p_tmp->num = num;
p_tmp->p_next = NULL;
p_tmp->p_prev = NULL;
for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid == &(p_link->tail) || p_mid->num > num) {
p_first->p_next = p_tmp;
p_tmp->p_prev = p_first;
p_tmp->p_next = p_mid;
p_mid->p_prev = p_tmp;
break;
}
}
p_link->p_cur = NULL;
}
//删除最前边数字的函数
void link_remove_head(link *p_link) {
node *p_first = NULL, *p_mid = NULL, *p_last = NULL;
p_first = &(p_link->head);
p_mid = p_first->p_next;
p_last = p_mid->p_next;
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
p_link->p_cur = NULL;
}
//删除最后一个结点的函数
void link_remove_tail(link *p_link) {
if (p_link->head.p_next != &(p_link->tail)) {
node *p_first = p_link->tail.p_prev->p_prev;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
}
p_link->p_cur = NULL;
}
/*void link_remove_tail(link *p_link) {
node *p_node = NULL;
for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_last == &(p_link->tail)) {
p_first->p_next = p_last;
p_last->p_prev = p_first;
free(p_mid);
p_mid = NULL;
break;
}
}
p_link->p_cur = NULL;
}*/
//获得最前面数字的函数
int link_get_head(const link *p_link, int*p_num) {
if (p_link->head.p_next == &(p_link->tail)) {
return 0;
}
else {
*p_num = p_link->head.p_next->num;
return 1;
}
}
//获得最后一个数字的函数
int link_get_tail(const link *p_link, int*p_num) {
if (p_link->head.p_next == &(p_link->tail)) {
return 0;
}
else {
*p_num = p_link->tail.p_prev->num;
return 1;
}
}
/*int link_get_tail(const link *p_link, int*p_num) {
node *p_node = NULL;
for (p_node = &(p_link->head);p_node !=&(p_link->tail);p_node = p_node->p_next) {
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_last == &(p_link->tail)) {
*p_num = p_mid->num;
return 1;
}
}
return 0;
}*/
//开始从前向后遍历所有结点
void link_begin(link *p_link) {
p_link->p_cur = &(p_link->head);
}
//在遍历过程中获得下一个数字
int link_next(link *p_link, int *p_num) {
if (!(p_link->p_cur)) {
return 0;
}
p_link->p_cur = p_link->p_cur->p_next;
if (p_link->p_cur == &(p_link->tail)) {
p_link->p_cur = NULL;
return 0;
}
else {
*p_num = p_link->p_cur->num;
return 1;
}
}
//开始从后向前依次遍历链表中所有结点
void link_rbegin(link *p_link) {
p_link->p_cur = &(p_link->tail);
}
//在从后先前遍历过程中获得前一个数字
int link_prev(link *p_link, int *p_num) {
if (!(p_link->p_cur)) {
return 0;
}
p_link->p_cur = p_link->p_cur->p_prev;
if (p_link->p_cur == &(p_link->head)) {
return 0;
}
else {
*p_num = p_link->p_cur->num;
return 1;
}
}
改成头文件:
#ifndef __01LINK_H__
#define __01LINK_H__
typedef struct node {
int num;
struct node *p_next;
struct node *p_prev;
} node;
typedef struct {
node head, tail;
node *p_cur;
} link;
//初始化函数
void link_init(link *);
//清理函数
void link_deinit(link *);
//获得有效数字个数的函数
int link_size(const link *);
//在链表头加入数字
void link_add_head(link *, int );
//在链表末尾追加数字
void link_append(link *, int );
//按顺序加入数字的函数
void link_insert_in_order(link *, int );
//删除最前边数字的函数
void link_remove_head(link *);
//删除最后一个结点的函数
void link_remove_tail(link *);
//获得最前面数字的函数
int link_get_head(const link *, int *);
//获得最后一个数字的函数
int link_get_tail(const link *, int *);
//开始从前向后遍历所有结点
void link_begin(link *);
//在遍历中获得下一个结点的数字
int link_next(link *, int *);
//开始从后向前遍历所有结点
void link_rbegin(link *);
//获得前一个数字的函数
int link_prev(link *, int *);
#endif //__01LINK_H__
编写主函数:
#include <stdio.h>
#include "01link.h"
int main() {
int num = 0;
link lnk = {0};
link_init(&lnk);
link_add_head(&lnk, 10);
link_append(&lnk, 100);
link_insert_in_order(&lnk, 70);
link_insert_in_order(&lnk, 20);
link_insert_in_order(&lnk, 40);
link_insert_in_order(&lnk, 30);
link_insert_in_order(&lnk, 50);
printf("数字个数是%d\n", link_size(&lnk));
link_begin(&lnk);
while (1) {
if (link_next(&lnk, &num)) {
printf("%d ", num);
}
else {
break;
}
}
printf("\n");
link_remove_head(&lnk);
link_remove_tail(&lnk);
link_get_head(&lnk, &num);
printf("最前边的数字是%d\n", num);
link_get_tail(&lnk, &num);
printf("最后边的数字是%d\n", num);
printf("数字个数是%d\n", link_size(&lnk));
link_begin(&lnk);
while (1) {
if (link_next(&lnk, &num)) {
printf("%d ", num);
}
else {
break;
}
}
printf("\n");
link_rbegin(&lnk);
while (1) {
if (link_prev(&lnk, &num)) {
printf("%d ", num);
}
else {
break;
}
}
printf("\n");
link_deinit(&lnk);
return 0;
}
树
如果单向线性链式物理结构中每个节点可以找到多个其它结点,就成了树
树里的所有结点可以分为几层,不同层之间符合线性规律(任意两层之间有前后顺序)
数的最上面一层应该只有一个结点,这个结点叫做树的根结点
如果树里两个结点之间有直接联系,这种联系叫做父子关系。其中靠近根结点的叫做父结点,另外一个叫子结点。
任何结点最多有一个父结点(根结点没有父结点)
如果任何一个结点最多只有两个子结点,则这种树叫做二叉树
二叉树是最简单的树
二叉树中某个结点的两个子结点分别叫做它的左子结点和右子结点
二叉树里任何结点都可以作为根结点代表一棵二叉树
一个结点的左子结点代表的树叫做这个结点的左子树,右子结点代表的树叫做它的右子树
树的操作大多数都是通过遍历实现的
通常采用递归函数实现树的遍历
(一棵树分为三部分:根、左子树、右子树,对左子树和对右子树的处理也是对树的处理,所以要使用递归)
遍历时左子树一定在右子树前面处理
前序遍历:若树为空,则空操作返回。先访问根结点,然后前序遍历左子树,再前序遍历右子树
中序遍历:若树为空,则空操作返回。首先中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树
后序遍历:若树为空,则空操作返回。从左到右先叶子后结点的方式遍历访问左右字树,最后访问根结点
有序二叉树:任何结点上左子树的值都比它本身小,右子树比本身数字大
练习:对树排序:
50
25 80
13 37 61 93
7
编写源文件2.c:
#include<stdlib.h>
#include"2.h"
//初始化函数
void tree_init(tree *p_tree){//指向一棵数
p_tree->p_node=NULL;
}
//清理函数
void tree_deinit(tree *p_tree){// 采用后序遍历清理,用递归函数
if(!(p_tree)){
return ; //若树为空,则空操作返回
}
tree_deinit(&(p_tree->p_node->left));//先清理左子树
tree_deinit(&(p_tree->p_node->right));//再清理右子树
free(p_tree->p_node);//最后释放根结点
p_tree->p_node=NULL;
}
//在有序二叉树里查找某个数字所在的位置
tree *tree_search_in_order(const tree*p_tree,int num){//采用前序遍历容易找,因为是有序二叉树,左子树的值比根结点小,右子树的值比根结点的值大
if(!(p_tree->p_node)){
return (tree *)p_tree; //显式声明
}
if(p_tree->p_node->num==num){ //找根结点
return (tree *)p_tree; //显式声明,没有警告
}
else if(p_tree->p_node->num>num){ //找左边
return tree_search_in_order(&(p_tree->p_node->left),num);
}
else{ //找右边
return tree_search_in_order(&(p_tree->p_node->right),num);
}
}
//在有序二叉树里插入新数字
void tree_insert_in_order(tree *p_tree,intnum){
tree *p_tr=tree_search_in_order(p_tree,num);//先查找新数字是否存在
if(p_tr->p_node){
return ; //如果存在,直接返回
}
node *p_tmp=(node *)malloc(sizeof(node));//如果不存在,分配内存
if(!p_tmp){ //内存分配失败,返回
return ;
}
p_tmp->num=num; //内存分配成功,把新数字赋值给新结点
p_tmp->left.p_node=NULL;
p_tmp->right.p_node=NULL;
p_tr->p_node=p_tmp;
}
//中序遍历打印函数
void tree_miter(tree*p_tree,void(*p_func)(int)/* 函数指针 */){
// 因为是有序二叉树,中序遍历可以使其从小到大排列
if(!(p_tree->p_node)){
return ;
}
tree_miter(&(p_tree->p_node->left),p_func);//递归调用处理左子树
p_func(p_tree->p_node->num);//处理根结点
tree_miter(&(p_tree->p_node->right),p_func);//递归调用处理右子树
}
//前序遍历函数
void tree_fiter(tree *p_tree,void(*p_func)(int)){
if(!(p_tree->p_node)){
return ;
}
p_func(p_tree->p_node->num);
tree_fiter(&(p_tree->p_node->left),p_func);
tree_fiter(&(p_tree->p_node->right),p_func);
}
//后序遍历函数
void tree_liter(tree *p_tree,void(*p_func)(int)){
if(!(p_tree->p_node)){
return ;
}
tree_fiter(&(p_tree->p_node->left),p_func);
tree_fiter(&(p_tree->p_node->right),p_func);
p_func(p_tree->p_node->num);
}
//从有序二叉树里删除一个数字的函数
void tree_remove(tree *p_tree,int num){
node *p_remove=NULL,*p_left=NULL,*p_right=NULL;
// 要删的结点 其左子树, 其右子树
tree *p_tmp=tree_search_in_order(p_tree,num);
if(!(p_tmp->p_node)){ //首先如果这数不存在,则不采取任何操作
return ;
}
p_remove=p_tmp->p_node;
p_left=p_remove->left.p_node;
p_right=p_remove->right.p_node;
if(!p_left){ //如果左子树为空
p_tmp->p_node=p_right;//把右子树当作结点
}
else if(!p_right){//如果右子树为空
p_tmp->p_node=p_left;//把左子树当作结点
}
else{//两个子树都存在时
tree*p_tmp1=tree_search_in_order(&(p_remove->left),p_right->num);
//找到要删除结点其右子树的值在左子树的位置,赋给tmp1结点
p_tmp1->p_node=p_right;//将右子树插到左子树(上个语句找的位置)里
p_tmp->p_node=p_left;//把左子树的结点替代原树(要删除的结点)的位置
}
free(p_remove);
p_remove=NULL;
}
编写头文件:2.h
#ifndef __2_H__
#define __2_H__
struct node;
//提前告诉编译器这个node结构体存在,不能与下面的结构体颠倒,只能把结构体指针放在前面,此时struct node *p_node占4个字节,下面的Node结构体的字节个数不确定
typedef struct{
struct node *p_node;//方块
}tree;
typedef struct node{ //圆圈
int num;
tree left; //圆圈里包含两个方块
tree right;
}node;
//初始化函数
void tree_init(tree *);//指向一棵数
//清理函数
void tree_deinit(tree *);// 采用后序遍历清理,用递归函数
tree *tree_search_in_order(const tree*,int);//采用前序遍历容易找,因为是有序二叉树,左子树的值比其父结点小,右子树的值比其父结点的值大
void tree_insert_in_order(tree *,int );
//中序遍历打印函数
void tree_miter(tree *,void(*)(int)) ;
void tree_fiter(tree *p_tree,void(*p_func)(int));
void tree_liter(tree *p_tree,void(*p_func)(int));
void tree_remove(tree *,int );
#endif //__2_H__
编写主函数2main.c:
#include<stdio.h>
#include"2.h"
void print_cb(int num){
printf("%d ",num);
}
int main(){
tree tr={0};
tree_init(&tr);
tree_insert_in_order(&tr,50);
tree_insert_in_order(&tr,25);
tree_insert_in_order(&tr,80);
tree_insert_in_order(&tr,13);
tree_insert_in_order(&tr,37);
tree_insert_in_order(&tr,61);
tree_insert_in_order(&tr,93);
tree_insert_in_order(&tr,7);
tree_remove(&tr,25);
tree_miter(&tr,print_cb);
printf("\n");
tree_deinit(&tr);
return 0;
}
输入指令:gcc2main.c 2.c
删掉一个数:
50
25 80
13 37 61 93
7
假如删除25
则树的图形变为:
50
13 80
7 37 61 93
或
50
37 80
13 61 93
7
在源函数增加函数:
58 void tree_remove(tree *p_tree,int num){
59 node *p_remove=NULL,*p_left=NULL,*p_right=NULL;
60 // 要删的结点 其左子树, 其右子树
61 tree*p_tmp=tree_search_in_order(p_tree,num);
62 if(!(p_tmp->p_node)){ //首先如果这数不存在,则不采取任何操作
63 return ;
64 }
65 p_remove=p_tmp->p_node;
66 p_left=p_remove->left.p_node;
67 p_right=p_remove->right.p_node;
68 if(!p_left){ //如果左子树为空
69 p_tmp->p_node=p_right;//把右子树当作结点
70 }
71 else if(!p_right){//如果右子树为空
72 p_tmp->p_node=p_left;//把左子树当作结点
73 }
74 else{//两个子树都存在时
75 tree*p_tmp1=tree_search_in_order(&(p_remove->left),p_right->num);
76//找到要删除结点其右子树的值在左子树的位置,赋给tmp1结点
77 p_tmp1->p_node=p_right;//将右子树插到左子树(上个语句找的位置)里
78
79 p_tmp->p_node=p_left;//把左子树的结点替代原树(要删除的结点)的位置
80 }
81 free(p_remove);
82 p_remove=NULL;
83 }
在头文件增加函数声明:
22 void tree_remove(tree *,int );
主函数增加一条语句变为:
6int main(){
7 tree tr={0};
8 tree_init(&tr);
9 tree_insert_in_order(&tr,50);
10 tree_insert_in_order(&tr,25);
11 tree_insert_in_order(&tr,80);
12 tree_insert_in_order(&tr,13);
13 tree_insert_in_order(&tr,37);
14 tree_insert_in_order(&tr,61);
15 tree_insert_in_order(&tr,93);
16 tree_insert_in_order(&tr,7);
17 tree_remove(&tr,25);
18 tree_miter(&tr,print_cb);
19 printf("\n");
20 tree_deinit(&tr);
21 return 0;
22 }
在源函数增加前序和后序遍历功能
57 //前序遍历函数
58void tree_fiter(tree *p_tree,void (*p_func)(int)){
59 if(!(p_tree->p_node)){
60 return ;
61 }
62 p_func(p_tree->p_node->num);
63 tree_fiter(&(p_tree->p_node->left),p_func);
64 tree_fiter(&(p_tree->p_node->right),p_func);
65 }
66//后序遍历函数
67void tree_liter(tree *p_tree,void (*p_func)(int)){
68 if(!(p_tree->p_node)){
69 return ;
70 }
71 tree_fiter(&(p_tree->p_node->left),p_func);
72 tree_fiter(&(p_tree->p_node->right),p_func);
73 p_func(p_tree->p_node->num);
74 }
在头文件添加函数声明:
22 void tree_fiter(tree *p_tree,void(*p_func)(int));
23 void tree_liter(tree *p_tree,void(*p_func)(int));
- Day19、双向链表、树(二叉树、遍历、删除结点)
- 删除结点 (双向链表)
- 删除二叉树结点
- 二叉树的创建,各种遍历,寻找结点,父结点,删除结点
- 二叉树转双向链表+BFS+中序遍历
- 二叉链表的建立、遍历,二叉树的深度、总结点、复制、叶结点总数
- 二叉树转换为双向链表(后序遍历)
- 二叉搜索树与双向链表(中序遍历 ****)
- 中序遍历二叉树将遍历结果存入双向循环链表C语言实现
- 【二叉树->链表】二叉树结构转双向线性链表结构(先序遍历)
- (Java实现)输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向
- 平衡二叉树(AVL)结点删除操作
- 数据结构与算法(十三)删除二叉树结点
- 二叉查找树/删除结点操作
- 二叉树(二)——遍历、深度统计、叶子结点统计、结点统计
- Java双向链表的创建结点,清空链表,添加结点,删除结点
- java实现二叉树的三种遍历方式,并把二叉树转为双向链表,输出二叉树
- 数据结构与算法分析学习笔记--第四章(二叉树:创建、递归遍历、非递归遍历、根据数据删除结点等)
- READING NOTE: ENet: A Deep Neural Network Architecture for Real-Time Semantic Segmentation
- 【Java_项目篇<1>】--JAVA实现坦克大战游戏--赋予敌人行动和攻击(五)
- XP系统连接不了WIFI的解决方案
- thinkphp中模版主题使用方法
- 是的,我更喜欢努力的自己
- Day19、双向链表、树(二叉树、遍历、删除结点)
- Vivado HLS从HDL到模型和C的描述(1):从运行一个程序开始
- 关于连续天数的几个思路。
- Android中使用SDcard进行文件的读取
- PNG文件格式
- Android项目重构之路:实现篇
- Day19、树的完整版代码
- Shape,Selector
- Cocos2d-x 3.X 事件分发机制