学嵌入式和C语言编程数据结构|学习日记Day22:栈,二叉树,哈希存储

📚 一、栈

栈是只允许从一端进行插入和删除数据的线性存储结构

可以操作增删的这一端叫栈顶 ,另一端叫栈底

  • 压栈(入栈):向栈存入数据
  • 出栈(弹栈):从栈取出数据
  • 核心特性:先进后出(FILO)

栈的实际应用

  1. 解决递归回溯类问题
  2. 软件撤销功能、网页回退缓存
  3. 字符串符号匹配,判断成对符号是否匹配、有无丢失

顺序栈

顺序栈基于数组实现,分为4种状态:满增栈、空增栈、满减栈、空减栈。

|----|------------------|--------------|
| 名词 | 说明 | 判断依据 |
| 满栈 | 栈顶位置一直存有元素 | 看栈顶位置是否存有元素 |
| 空栈 | 栈顶位置一直没有元素 | 看栈顶位置是否存有元素 |
| 增栈 | 栈顶移动方向:低地址 → 高地址 | 栈顶移动方向(生长方向) |
| 减栈 | 栈顶移动方向:高地址 → 低地址 | 栈顶移动方向(生长方向) |

链式栈实战代码

底层使用单链表实现栈,链表头部作为栈顶,入栈出栈都操作表头,效率O(1)。文件分为头文件 stack.h 、实现文件 stack.c 、测试 main.c

stack.h

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #ifndef STACK_H #define STACK_H typedef int Data_t; typedef struct node { Data_t data; struct node *pnext; }Node_s; typedef struct stack { Node_s *ptop; int clen; }Stack_s; Stack_s *create_stack(); int is_empty_stack(Stack_s *pstack); int push_stack(Stack_s *pstack, Data_t data); int pop_stack(Stack_s *pstack, Data_t *pdata); int get_stack_top(Stack_s *pstack,Data_t *ptop); void show_stack(Stack_s *pstack); void clear_stack(Stack_s *pstack); void destroy_stack(Stack_s *pstack); #endif |

stack.c

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include <stdlib.h> #include "stack.h" Stack_s *create_stack() { Stack_s *pstack=malloc(sizeof(Stack_s)); if(NULL==pstack) { printf("malloc error\n"); return NULL; } pstack->ptop=NULL; pstack->clen=0; return pstack; } int is_empty_stack(Stack_s *pstack) { return NULL==pstack->ptop; } int push_stack(Stack_s *pstack, Data_t data) { Node_s *pinsert=malloc(sizeof(Node_s)); if(pinsert==NULL) { printf("malloc error\n"); return -1; } pinsert->data=data; pinsert->pnext=NULL; pinsert->pnext=pstack->ptop; pstack->ptop=pinsert; pstack->clen++; return 0; } int pop_stack(Stack_s *pstack, Data_t *pdata) { if(is_empty_stack(pstack)) { return -1; } Node_s *pfree=pstack->ptop; pstack->ptop=pfree->pnext; if(pdata!=NULL) { *pdata=pfree->data; } free(pfree); pstack->clen--; return 0; } int get_stack_top(Stack_s *pstack,Data_t *ptopdata) { if(is_empty_stack(pstack)) { return -1; } else { *ptopdata=pstack->ptop->data; return 0; } } void show_stack(Stack_s *pstack) { Node_s *ptmp=pstack->ptop; while(ptmp) { printf("%d ",ptmp->data); ptmp=ptmp->pnext; } printf("\n"); } void clear_stack(Stack_s *pstack) { while(!is_empty_stack(pstack)) { pop_stack(pstack,NULL); } } void destroy_stack(Stack_s *pstack) { clear_stack(pstack); free(pstack); } |

main.c(链式栈测试)

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include "stack.h" int main(void) { Stack_s *pstack=NULL; pstack=create_stack(); if(NULL==pstack) { return -1; } for(int i=0;i<5;++i) { push_stack(pstack, i); } show_stack(pstack); printf("clen = %d\n",pstack->clen); Data_t pdata; pop_stack(pstack, &pdata); show_stack(pstack); printf("pop : %d\n",pdata); printf("clen = %d\n",pstack->clen); Data_t ptopdata; int ret=0; ret=get_stack_top(pstack,&ptopdata); if(ret==0) { printf("top : %d\n",ptopdata); } destroy_stack(pstack); return 0; } |

代码关键点:

  1. ptop永远指向链表第一个节点(栈顶)
  2. push:新节点插在链表头部
  3. pop:释放栈顶节点,更新ptop
  4. clear_stack循环pop清空全部节点,destroy_stack先清空再释放栈管理结构体,防止内存泄漏

