跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

数据结构入门指南:线性结构与C语言实现

介绍数据结构入门知识,重点讲解线性结构(数组、链表、栈、队列)的核心特性与C语言实现。涵盖顺序存储与链式存储的区别,时间复杂度分析,以及动态数组扩容、单链表操作、顺序栈与链式队列的代码示例。包含括号匹配实战案例,总结内存管理、空指针处理等常见问题,并提供进阶学习路线,适合底层开发与嵌入式场景参考。

雾岛听风发布于 2026/3/24更新于 2026/8/1742 浏览

数据结构入门指南:线性结构核心知识点与C语言实现

数据结构是程序设计的基石,直接决定代码的效率与可扩展性,尤其在Linux内核开发、嵌入式设备资源管理等场景中,合理运用数据结构能显著优化程序性能。本文聚焦入门必学的线性结构(数组、链表、栈、队列),通过精简概念+规范C语言代码,带初学者快速掌握核心用法,为后续复杂结构(树、图)打下基础。

一、数据结构的核心价值与应用场景

数据结构的本质是'组织与存储数据的方式',其选择直接影响程序的时间复杂度与空间复杂度,核心应用场景如下:

  • 底层开发:Linux内核用链表管理进程、栈保存函数调用上下文,嵌入式设备用队列缓冲外设数据。
  • 算法优化:任何算法都依赖数据结构承载数据,如排序算法基于数组/链表,搜索算法基于树结构。
  • 工程实践:数据库索引用B+树,消息队列用队列结构,缓存机制用哈希表(后续进阶)。

入门阶段重点攻克线性结构——即数据元素按线性顺序排列的结构,是最基础且高频使用的类型。

二、预备知识:环境与核心概念

2.1 开发环境

沿用C语言入门环境(Windows Dev-C++/Linux GCC),无需额外配置,代码兼容双平台,编译运行方式与C语言程序一致。

2.2 核心概念
  • 逻辑结构:数据元素间的关联关系(线性结构为'一对一',如数组元素、链表节点)。
  • 物理结构:数据在内存中的存储方式,分为'顺序存储'(如数组,连续内存)和'链式存储'(如链表,离散内存)。
  • 时间复杂度:衡量操作(增删查改)的效率,常用O(1)(常数级)、O(n)(线性级)表示,越低效率越高。

三、线性结构核心知识点与C语言实现

以下按'数组→链表→栈→队列'的顺序讲解,每个结构聚焦核心特性、实现代码与适用场景,代码遵循变量名规范,仅保留核心功能。

3.1 数组(顺序存储线性结构)
3.1.1 核心特性

连续内存存储,支持随机访问(通过下标快速定位),但插入/删除元素需移动后续元素,效率较低(时间复杂度O(n)),适合元素数量固定、查询频繁的场景。

3.1.2 C语言实现(动态数组)

静态数组大小固定,实际开发中常用动态数组(基于malloc分配内存),实现元素的增删查改:

#include <stdio.h>
#include <stdlib.h>

// 动态数组结构体
typedef struct {
    int* array_data; // 存储数据的指针
    int array_size;  // 数组容量
    int element_count; // 实际元素个数
} DynamicArray;

// 初始化动态数组
void init_dynamic_array(DynamicArray* dynamic_array, int initial_size) {
    dynamic_array->array_data = (int*)malloc(initial_size * sizeof(int));
    dynamic_array->array_size = initial_size;
    dynamic_array->element_count = 0;
}

// 插入元素(尾部插入,效率O(1))
void insert_element(DynamicArray* dynamic_array, int element_value) {
    // 容量不足时扩容(翻倍扩容,减少扩容次数)
    if (dynamic_array->element_count == dynamic_array->array_size) {
        dynamic_array->array_size *= 2;
        dynamic_array->array_data = (int*)realloc(dynamic_array->array_data, dynamic_array->array_size * sizeof(int));
    }
    dynamic_array->array_data[dynamic_array->element_count++] = element_value;
}

// 查找元素(按值查找,效率O(n))
int find_element(DynamicArray* dynamic_array, int target_value) {
    for (int i = 0; i < dynamic_array->element_count; i++) {
        if (dynamic_array->array_data[i] == target_value) {
            return i; // 返回下标,未找到返回 -1
        }
    }
    return -1;
}

// 释放内存
void free_dynamic_array(DynamicArray* dynamic_array) {
    free(dynamic_array->array_data);
    dynamic_array->array_data = NULL;
    dynamic_array->array_size = 0;
    dynamic_array->element_count = 0;
}

