当前位置: 首页 > news >正文

Day24 目录遍历、双向链表、栈

day24 目录遍历、双向链表、栈


显示指定目录下的所有 .h 文件

功能描述

遍历指定目录(递归进入子目录),查找所有以 .h 为后缀的头文件,将其完整路径(路径 + 文件名)存储到双向链表中,并正向或反向打印输出。

核心技术点
  • 使用 opendir / readdir / closedir 进行目录遍历
  • 使用 DT_DIR 判断是否为目录,实现递归
  • 使用 sprintf 拼接路径字符串
  • 使用 strcmp 判断文件后缀是否为 .h
  • 使用双向链表动态存储匹配的文件路径

双向链表结构定义(DouLink.h
#ifndef _DOULINK_H_
#define _DOULINK_H_// 定义通用数据类型,用于存储文件路径
typedef struct person {char path[512];           // 存储文件的完整路径(含目录)
} DATATYPE;// 双向链表节点
typedef struct dou_node {DATATYPE data;            // 数据域struct dou_node *prev;    // 指向前一个节点struct dou_node *next;    // 指向后一个节点
} DouLinkNode;// 双向链表管理结构
typedef struct list {DouLinkNode *head;        // 指向链表头节点int clen;                 // 当前链表长度
} DouLinkList;// 遍历方向枚举
typedef enum {DIR_FORWARD,              // 正向遍历DIR_BACKWARD              // 反向遍历
} DIRECT;// 函数指针类型,用于查找、删除等操作
typedef int (*PFUN)(DATATYPE* data, void* arg);// 函数声明
DouLinkList *CreateDouLinkList();
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos);
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int ModifyDouLinkList(DouLinkList *dl, char *name, DATATYPE *newdata);
int DeleteDouLinkList(DouLinkList *dl, char *name);
int GetSizeDouLinkList(DouLinkList *dl);
int IsEmptyDouLinkList(DouLinkList *dl);
int DestroyDouLinkList(DouLinkList *dl);
int ShowDouLinkList(DouLinkList *dl, DIRECT direct);#endif // _DOULINK_H_

说明:该头文件定义了通用的双向链表结构,适用于多种数据类型。当前用于存储 .h 文件路径。


双向链表实现(DouLink.c
#include "DouLink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 创建空双向链表
DouLinkList *CreateDouLinkList() {DouLinkList *dl = malloc(sizeof(DouLinkList));if (NULL == dl) {perror("CreateDouLinkList malloc");return NULL;}dl->head = NULL;dl->clen = 0;return dl;
}// 头插法插入节点
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data) {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertHeadDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;if (IsEmptyDouLinkList(dl)) {dl->head = newnode;} else {newnode->next = dl->head;dl->head->prev = newnode;dl->head = newnode;}dl->clen++;return 0;
}// 正向或反向打印链表
int ShowDouLinkList(DouLinkList *dl, DIRECT direct) {DouLinkNode *tmp = dl->head;if (DIR_FORWARD == direct) {while (tmp) {printf("%s\n", tmp->data.path);  // 输出文件路径tmp = tmp->next;}} else {// 找到最后一个节点while (tmp && tmp->next) {tmp = tmp->next;}// 从尾向前遍历while (tmp) {printf("%s\n", tmp->data.path);tmp = tmp->prev;}}return 0;
}// 判断链表是否为空
int IsEmptyDouLinkList(DouLinkList *dl) {return 0 == dl->clen;
}// 尾插法插入节点
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data) {if (IsEmptyDouLinkList(dl)) {return InsertHeadDouLinkList(dl, data);} else {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertTailDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;DouLinkNode *tmp = dl->head;while (tmp->next) {tmp = tmp->next;}newnode->prev = tmp;tmp->next = newnode;}dl->clen++;return 0;
}// 在指定位置插入节点
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos) {int size = GetSizeDouLinkList(dl);if (pos < 0 || pos > size) {printf("InsertPosDouLinkList pos error\n");return 1;}if (0 == pos) {return InsertHeadDouLinkList(dl, data);} else if (size == pos) {return InsertTailDouLinkList(dl, data);} else {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertPosDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;DouLinkNode *tmp = dl->head;while (pos--) {tmp = tmp->next;}newnode->next = tmp;newnode->prev = tmp->prev;tmp->prev = newnode;newnode->prev->next = newnode;dl->clen++;}return 0;
}// 通用查找函数:通过函数指针匹配数据
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg) {DouLinkNode* tmp = dl->head;while (tmp) {if (fun(&tmp->data, arg)) {return tmp;}tmp = tmp->next;}return NULL;
}// 获取链表长度
int GetSizeDouLinkList(DouLinkList *dl) {return dl->clen;
}// 销毁链表(释放所有节点和链表结构)
int DestroyDouLinkList(DouLinkList *dl) {DouLinkNode *tmp = NULL;while (dl->head) {tmp = dl->head;dl->head = dl->head->next;free(tmp);}free(dl);return 0;
}