🌳 二、二叉树(一对多树形结构)

树:由根节点和若干子节点构成,具备一对多关系的集合。

空树:没有任何节点。

基础名词释义

  • 根节点:树最顶层节点
  • 叶子节点(终端节点):没有子节点,节点度为0
  • 分支节点:拥有子节点的节点
  • 节点的度:节点拥有的子节点数量
  • 树的度(广度):树内节点的最大度
  • 树的深度:描述树的层数

二叉树、满二叉树、完全二叉树

二叉树:树的度为2的树形结构,左右子节点不能互换。

小提示:删除完全二叉树节点,需要从下到上、从右往左删除。

二叉树四种遍历方式

  1. 前序遍历:根 → 左子树 → 右子树
  2. 中序遍历:左子树 → 根 → 右子树
  3. 后序遍历:左子树 → 右子树 → 根
  4. 层序遍历:从上到下,从左至右逐层访问(需要借助队列实现)

|-----------------------------------------------------------------------------|
| |

链式二叉树完整工程代码

工程包含:tree.h、tree.c、linkque.h、linkque.c(层序遍历依赖链式队列)、main.c测试文件。

tree.h

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #ifndef tree_h #define tree_h typedef char Data_t; typedef struct tr_node { Data_t data; struct tr_node *pl; struct tr_node *pr; }Tnode_s; extern Tnode_s *create_bin_tree(); extern void pre_show_bin_tree(Tnode_s *proot); extern void mid_show_bin_tree(Tnode_s *proot); extern void tail_show_bin_tree(Tnode_s *proot); extern void floor_show_bin_tree(Tnode_s *proot); extern int get_node_cnt(Tnode_s *proot); extern int get_tree_deep(Tnode_s *proot); extern void destroy_tree(Tnode_s *proot); #endif |

linkque.h 层序遍历依赖队列头文件

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #ifndef LINKQUE_H #define LINKQUE_H #include "tree.h" typedef Tnode_s * Data_s; typedef struct node { Data_s data; struct node *pnext; }Node_s; typedef struct linkque { Node_s *phead; Node_s *ptail; int clen; }Linkque_s; extern Linkque_s *creat_linkque(); extern int is_empty_linkque(Linkque_s *pque); extern int insert_linkque_tail(Linkque_s *pque,Data_s data); extern int pop_link_queue(Linkque_s *pque, Data_s *data); extern int get_link_queue_head(Linkque_s *pque,Data_s *pdata); extern void show_linkque(Linkque_s *pque); extern void destory_linkque(Linkque_s *pque); #endif |

linkque.c 链式队列实现

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include <stdlib.h> #include "linkque.h" Linkque_s *creat_linkque() { Linkque_s *pque=malloc(sizeof(Linkque_s)); if(NULL==pque) { printf("malloc error\n"); return NULL; } pque->phead=NULL; pque->ptail=NULL; pque->clen=0; return pque; } int is_empty_linkque(Linkque_s *pque) { return NULL==pque->phead; } int insert_linkque_tail(Linkque_s *pque,Data_s data) { Node_s *pinsert=malloc(sizeof(Node_s)); if(NULL==pinsert) { printf("malloc error\n"); return -1; } pinsert->data=data; pinsert->pnext=NULL; if(is_empty_linkque(pque)) { pque->phead=pinsert; pque->ptail=pinsert; } else { pque->ptail->pnext=pinsert; pque->ptail=pinsert; } pque->clen++; return 0; } int pop_link_queue(Linkque_s *pque, Data_s *data) { if(is_empty_linkque(pque)) { return -1; } Node_s *pfree=pque->phead; pque->phead=pfree->pnext; if(data!=NULL) { *data=pfree->data; } free(pfree); if(NULL==pque->phead) { pque->ptail=NULL; } pque->clen--; return 0; } int get_link_queue_head(Linkque_s *pque,Data_s *pdata) { if(is_empty_linkque(pque)) { return -1; } *pdata=pque->phead->data; return 0; } void show_linkque(Linkque_s *pque) { if(is_empty_linkque(pque)) { return ; } Node_s *ptmp=pque->phead; while (NULL!=ptmp) { ptmp=ptmp->pnext; } printf("\n"); } void destory_linkque(Linkque_s *pque) { while(!is_empty_linkque(pque)) { pop_link_queue(pque,NULL); } free(pque); return ; } |