int main() {
    DynamicArray dynamic_array;
    init_dynamic_array(&dynamic_array, 2); // 初始容量为 2
    insert_element(&dynamic_array, 10);
    insert_element(&dynamic_array, 20);
    insert_element(&dynamic_array, 30); // 触发扩容,容量变为 4
    int target_index = find_element(&dynamic_array, 20);
    printf("元素 20 的下标:%d\n", target_index);
    free_dynamic_array(&dynamic_array);
    return 0;
}
3.2 链表(链式存储线性结构)
3.2.1 核心特性

离散内存存储,通过指针连接节点,插入/删除元素无需移动数据(仅修改指针),效率O(1),但不支持随机访问(需从头遍历,效率O(n)),适合元素数量动态变化、插入删除频繁的场景(如Linux进程链表)。

3.2.2 C语言实现(单链表)

单链表是最基础的链表类型,每个节点包含数据域和指向下一节点的指针:

#include <stdio.h>
#include <stdlib.h>

// 链表节点结构体
typedef struct LinkNode {
    int node_data; // 数据域
    struct LinkNode* next; // 指针域,指向后续节点
} LinkNode;

// 创建新节点
LinkNode* create_link_node(int data_value) {
    LinkNode* new_node = (LinkNode*)malloc(sizeof(LinkNode));
    new_node->node_data = data_value;
    new_node->next = NULL;
    return new_node;
}

// 头部插入节点(效率O(1))
void insert_node_head(LinkNode** head_node, int data_value) {
    LinkNode* new_node = create_link_node(data_value);
    new_node->next = *head_node;
    *head_node = new_node;
}

// 遍历链表
void traverse_link_list(LinkNode* head_node) {
    LinkNode* current_node = head_node;
    while (current_node != NULL) {
        printf("%d ", current_node->node_data);
        current_node = current_node->next;
    }
    printf("\n");
}

// 释放链表内存
void free_link_list(LinkNode* head_node) {
    LinkNode* temp_node;
    while (head_node != NULL) {
        temp_node = head_node;
        head_node = head_node->next;
        free(temp_node);
    }
}

int main() {
    LinkNode* head_node = NULL; // 链表头节点初始化
    insert_node_head(&head_node, 30);
    insert_node_head(&head_node, 20);
    insert_node_head(&head_node, 10);
    printf("链表遍历结果:");
    traverse_link_list(head_node); // 输出:10 20 30
    free_link_list(head_node);
    return 0;
}
3.3 栈(先进后出LIFO结构)
3.3.1 核心特性

受限线性结构,仅允许在一端(栈顶)插入/删除元素,另一端(栈底)固定,符合'先进后出'规则,常用顺序存储(数组)实现,效率O(1),应用场景如函数调用栈、括号匹配、表达式求值。

3.3.2 C语言实现(顺序栈)
#include <stdio.h>
#include <stdlib.h>
#define MAX_STACK_SIZE 100 // 栈最大容量

// 顺序栈结构体
typedef struct {
    int stack_data[MAX_STACK_SIZE]; // 存储栈元素
    int stack_top; // 栈顶指针(-1 表示空栈)
} SequenceStack;

// 初始化栈
void init_sequence_stack(SequenceStack* sequence_stack) {
    sequence_stack->stack_top = -1;
}

// 判断栈空
int is_stack_empty(SequenceStack* sequence_stack) {
    return sequence_stack->stack_top == -1;
}

// 入栈
void push_stack_element(SequenceStack* sequence_stack, int element_value) {
    if (sequence_stack->stack_top == MAX_STACK_SIZE - 1) {
        printf("栈满,无法入栈\n");
        return;
    }
    sequence_stack->stack_data[++sequence_stack->stack_top] = element_value;
}

// 出栈
int pop_stack_element(SequenceStack* sequence_stack) {
    if (is_stack_empty(sequence_stack)) {
        printf("栈空,无法出栈\n");
        exit(1); // 异常退出
    }
    return sequence_stack->stack_data[sequence_stack->stack_top--];
}

int main() {
    SequenceStack sequence_stack;
    init_sequence_stack(&sequence_stack);
    push_stack_element(&sequence_stack, 10);
    push_stack_element(&sequence_stack, 20);
    push_stack_element(&sequence_stack, 30);
    printf("出栈元素:%d\n", pop_stack_element(&sequence_stack)); // 30
    printf("出栈元素:%d\n", pop_stack_element(&sequence_stack)); // 20
    return 0;
}
3.4 队列(先进先出FIFO结构)
3.4.1 核心特性