理想运行结果

  • CreateDouLinkList() 返回一个空链表指针,head=NULL, clen=0
  • 插入操作后链表长度递增,ShowDouLinkList 可按正/反顺序输出路径
  • DestroyDouLinkList 释放所有内存,无泄漏

目录遍历与头文件查找(main.c
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DouLink.h"// 指定起始路径(可修改)
char path[] = "/home/linux/20250818cd";// 判断文件是否为 .h 后缀
int is_headfile(char* path) {if (strlen(path) < 3) return 0;char* tmp = &path[strlen(path) - 2];  // 取最后两个字符前的位置if (0 == strcmp(tmp, ".h")) {return 1;}return 0;
}// 递归遍历目录,将 .h 文件路径插入链表
int do_ls(char* path, DouLinkList* dl) {DIR* dir = opendir(path);if (NULL == dir) {perror("opendir");return 1;}struct dirent* info;while ((info = readdir(dir)) != NULL) {char newpath[512] = {0};sprintf(newpath, "%s/%s", path, info->d_name);  // 拼接完整路径if (info->d_type == DT_DIR) {// 忽略 . 和 ..if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {continue;}// 递归进入子目录do_ls(newpath, dl);} else {// 非目录,检查是否为 .h 文件DATATYPE data = {0};if (is_headfile(newpath)) {strcpy(data.path, newpath);InsertHeadDouLinkList(dl, &data);  // 头插存储}}}closedir(dir);return 0;
}// 主函数:创建链表、遍历目录、打印结果
int main(int argc, char** argv) {DouLinkList* dl = CreateDouLinkList();do_ls("/home/linux", dl);  // 从 /home/linux 开始递归查找printf("=== 所有 .h 文件路径(正向输出)===\n");ShowDouLinkList(dl, DIR_FORWARD);DestroyDouLinkList(dl);return 0;
}

理想运行结果示例

=== 所有 .h 文件路径(正向输出)===
/home/linux/20250818cd/test.h
/home/linux/20250818cd/include/utils.h
/home/linux/20250818cd/module/config.h
  • 成功递归进入所有子目录
  • 正确识别 .h 文件并存储完整路径
  • 输出顺序取决于插入方式(头插 → 逆序)

双向链表(通用版)

功能说明

该双向链表支持插入、删除、查找、修改、遍历等操作,适用于存储结构化数据(如学生信息)。

数据结构定义(DouLink.h
#ifndef _DOULINK_H_
#define _DOULINK_H_typedef struct person {char name[32];char sex;int age;int score;
} DATATYPE;typedef struct dou_node {DATATYPE data;struct dou_node *prev;struct dou_node *next;
} DouLinkNode;typedef struct list {DouLinkNode *head;int clen;
} DouLinkList;typedef enum {DIR_FORWARD,DIR_BACKWARD
} DIRECT;typedef int (*PFUN)(DATATYPE* data, void* arg);// 函数声明(略去重复部分)
DouLinkList *CreateDouLinkList();
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos);
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int ModifyDouLinkList(DouLinkList *dl, PFUN fun, void* arg, DATATYPE *newdata);
int DeleteDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int GetSizeDouLinkList(DouLinkList *dl);
int IsEmptyDouLinkList(DouLinkList *dl);
int DestroyDouLinkList(DouLinkList *dl);
int ShowDouLinkList(DouLinkList *dl, DIRECT direct);#endif

说明:此版本 DATATYPE 包含姓名、性别、年龄、成绩,适用于人员管理。


链表操作实现(DouLink.c
  • CreateDouLinkListInsertHead/InsertTail/InsertPosShowDouLinkListIsEmptyGetSize 实现同上(略)
  • 新增 ModifyDouLinkListDeleteDouLinkListDestroyDouLinkList
// 根据条件修改节点数据
int ModifyDouLinkList(DouLinkList *dl, PFUN fun, void *arg, DATATYPE *newdata) {DouLinkNode *tmp = FindDouLinkList(dl, fun, arg);if (NULL == tmp) {printf("ModifyDouLinkList failure...\n");return 1;}memcpy(&tmp->data, newdata, sizeof(DATATYPE));return 0;
}// 根据条件删除节点
int DeleteDouLinkList(DouLinkList *dl, PFUN fun, void *arg) {DouLinkNode *tmp = FindDouLinkList(dl, fun, arg);if (NULL == tmp) {printf("del failure...\n");return 1;}if (tmp == dl->head) {dl->head = dl->head->next;if (dl->head) dl->head->prev = NULL;} else {if (tmp->next) tmp->next->prev = tmp->prev;tmp->prev->next = tmp->next;}free(tmp);dl->clen--;return 0;
}// 销毁整个链表
int DestroyDouLinkList(DouLinkList *dl) {DouLinkNode *tmp;while (dl->head) {tmp = dl->head;dl->head = dl->head->next;free(tmp);}free(dl);return 0;
}

测试程序(test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DouLink.h"// 按姓名查找
int findperbyname(DATATYPE* data, void* arg) {return 0 == strcmp(data->name, (char*)arg);
}// 按年龄查找
int findperbyage(DATATYPE* data, void* arg) {return data->age == *(int*)arg;
}int main(int argc, char** argv) {DouLinkList* dl = CreateDouLinkList();DATATYPE data[] = {{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92},{"zhangfei", 'm', 50, 80},};InsertHeadDouLinkList(dl, &data[0]);InsertHeadDouLinkList(dl, &data[1]);InsertHeadDouLinkList(dl, &data[2]);printf("------------正向输出--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);printf("------------反向输出--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);InsertTailDouLinkList(dl, &data[3]);printf("------------尾插后--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);InsertPosDouLinkList(dl, &data[4], 2);printf("------------位置插入后--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);int age = 50;DouLinkNode* tmp = FindDouLinkList(dl, findperbyage, &age);if (tmp) {printf("找到年龄为50的人: %s, 成绩: %d\n", tmp->data.name, tmp->data.score);}ModifyDouLinkList(dl, findperbyname, "lisi", &data[5]);printf("------------修改后--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);DeleteDouLinkList(dl, findperbyname, "liubei");printf("------------删除后--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);DestroyDouLinkList(dl);return 0;
}

理想运行结果

  • 正确插入、查找、修改、删除
  • 输出格式:name:xxx sex:x age:xx score:xx
  • 修改后 lisi 变为 zhangfeiliubei 被删除

顺序表与链表对比

对比项顺序表链表
存储方式连续内存空间物理上不连续,逻辑上连续
查找性能O(1) —— 支持随机访问O(n) —— 需遍历
插入/删除O(n) —— 需移动元素O(1) —— 只需修改指针(已知位置)
空间分配静态/固定大小,易浪费或溢出动态分配,灵活,无空间浪费

循环链表

  • 将单链表最后一个节点的 next 指针指向头节点第一个有效节点,形成环状结构。
  • 空表判断:head == NULL
  • 非空表遍历条件:p->next != head(代替 p->next != NULL
  • 常用于约瑟夫环、循环任务调度等场景。

双向链表(结构简写版)

typedef struct DulNode {ElemType data;               // 数据域struct DulNode *pri;         // 指向前驱struct DulNode *next;        // 指向后继
} DulNode, *DuLinkList;

✅ 注意:原始代码中 sturct 拼写错误,已修正为 struct


栈(Stack)

定义

限定仅在表尾进行插入和删除操作的线性表,遵循 “先进后出”(LIFO) 原则。

核心概念
  • 栈顶:允许操作的一端(插入/删除)
  • 栈底:不允许操作的一端
  • 基本操作
    • 入栈(Push)
    • 出栈(Pop)
    • 获取栈顶元素(GetTop)
分类
  • 顺序栈(数组实现)
  • 链式栈(链表实现)
    在这里插入图片描述
链式栈结构定义(LinkStack.h
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_typedef struct {char name[50];char sex;int age;int score;
} DATATYPE;typedef struct _linkstacknode {DATATYPE data;struct _linkstacknode *next;
} LinkStackNode;typedef struct {LinkStackNode* top;   // 栈顶指针int clen;             // 元素个数
} LinkStack;// 函数声明
LinkStack* CreateLinkStack();
int PushLinkStack(LinkStack* ls, DATATYPE* data);
int PopLinkStack(LinkStack* ls);
DATATYPE* GetTopLinkStack(LinkStack* ls);
int IsEmptyLinkStack(LinkStack* ls);
int GetSizeLinkStack(LinkStack* ls);
int DestroyLinkStack(LinkStack* ls);#endif

链式栈实现(LinkStack.c
#include "LinkStack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>LinkStack* CreateLinkStack() {LinkStack* ls = malloc(sizeof(LinkStack));if (!ls) {perror("CreateLinkStack malloc error");return NULL;}ls->top = NULL;ls->clen = 0;return ls;
}int PushLinkStack(LinkStack* ls, DATATYPE* data) {LinkStackNode* newnode = malloc(sizeof(LinkStackNode));if (!newnode) {perror("PushLinkStack malloc error");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = ls->top;ls->top = newnode;ls->clen++;return 0;
}int PopLinkStack(LinkStack* ls) {if (IsEmptyLinkStack(ls)) {printf("linkstack is empty\n");return 1;}LinkStackNode* tmp = ls->top;ls->top = ls->top->next;free(tmp);ls->clen--;return 0;
}DATATYPE* GetTopLinkStack(LinkStack* ls) {if (IsEmptyLinkStack(ls)) return NULL;return &ls->top->data;
}int IsEmptyLinkStack(LinkStack* ls) {return 0 == ls->clen;
}int GetSizeLinkStack(LinkStack* ls) {return ls->clen;
}int DestroyLinkStack(LinkStack* ls) {while (!IsEmptyLinkStack(ls)) {PopLinkStack(ls);}free(ls);return 0;
}

测试程序(test_stack.c
#include <stdio.h>
#include "LinkStack.h"int main() {LinkStack* ls = CreateLinkStack();DATATYPE data[] = {{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92}};for (int i = 0; i < 5; i++) {PushLinkStack(ls, &data[i]);}printf("出栈顺序(LIFO):\n");while (!IsEmptyLinkStack(ls)) {DATATYPE* tmp = GetTopLinkStack(ls);printf("name:%s score:%d\n", tmp->name, tmp->score);PopLinkStack(ls);}DestroyLinkStack(ls);return 0;
}

理想运行结果

出栈顺序(LIFO):
name:liubei score:92
name:guanerge score:91
name:wangmaizi score:90
name:lisi score:84
name:zhangsan score:80

使用栈实现 C 源文件符号匹配

原理
  • 遇到 ([{ 入栈
  • 遇到 )]} 检查栈顶是否匹配,匹配则出栈,否则报错
  • 文件结束时栈应为空,否则有未闭合符号
实现代码(check_brackets.c
#include <stdio.h>
#include <string.h>
#include "LinkStack.h"// 记录符号位置的结构
typedef struct {char c;int row;int col;
} SymData;int do_check(char* linebuf, LinkStack* ls, int num) {char* tmp = linebuf;SymData data = {0};SymData* top = NULL;int col = 1;while (*tmp) {switch (*tmp) {case '(':case '[':case '{':data.c = *tmp;data.row = num;data.col = col;PushLinkStack(ls, (DATATYPE*)&data);break;case ')':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '(') {printf("括号不匹配: ) at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;case ']':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '[') {printf("括号不匹配: ] at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;case '}':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '{') {printf("括号不匹配: } at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;}tmp++;col++;}return 0;
}int main() {LinkStack* ls = CreateLinkStack();FILE* fp = fopen("/home/linux/2.c", "r");if (!fp) {printf("fopen error\n");return 1;}int num = 1;char buf[1024];while (fgets(buf, sizeof(buf), fp)) {if (do_check(buf, ls, num)) {fclose(fp);DestroyLinkStack(ls);return 1;}num++;}if (IsEmptyLinkStack(ls)) {printf("✅ 所有括号匹配成功!\n");} else {SymData* top = (SymData*)GetTopLinkStack(ls);printf("❌ 存在未闭合符号: %c at row %d col %d\n", top->c, top->row, top->col);}fclose(fp);DestroyLinkStack(ls);return 0;
}

理想运行结果

  • 若括号全部匹配:输出 ✅ 所有括号匹配成功!
  • 若有不匹配:输出具体错误位置
  • 支持跨行匹配

栈的核心要点总结

  • 本质:受限制的线性表,仅允许在一端进行操作
  • 操作特性:先进后出(LIFO)
  • 两端定义
    • 栈顶:可进行插入/删除
    • 栈底:固定,不可操作
  • 主要作用
    • 递归调用管理
    • 表达式求值
    • 回溯算法
    • 符号匹配检查
  • 基本操作
    • 入栈(Push)
    • 出栈(Pop)
    • 获取栈顶(GetTop)
http://www.dtcms.com/a/343327.html

相关文章:

  • Mac电脑 3D建模工具--犀牛Rhino
  • 【个人网络整理】NOIP / 省选 /NOI 知识点汇总
  • 视频孪生技术在城市政务数字化转型中的应用与价值探索
  • ES_映射
  • Nacos-10--认识Nacos中的Raft协议(Nacos强一致性的实现原理)
  • VirtualBox 安装 Ubuntu Server 系统及 Ubuntu 初始配置
  • 区块链联邦学习思路一
  • 14、软件实现与测试
  • 实践题:智能健康监测系统设计方案
  • centos下安装Nginx(搭建高可用集群)
  • 亚马逊产品排名提升策略:从传统运营到AI驱动的智能化突破
  • 《信任链:幽灵签名》
  • 近端策略优化 (PPO) 算法深度解析
  • 智能求职推荐系统
  • mfc140u.dll文件是什么?解决mfc140u.dll文件丢失的有效解决方法分享
  • 密码管理中明文密码与空密码的危害与预防
  • 民国悬爱网剧《春迟》腾讯独播,石雨晴担纲联合出品人与联合制片人
  • Redis 主从复制(重点理解流程和原理)
  • Windows下服务封装
  • mac电脑使用(windows转Mac用户)
  • Java多线程编程——基础篇
  • STM32输入捕获相位差测量技术详解(基于TIM1复位模式)
  • Nacos 深度指南:从入门到高可用集群部署
  • ES6 面试题及详细答案 80题 (01-05)-- 基础语法与变量声明
  • C++宏的高级用法与元编程技巧
  • 数据结构青铜到王者第一话---数据结构基本常识(2)
  • 指数续创新高,期权的几种应对之策
  • 在线《相关性分析》
  • rs-fMRI_一篇文章中分析方法的梳理(翻译)
  • 职星学院企业培训系统:私有化部署赋能企业知识安全