tree.c 二叉树核心逻辑

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include <stdlib.h> #include "tree.h" #include "linkque.h" Data_t tree\[\]="ABF##GC###DH#I##E##"; int idx=0; Tnode_s *create_bin_tree() { Data_t data=treeidx++; if('#' == data) { return NULL; } Tnode_s *ptnode=malloc(sizeof(Tnode_s)); if(NULL == ptnode) { printf("malloc error\n"); return NULL; } ptnode->data=data; ptnode->pl=create_bin_tree(); ptnode->pr=create_bin_tree(); return ptnode; } void pre_show_bin_tree(Tnode_s *proot) { if(NULL == proot) { return ; } printf("%c",proot->data); pre_show_bin_tree(proot->pl); pre_show_bin_tree(proot->pr); } void mid_show_bin_tree(Tnode_s *proot) { if(NULL == proot) { return ; } mid_show_bin_tree(proot->pl); printf("%c",proot->data); mid_show_bin_tree(proot->pr); } void tail_show_bin_tree(Tnode_s *proot) { if(NULL == proot) { return ; } tail_show_bin_tree(proot->pl); tail_show_bin_tree(proot->pr); printf("%c",proot->data); } void floor_show_bin_tree(Tnode_s *proot) { Linkque_s *plque=NULL; plque=creat_linkque(); if(NULL == plque) { return ; } Data_s outdata; insert_linkque_tail(plque,proot); while (!is_empty_linkque(plque)) { pop_link_queue(plque,&outdata); printf("%c", outdata->data); if (outdata->pl != NULL) { insert_linkque_tail(plque, outdata->pl); } if (outdata->pr != NULL) { insert_linkque_tail(plque, outdata->pr); } } destory_linkque(plque); } int get_node_cnt(Tnode_s *proot) { if(NULL == proot) { return 0; } return 1+get_node_cnt(proot->pl)+get_node_cnt(proot->pr); } int get_tree_deep(Tnode_s *proot) { if(NULL == proot) { return 0; } int left_floor=get_tree_deep(proot->pl); int right_floor=get_tree_deep(proot->pr); return left_floor>right_floor?left_floor+1:right_floor+1; } void destroy_tree(Tnode_s *proot) { if(NULL == proot) { return ; } destroy_tree(proot->pl); destroy_tree(proot->pr); free(proot); } |

main.c二叉树测试代码

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include "tree.h" #include "linkque.h" int main(void) { Tnode_s *ptnode=NULL; ptnode=create_bin_tree(); if(ptnode == NULL) { return -1; } pre_show_bin_tree(ptnode); printf("\n"); mid_show_bin_tree(ptnode); printf("\n"); tail_show_bin_tree(ptnode); printf("\n"); floor_show_bin_tree(ptnode); printf("\n"); int node_cnt=0; node_cnt=get_node_cnt(ptnode); printf("node_cnt = %d\n",node_cnt); int tree_deep=0; tree_deep=get_tree_deep(ptnode); printf("tree_deep = %d\n",tree_deep); destroy_tree(ptnode); return 0; } |

代码关键点总结

  1. create_bin_tree:使用带#标记的前序字符串递归创建二叉树,#代表空节点
  2. 前、中、后序全部依靠递归实现;层序遍历必须借助队列,先入队根节点,出队打印,再依次入队左右孩子
  3. get_node_cnt:递归,当前节点+左子树节点数+右子树节点数
  4. get_tree_deep:取左右子树深度较大值+1
  5. destroy_tree后序释放,先释放左右子树,再释放自己,杜绝内存泄漏

🔑 三、哈希存储

哈希存储:在数据关键字与存储位置之间建立映射关系,依据映射快速存、取数据。这个映射函数叫做哈希函数(散列函数)

核心目的:实现快速检索数据。

流程:数据key → 哈希函数 → 得到存储位置。

常见哈希函数

  1. 求余法:f(key) = key % 10
  2. 一次函数法:f(key) = a*key + b

哈希冲突

条件: key1≠key2 ,但是 f(key1)=f(key2)

不同关键字,经过哈希计算得到同一个存储位置,就产生哈希冲突。

解决哈希冲突两种方案

  1. 开放定址法:哈希表本身提供连续内存空间,冲突就向后寻找下一个空位。
  2. 链地址法:数组每一个位置挂载一条链表,冲突的数据挂接到同一个链表上。本次代码就是链地址法实现哈希表。

链地址法哈希表实战代码

功能:存储人名和电话号码,以名字首字符作为key计算哈希地址,冲突则挂在对应位置的单链表。文件包含 hash.h hash.c main.c