受限线性结构,允许在一端(队尾)插入元素,另一端(队头)删除元素,符合'先进先出'规则,常用链式存储实现(避免顺序存储的假溢出),应用场景如任务调度、外设数据缓冲、消息队列。

3.4.2 C语言实现(链式队列)
#include <stdio.h>
#include <stdlib.h>

// 队列节点结构体
typedef struct QueueNode {
    int node_data;
    struct QueueNode* next;
} QueueNode;

// 队列结构体(记录队头和队尾)
typedef struct {
    QueueNode* queue_front; // 队头
    QueueNode* queue_rear; // 队尾
} LinkQueue;

// 初始化队列
void init_link_queue(LinkQueue* link_queue) {
    link_queue->queue_front = link_queue->queue_rear = (QueueNode*)malloc(sizeof(QueueNode));
    link_queue->queue_front->next = NULL;
}

// 判断队列空
int is_queue_empty(LinkQueue* link_queue) {
    return link_queue->queue_front == link_queue->queue_rear;
}

// 入队
void enqueue_element(LinkQueue* link_queue, int element_value) {
    QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
    new_node->node_data = element_value;
    new_node->next = NULL;
    link_queue->queue_rear->next = new_node;
    link_queue->queue_rear = new_node;
}

// 出队
int dequeue_element(LinkQueue* link_queue) {
    if (is_queue_empty(link_queue)) {
        printf("队列空,无法出队\n");
        exit(1);
    }
    QueueNode* temp_node = link_queue->queue_front->next;
    int element_value = temp_node->node_data;
    link_queue->queue_front->next = temp_node->next;
    // 队尾与队头重合时,更新队尾
    if (link_queue->queue_rear == temp_node) {
        link_queue->queue_rear = link_queue->queue_front;
    }
    free(temp_node);
    return element_value;
}

int main() {
    LinkQueue link_queue;
    init_link_queue(&link_queue);
    enqueue_element(&link_queue, 10);
    enqueue_element(&link_queue, 20);
    enqueue_element(&link_queue, 30);
    printf("出队元素:%d\n", dequeue_element(&link_queue)); // 10
    printf("出队元素:%d\n", dequeue_element(&link_queue)); // 20
    return 0;
}

四、经典实战案例:括号匹配(栈的应用)

以面试高频题'括号匹配'为例,演示栈的实际应用,判断输入字符串中的括号(()、[]、{})是否成对闭合:

#include <stdio.h>
#include <string.h>
#define MAX_STACK_SIZE 100

typedef struct {
    char stack_data[MAX_STACK_SIZE];
    int stack_top;
} BracketStack;

void init_bracket_stack(BracketStack* bracket_stack) {
    bracket_stack->stack_top = -1;
}

void push_bracket(BracketStack* bracket_stack, char bracket_char) {
    bracket_stack->stack_data[++bracket_stack->stack_top] = bracket_char;
}

char pop_bracket(BracketStack* bracket_stack) {
    return bracket_stack->stack_data[bracket_stack->stack_top--];
}

// 判断括号匹配
int is_bracket_matched(char* input_string) {
    BracketStack bracket_stack;
    init_bracket_stack(&bracket_stack);
    int string_length = strlen(input_string);
    for (int i = 0; i < string_length; i++) {
        char current_char = input_string[i];
        // 左括号入栈
        if (current_char == '(' || current_char == '[' || current_char == '{') {
            push_bracket(&bracket_stack, current_char);
        } else {
            // 右括号无对应左括号,匹配失败
            if (bracket_stack.stack_top == -1) return 0;
            char top_bracket = pop_bracket(&bracket_stack);
            // 检查括号是否成对
            if ((current_char == ')' && top_bracket != '(') ||
                (current_char == ']' && top_bracket != '[') ||
                (current_char == '}' && top_bracket != '{')) {
                return 0;
            }
        }
    }
    // 栈空说明所有括号都匹配
    return bracket_stack.stack_top == -1;
}

int main() {
    char input_string[100];
    printf("请输入包含括号的字符串:");
    scanf("%s", input_string);
    if (is_bracket_matched(input_string)) {
        printf("括号匹配成功\n");
    } else {
        printf("括号匹配失败\n");
    }
    return 0;
}

五、入门常见问题与避坑指南

  • 链表空指针问题:遍历或操作链表时,需先判断节点指针是否为NULL,避免野指针访问(如链表尾节点next必须置空)。
  • 栈/队列边界判断:入栈前检查栈满,出栈前检查栈空;队列同理,避免越界访问或空操作。
  • 内存泄漏:链式结构(链表、链式队列)的节点需手动free,避免长期运行导致内存耗尽,建议封装专门的释放函数。
  • 顺序存储假溢出:顺序队列若仅移动指针,会出现'队尾满但队头有空余'的假溢出,可通过循环队列优化。

六、进阶学习路线(贴合底层开发)

掌握线性结构后,可按以下路线深化,适配Linux系统编程与嵌入式开发需求:

  1. 复杂线性结构:双向链表、循环链表(Linux内核常用)、循环队列、优先级队列(堆实现)。
  2. 非线性结构:树(二叉树、红黑树)、图,重点掌握二叉树的遍历(前中后序)与红黑树在Linux内核中的应用。
  3. 算法结合:学习基于数据结构的排序(链表排序、堆排序)、搜索(二叉树查找)算法,优化时间复杂度。
  4. 实战落地:在嵌入式开发中用队列缓冲串口数据,用链表管理传感器节点,复刻Linux内核简单链表逻辑。

总结

数据结构入门的关键是'理解特性 + 多实战',线性结构虽简单,但需明确不同结构的适用场景——数组适合查询,链表适合增删,栈/队列适合特定顺序需求。本文实现的代码均为核心逻辑,可直接用于课程作业或小型项目,建议手动敲写并调试,理解指针操作与内存管理的细节。

后续学习中,需结合Linux内核源码、嵌入式项目案例,体会数据结构在底层开发中的优化作用,逐步建立'数据结构决定程序性能'的思维,为复杂项目开发筑牢基础。

目录

  1. 数据结构入门指南:线性结构核心知识点与C语言实现
  2. 一、数据结构的核心价值与应用场景
  3. 二、预备知识:环境与核心概念
  4. 2.1 开发环境
  5. 2.2 核心概念
  6. 三、线性结构核心知识点与C语言实现
  7. 3.1 数组(顺序存储线性结构)
  8. 3.1.1 核心特性
  9. 3.1.2 C语言实现(动态数组)
  10. 3.2 链表(链式存储线性结构)
  11. 3.2.1 核心特性
  12. 3.2.2 C语言实现(单链表)
  13. 3.3 栈(先进后出LIFO结构)
  14. 3.3.1 核心特性
  15. 3.3.2 C语言实现(顺序栈)
  16. 3.4 队列(先进先出FIFO结构)
  17. 3.4.1 核心特性
  18. 3.4.2 C语言实现(链式队列)
  19. 四、经典实战案例:括号匹配(栈的应用)
  20. 五、入门常见问题与避坑指南
  21. 六、进阶学习路线(贴合底层开发)
  22. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Chrome 开发者工具(DevTools)快速入门
  • AI 普及时代,普通人如何靠差异化突围?
  • 秋叶绘世 Stable Diffusion 整合包与 ComfyUI 启动器说明
  • Open WebUI MCPo 项目解析:将 MCP 工具转换为 OpenAPI 接口
  • 云原生发展趋势及 Kubernetes 核心面试题汇总
  • 力扣 Hot 100 普通数组题解 Python 版
  • AI 写作避坑指南:Qwen3-4B-Instruct 部署与优化
  • 6款免费AI写作软件测评:AI写网文工具对比与去重技巧
  • AI 大模型通信机制:流式传输与数据封装逻辑详解
  • 将Microi低代码引擎集成到React项目中
  • 前端文件上传进阶:告别原生 input 的局限
  • 前端微前端架构实践:告别巨石应用
  • Self-Instruct:基于 LLM 的指令数据集自动生成策略
  • Spring Boot 4.0 虚拟线程时代:WebFlux 与 WebMVC 选型指南
  • 基于 ESP32-S3 芯片的 Wi-Fi 智能机器人系统设计
  • Flutter BIP340 适配鸿蒙 HarmonyOS 实战:Schnorr 签名应用
  • Python 操作符 == 与 is 的深度解析与使用场景
  • 2026 年 3 月中旬 AI 产业动态:算力、智能体与生态
  • Flutter eip55 库在鸿蒙系统的以太坊地址校验适配指南
  • 基于 Web-Check 与内网穿透的远程网站安全检测方案

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online