hash.h

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #ifndef HASH_H #define HASH_H #define HASH_SIZE 27 typedef struct per { char name32; char tel16; }Data_s; typedef struct node { Data_s data; struct node *pnext; }Node_s; extern int hash_function(char key); extern int insert_hash_table(Node_s **hash_table,Data_s data); extern void show_hash_table(Node_s **hash_table); #endif |

hash.c

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include <stdlib.h> #include "hash.h" int hash_function(char key) { if('a' <= key && key <= 'z') { return key-'a'; } else if('A' <= key && key <= 'Z') { return key-'A'; } else { return HASH_SIZE-1; } } int insert_hash_table(Node_s **hash_table,Data_s data) { int addr = hash_function(data.name0); Node_s *pnode = malloc(sizeof(Node_s)); if(NULL == pnode) { printf("malloc error\n"); return -1; } pnode->data=data; pnode->pnext=NULL; pnode->pnext=hash_tableaddr; hash_tableaddr=pnode; return 0; } void show_hash_table(Node_s **hash_table) { for(int i=0;i<HASH_SIZE;++i) { if(hash_tablei != NULL) { Node_s *pnode = malloc(sizeof(Node_s)); if(NULL == pnode) { printf("malloc error\n"); return ; } pnode=hash_tablei; while(pnode != NULL) { printf("%s %s\n",pnode->data.name,pnode->data.tel); pnode=pnode->pnext; } } } } |

main.c(哈希表测试)

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| C #include <stdio.h> #include "hash.h" int main(void) { Node_s *hash_tableHASH_SIZE={NULL}; Data_s per5={{"zhangsan","110"},{"lisi","120"}, {"wanger","119"},{"zhaowu","114"}, {"tianqi","100"}}; for(int i=0;i<5;++i) { insert_hash_table(hash_table,peri); } show_hash_table(hash_table); return 0; } |

哈希代码关键点

  1. hash_function:取名字首字母,小写a‑z映射0‑25,非字母放到下标26位置;
  2. 插入采用头插法,新节点直接挂载到数组对应下标链表头部;
  3. 链地址法:哈希地址相同的全部挂在同一条链表,以此解决哈希冲突;
  4. 注意:当前代码缺少哈希表销毁释放逻辑,实际项目中需要补充遍历释放每一个链表节点,避免内存泄漏。

✍️今日学习小结

  1. 栈记住核心规则:先进后出。手写链式栈,熟悉创建、入栈、出栈、取栈顶、清空销毁整套接口,注意内存释放,防止内存泄漏。
  2. 二叉树分清满二叉树、完全二叉树概念;前中后序递归遍历,层序遍历需要队列辅助;递归求节点总数、树深度;递归字符串生成二叉树、后序销毁树,笔试常考遍历序列还原树。
  3. 哈希存储重点理解哈希函数、哈希冲突,两种冲突解决方法是考试高频考点;完成链地址法哈希表代码实现,理解头插法挂载冲突节点。

今日踩坑点:

①忘记销毁栈/二叉树/队列,出现内存泄漏;

②层序遍历队列用完没有销毁;

③二叉树递归创建的时候索引全局变量容易出错;

④本次哈希示例代码没有写释放接口,实际开发必须手动释放链表节点。

相关推荐
Navigator_Z1 小时前
LeetCode //C - 1206. Design Skiplist
c语言·算法·leetcode
码行山野赴时序归途1 小时前
三道经典数组题:从暴力到最优的算法思维
c语言·开发语言·数据结构·算法·leetcode
Navigator_Z3 小时前
LeetCode //C - 1209. Remove All Adjacent Duplicates in String II
c语言·算法·leetcode
for_ever_love__3 小时前
python基础语法学习: 闭包
开发语言·python·学习·闭包
皮卡丘不断更5 小时前
从遥操作示范到可复现训练:LeRobot 0.6.1 的机器人学习工作流
人工智能·学习·机器人·开源·开发工具
IZero075 小时前
Elasticsearch 学习笔记筑基-文档操作篇
笔记·学习·elasticsearch
码行山野赴时序归途6 小时前
算法效率的度量(上):时间复杂度中的对数思维
c语言·数据结构·算法
小白说大模型7 小时前
AI驱动的个性化学习路径:知识图谱与知识点关联的存储与推理
大数据·人工智能·学习·mysql·机器学习·prompt·知识图谱
吃好睡好便好7 小时前
三伏天结束了
学习·生活·毕淑敏·心理游戏