29. 【C语言】动态数据结构:单向链表

C语言期末复习指南:核心考点详解与编程实战解析 C语言作为一门贴近硬件的编程语言,其核心在于对数据类型、内存管理和程序流程的精确控制。理解其底层原理,如指针的本质是内存地址、数组的连续存储特性以及函数的调用机制,是掌握这门语言的关键。这些基础概念不仅构成了程序运行的基石,也直接决定了代码的性能与安全性。在工程实践中,熟练运用指针进行高效的数据操作、通过结构体组织复杂信息以及利用文件操作实现数据持久化,是解决实际问题的必备技能。对于面临期末复习或技术面试的学习者而言,深入剖析【指针与内存管理】的常见陷阱,并通过【字符串处理】等经典例题构建解题思维,能够有效 阅读详情

前面我们学数组时说过,数组在内存中是连续排列的,这让它访问任意元素飞快(O(1)),但也带来一个死穴:插入和删除慢如牛。想在数组中间插入一个新元素,需要把后面的元素全部往后挪一位,平均要移动一半的数据。

而链表正是为解决这个问题而生。它不再要求元素们挤在一起,而是用“指针”把散落在内存各处的节点串起来。插入一个节点只要修改几个指针,时间复杂度 O(1)。今天我们就来攻克最基础的链表——单向链表。


一、链表是什么?——火车与挂钩

想象一列火车:每节车厢都装着一部分货物,并且挂着一个钩子,连接到下一节车厢。火车头(头指针)只要抓住第一节车厢,就能沿着钩子找到整个列车。

在链表里:

  • 车厢 = 结点(Node),包含两样东西:数据 + 指向下一个结点的指针。
  • 钩子 = next 指针。
  • 火车头 = 头指针(head),指向第一个结点。

单向链表中,每个结点只知道自己的后继是谁,不知道前驱。最后一个结点的 next 指向 NULL,表示列车结束。

head → [数据|next] → [数据|next] → [数据|NULL]

二、结点的定义:结构体 + 自引用

链表结点是一个结构体,里面有一个同类型的指针指向下一个结点。这种“自己指向自己”的结构称为自引用结构。

typedef struct Node {
    int data;            // 数据域(可以是任意类型,甚至 void*)
    struct Node *next;   // 指针域:指向下一个结点
} Node;

typedef 让我们以后写 Node 而不是 struct Node。但注意:在结构体内部,Node 这个别名还没定义完成,所以必须写 struct Node *next;。

数据域这里用了 int 作为例子。实际可以换成任何需要的类型,甚至用 void* 指向外部数据(后面专门讲通用链表时会展开)。


三、创建结点:malloc + 赋值

链表的结点是动态分配的,因为我们需要它们在函数返回后依然存在(动态存储期)。

#include <stdlib.h>

Node *create_node(int data) {
    Node *new_node = (Node*)malloc(sizeof(Node));
    if (new_node == NULL) {
        return NULL;   // 分配失败
    }
    new_node->data = data;
    new_node->next = NULL;
    return new_node;
}

每次调用 create_node 就在堆上造一节新“车厢”,初始时它的钩子为空,等着被挂到列车上。


四、插入结点

1. 头插法:新结点变成新的头

把新结点放到链表最前面,原来的头结点成为它的后继。时间复杂度 O(1)。

// head 是指向头指针的指针(二级指针)
void insert_at_head(Node **head, int data) {
    Node *new_node = create_node(data);
    if (new_node == NULL) return;

    new_node->next = *head;   // 新结点的 next 指向原来的第一个
    *head = new_node;         // 头指针指向新结点
}

为什么用二级指针? 因为插入操作可能需要修改 main 里的头指针变量本身。如果我们只传 Node *head(一级指针),函数内部修改的只是副本,外面的 head 纹丝不动。传 Node **head 我们就能解引用修改外部的指针。这与第十九篇动态分配时讲的一样。

调用:

Node *head = NULL;
insert_at_head(&head, 10);
insert_at_head(&head, 20);
insert_at_head(&head, 30);
// 现在链表:30 -> 20 -> 10 -> NULL

2. 尾插法:新结点追加到末尾

需要先找到最后一个结点(next == NULL 的那个),然后让它指向新结点。如果链表为空,新结点直接成为头。

时间复杂度 O(n),因为要遍历找到尾部。

void insert_at_tail(Node **head, int data) {
    Node *new_node = create_node(data);
    if (new_node == NULL) return;

    if (*head == NULL) {
        *head = new_node;
        return;
    }

    Node *current = *head;
    while (current->next != NULL) {   // 找到最后一个结点
        current = current->next;
    }
    current->next = new_node;
}

五、遍历链表

从头结点开始,沿着 next 一路走,直到碰到 NULL。

void print_list(Node *head) {
    Node *current = head;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

遍历是许多操作的基础——查找、计数、打印都靠它。


六、查找结点:按值搜索

从头开始,依次比较每个结点的数据,找到返回结点指针,找不到返回 NULL。

Node *find_node(Node *head, int target) {
    Node *current = head;
    while (current != NULL) {
        if (current->data == target) {
            return current;   // 找到了
        }
        current = current->next;
    }
    return NULL;   // 没找到
}

如果要按其他条件搜索(比如查找某个学号的学生),逻辑完全一样,只是比较的条件不同。


七、删除结点

删除比插入复杂,需要处理三种情况。

1. 删除头结点

直接让头指针指向第二个结点,然后释放原头结点。

2. 删除中间/尾部结点

需要找到被删结点的前一个结点,把它的 next 跨过被删结点,指向被删结点的下一个,然后释放被删结点。

同样用二级指针,因为可能要修改头指针。

void delete_node(Node **head, int target) {
    if (*head == NULL) return;

    Node *current = *head;
    Node *prev = NULL;

    // 寻找要删除的结点
    while (current != NULL && current->data != target) {
        prev = current;
        current = current->next;
    }

    if (current == NULL) return;   // 没找到

    if (prev == NULL) {
        // 要删除的是头结点
        *head = current->next;
    } else {
        // 要删除的是中间或尾部结点
        prev->next = current->next;
    }

    free(current);
}

八、释放整个链表

链表用完了,必须归还所有动态分配的内存,否则造成内存泄漏。

void free_list(Node *head) {
    Node *current = head;
    while (current != NULL) {
        Node *temp = current;      // 记住当前结点
        current = current->next;   // 先移动到下一个
        free(temp);                // 再释放记住的那个
    }
}

顺序不能错——如果先 free(current),就再也找不到下一个结点了。


九、反向遍历

单向链表只能从头往后走,如果需要从尾到头输出,有两种方法。

方法 1:递归

void print_reverse_recursive(Node *head) {
    if (head == NULL) return;
    print_reverse_recursive(head->next);
    printf("%d ", head->data);
}

原理是递归会一直走到最后一个结点,然后在逐层返回时打印。优点是简洁,缺点是链表太长会导致栈溢出。

方法 2:辅助栈(迭代)

把数据逐个压入数组(或其他栈结构),再逆序输出。这需要额外 O(n) 空间,但避免了递归栈风险。

void print_reverse_iterative(Node *head) {
    int stack[1000];   // 假设链表不超过 1000 个
    int top = 0;
    Node *curr = head;
    while (curr != NULL) {
        stack[top++] = curr->data;
        curr = curr->next;
    }
    while (top > 0) {
        printf("%d ", stack[--top]);
    }
}

另一个经典方法是不需要额外空间的原地反转,但那会改变链表结构,用于需要反复反向遍历的场景。


十、完整示例:串联所有操作

把上面的函数整合起来,测试一遍。

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

Node *create_node(int data) {
    Node *n = (Node*)malloc(sizeof(Node));
    if (n) { n->data = data; n->next = NULL; }
    return n;
}

void insert_at_head(Node **head, int data) {
    Node *n = create_node(data);
    if (n) { n->next = *head; *head = n; }
}

void insert_at_tail(Node **head, int data) {
    Node *n = create_node(data);
    if (!n) return;
    if (*head == NULL) { *head = n; return; }
    Node *cur = *head;
    while (cur->next) cur = cur->next;
    cur->next = n;
}

void delete_node(Node **head, int target) {
    if (*head == NULL) return;
    Node *cur = *head, *prev = NULL;
    while (cur && cur->data != target) { prev = cur; cur = cur->next; }
    if (!cur) return;
    if (prev) prev->next = cur->next;
    else *head = cur->next;
    free(cur);
}

void print_list(Node *head) {
    for (Node *cur = head; cur; cur = cur->next)
        printf("%d -> ", cur->data);
    printf("NULL\n");
}

void free_list(Node *head) {
    Node *cur = head;
    while (cur) { Node *tmp = cur; cur = cur->next; free(tmp); }
}

int main(void) {
    Node *head = NULL;

    insert_at_head(&head, 3);
    insert_at_head(&head, 2);
    insert_at_head(&head, 1);
    insert_at_tail(&head, 4);
    insert_at_tail(&head, 5);
    printf("初始链表: "); print_list(head);   // 1->2->3->4->5->NULL

    delete_node(&head, 3);
    printf("删除 3 后: "); print_list(head);   // 1->2->4->5->NULL

    delete_node(&head, 1);
    printf("删除 1 后: "); print_list(head);   // 2->4->5->NULL

    free_list(head);
    return 0;
}

十一、常见错误与陷阱

1. 忘记在删除或释放后更新指针

free(head);
// head 没有置 NULL,如果后续不小心再访问,就是悬垂指针

释放头结点后,如果外部还要用 head,必须把它置为 NULL。可以 free_list 后手动 head = NULL;。

2. 释放顺序错误导致内存泄漏或访问无效

必须先保存 next,再 free 当前结点,否则就和链表断了联系。

3. 插入时没有修改外部头指针

如果不用二级指针而用一级指针,insert_at_head 函数内部修改了形参,外部的 head 不变,导致链表“丢失”。

void bad_insert(Node *head, int data) {
    Node *n = create_node(data);
    n->next = head;   // head 是形参,修改不影响外部
    head = n;
}
// main 里的 head 依然为 NULL

要么返回新的头指针,要么用二级指针,这是链表操作的核心设计选择。

4. 遍历时忘记移动指针,导致死循环

while (cur != NULL) {
    printf("%d\n", cur->data);
    // 忘了 cur = cur->next;  → 无限循环
}

5. 对空链表操作未判空

对空链表调用 delete_node 或 print_list 时,如果没有在开头处理 head == NULL,可能崩溃。所有函数都对空链表保持友好是基本要求。


十二、小结

单向链表是最基础也最经典的动态数据结构。今天你学会了:

  • 用自引用结构体定义结点。
  • 头插、尾插、按值删除。
  • 遍历与查找。
  • 用递归或栈实现反向遍历。
  • 正确的内存释放步骤。
  • 为什么链表操作常使用二级指针。

链表的最大优点在于插入和删除不需要移动数据,代价是丧失了随机访问能力(找第 k 个元素要 O(n))。这种取舍决定了数组和链表在不同场景下的选择。

单向链表只有一个方向的钩子,有时会带来不便——比如删尾部结点要遍历整个链表找到前驱。下一篇我们将引入双向链表和循环链表,让每个结点同时知道前驱和后继,进一步拓展数据结构的灵活性。结合 void* 和函数指针,我们还能写出真正的通用链表库,让一套代码支持任意数据类型。


课后小练习

  1. 实现一个函数 int list_length(Node *head),返回链表长度。
  2. 实现一个函数 Node *get_node_at(Node *head, int index),返回第 index 个结点(从 0 开始),不存在返回 NULL。
  3. 实现一个函数 void reverse_list(Node **head),原地反转单向链表。提示:需要三个指针,逐步改变链接方向。
  4. (小挑战)基于今天这个 Node 定义,实现一个函数 int has_cycle(Node *head),判断链表是否有环(即某个结点的 next 指回了前面的结点)。提示:用快慢指针(Floyd 判圈法)。

我们下期见!

💡获取本系列示例代码请访问 GitCode 仓库。

29、掌握Python数据结构:深入解析与实战应用 本文深入解析了Python中常用的内置数据结构,包括列表、元组、字典和集合,并探讨了它们的高级应用。文章还介绍了栈、队列、链表、树和图等复杂数据结构的实现方法,以及常见的搜索和排序算法。最后通过实际案例分析,展示了如何将这些数据结构应用于缓存系统、社交网络和文本处理等领域,帮助开发者编写高效且可维护的代码。 阅读详情

相关推荐

29、探索编程世界:从基础到高级

本文从编程的基础概念入手,逐步深入探讨了数据结构、算法、面向对象编程、网络编程、数据库编程、并发编程以及编程中的安全与隐私等多个方面。无论是初学者还是有一定经验的开发者,都能从中获得新的知识和技能,帮助提升编程能力,成为一名优秀的程序员。

prometheus5watch的博客 250

C语言结构体指针:从内存管理到动态数据结构的高效实现

在C语言编程中,结构体(struct)是一种将不同类型数据组合成单一实体的复合数据类型,它解决了数据碎片化管理的问题,使代码组织更清晰。其核心原理在于内存布局的连续性和成员访问的偏移量计算。结构体的技术价值在于提升数据组织的逻辑性和封装性,是构建复杂数据模型的基础。在实际应用中,结构体常用于定义记录、配置参数和网络协议包等场景。然而,当涉及高效函数传参、动态内存分配以及构建链表、树等非线性数据结构时,直接操作结构体变量会带来巨大的性能开销和灵活性限制。此时,结构体指针(指向结构体的指针)成为关键技术,它通过

weixin_30613343的博客 558

C语言实战:从零构建自习室管理系统,掌握数据结构与文件操作

在计算机编程领域,数据结构与文件操作是构建任何实用软件系统的两大基石。数据结构决定了程序如何高效组织和管理内存中的数据,而文件操作则实现了数据的持久化存储,确保信息在程序关闭后不会丢失。理解这两者的原理,对于开发健壮、可靠的应用程序具有核心价值。以C语言为例,其接近硬件的特性让开发者能深入理解内存管理和数据组织的本质,通过结构体、数组、链表等基础数据结构,可以构建出复杂的数据模型。这种能力在资源管理、状态跟踪等应用场景中尤为重要。本文以自习室管理系统为具体案例,详细解析了如何运用C语言的结构体定义实体、使用

weixin_30443075的博客 418

50个经典C语言题目

一些简单的常识性问题

Jonathan的博客 581

Mac新手必看:用Homebrew安装wget的保姆级教程,附常见报错一键修复

本文为Mac新手提供使用Homebrew安装wget的详细教程,涵盖从Homebrew安装到wget基础使用的全流程,并附常见报错一键修复方案。通过本指南,用户可轻松掌握命令行下载工具wget的安装与使用技巧,解决Mac环境下资源下载的各种问题。

weixin_30635053的博客 365

Spring AI + RAG 构建电商智能客服:从 PDF 文档解析到精准问答的全链路实战

核心框架:向量数据库:- PostgreSQL + pgvector (生产推荐)- Redis Stack (轻量级方案)- ChromaDB (快速原型)文档解析:- Apache PDFBox: PDF 文本提取- Spring AI Document Readers: 结构化解析大模型:- 阿里云百炼 (DashScope)- 本地部署:Qwen/ChatGLM/*** 电商领域实体*/@Data@Builder@ToString/*** 商品类型。

一起修行,不孤单。这里有编程语言、开发工具、学习方法论和踩坑笔记。用简单的话说复杂的事,陪你从小白到进阶。周更干货,欢迎一起成长! 379

C语言版数据结构详解与实现

C语言版数据结构详解与实现大家好,我是微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们来深入探讨C语言中数据结构的详细实现及其应用。数据结构概述数据结构是计算机存储、组织数据的方式,涉及到如何在计算机中组织和存储数据以便有效使用的问题。常见的数据结构包括数组、链表、栈、队列、树、图等。数组数组是一...

weixin_42299310的博客 135

SQL Prompt 9.0插件突然无法使用解决办法(附防联网激活)多图+文字

可能由于Redgate公司最近抓盗版抓得严,导致激活后的SQL Prompt联网后突然无法使用,无论卸载重装都没办法继续使用,本人同时使用SQL Assistant和SQL Prompt,我喜欢SQL Assistant代码提示功能,而SQL Prompt我喜欢将查询结果复制后可以直接生产插入脚本,这是最爽的一件事,即便对表有多么熟悉,没有这些插件,几行语法都要敲大半天,目前本人已经离不开这两个插...

骚团长的博客 1万+

程序员常用的几种数据结构在C语言中的详解与代码实践

在计算机科学和软件开发中,数据结构是对数据进行有效组织和管理的关键。它们不仅是算法设计的基础,也是优化程序性能的核心要素。本文将全面深入地介绍四种程序员常用的C语言数据结构:数组、链表、栈和队列,同时配以详细的代码示例,帮助读者理解和掌握这些基础数据结构的实现和应用。

极客代码 828

【SAP】ABAP——ALV显示后选择数据操作

在ALV显示后,可全选、全不选或者选择几条数据进行发邮件、更新、下单等等操作。 在user_command中的实现方式如下: 方式一: FORM user_command USING ucomm LIKE sy-ucomm selfield TYPE slis_selfield. DATA: lr_grid TYPE REF TO cl_gui_alv_grid. DATA: lt_rows TYPE lvc_t_row WITH

Mona_1220的博客 4090

硬件基础(4):(6)高精度ADC设计中模拟地与数字地的划分与连接

在实际 PCB 设计中,**接地与布线**对于获得干净的参考电压和高精度 ADC 测量至关重要。下面详细阐述**如何在硬件层面减少数字噪声耦合到模拟电路、保障参考电压的稳定性**。

FCZlll的博客 2642

C语言(谭浩强版本,主讲人:小甲鱼)P53-P58

结构体与共用体

Scxioi0的博客 482

Nest的限流方案

多维度限流(如 IP、用户 ID、接口级别、请求类型)可配置化(支持不同环境和接口动态配置)可观测性(接入日志、监控系统)高性能和分布式支持(Redis 限流器)与鉴权鉴权链路集成(结合 JWT、API Token)以下是一个“标准// 优先按用户ID限流,否则按IP在main.ts维度建议限流存储推荐 Redis 支持多实例限流 Key 设计支持 IP + 用户ID + 路由等多种维度限流策略精细化配置,接口粒度调整异常处理自定义响应体、日志告警观测性。

Bruce__taotao的博客 1196

30天C语言从零到项目实战:掌握编程核心,开启系统开发之路

编程语言是计算机科学的基础工具,其核心在于理解计算机如何执行指令和处理数据。C语言作为一门中级语言,兼具高级语言的结构化特性和接近硬件的控制能力,通过指针和内存管理等机制,让开发者能够深入理解程序在计算机底层的运行原理。这种对底层机制的掌握,为学习操作系统、编译原理等核心课程奠定了坚实基础,并在系统软件、嵌入式开发、高性能计算等领域具有不可替代的技术价值。本文围绕C语言学习路径,结合指针和内存管理等热词,提供一套从语法基础到项目实战的30天系统训练方案,帮助初学者构建扎实的编程思维和工程实践能力。

weixin_34389926的博客 353

实战解析:从Innovus到Calibre的LVS错误排查全流程

本文详细解析了从Innovus到Calibre的LVS错误排查全流程,涵盖物理验证关键步骤与工具链协同。通过实战案例分享,重点解决器件不匹配、连接性错误等常见问题,并提供GDS导出规范、网表修正策略等实用技巧,帮助工程师高效完成芯片设计物理验证。

9o8p7i6u5y的博客 1034

脑电信号处理实战 01 | 从 0 跑通第一个 EEG 分析:加载、滤波、频谱与“睁眼闭眼“

本文以 UCI 公开数据集"EEG Eye State"为例,从零走通脑电处理的完整流水线——数据加载、1–40 Hz 带通滤波、Welch 功率谱、频带功率、通道相关性与箱线图质控、睁闭眼对比和时频分析,并用 8 张科研风格图层层揭示这份数据中的伪迹与通道重复问题,说明"流程跑得通不等于结果可信,质量控制永远先行"。

qq_53529450的博客 112

C语言复杂strust多重数据排序二分查找算法分析

在C语言中,复杂的 struct 数据结构中使用二分查找是有条件的。具体来说,二分查找的前提条件是数据必须是有序的,这通常是针对数组或者链表等线性数据结构中的数值或特定的字段。如果你的 struct 数据结构是一个数组,并且你希望基于某个特定的字段进行查找,那么可以使用二分查找。条件和步骤有序性: 首先,你的 struct...

cnzzs的博客 870

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

ZOJ-1056

贪吃蛇模拟,链表的运用,用C硬写的。。感觉JAVA的LinkedList应该会简单

xhldtc的专栏 528

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C 语言学习坚持百日基本功-动态内存管理-29

C语言标准库(`stdlib.h`)提供了4个核心函数实现动态内存管理:`malloc`、`calloc`、`realloc`、`free`,部分编译器还扩展了`alloca`(栈上动态分配),本文重点讲解标准函数。4. **使用`sizeof`计算类型大小**:避免硬编码字节数(如`malloc(5*sizeof(int))`而非`malloc(20)`);- **参数**:`ptr`为已分配的内存指针(`malloc/calloc/realloc`返回的指针),`new_size`为新的字节数;

寒寒豆豆软件公司的博客 436

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

嵌入式八股文总结(C语言篇)

最近正在准备秋招,在此每天记录一些面试常问的八股文,希望可以和大家一起进步。(持续更新中……)

小海螺的博客 3万+

知识点总结报告 1.25

知识点:链表 链表是一种动态数据结构,相较于数组,具有节省内存空间的作用,但链表在内存中的地址不一定是连续的。链表有单向链表,双向链表以及环形链表等,今天总结单向链表。 单项链表包含一个数据域和一个指针域。在定义结构体时不能嵌套结构体本身但可以包含指向本结构体的指针。这是因为指针的长度都是四个字节不受指向类型限制。指针域用于指向下一个链表的地址,头指针很重要,如果丢失了那么整个链表都会丢失。尾...

weixin_34059951的博客 163

学生信息管理系统

链表数据结构的创建、遍历、插入、删除操作;指针和动态内存管理的malloc/free使用;结构体和枚举类型的自定义应用;文件I/O操作包括二进制(fread/fwrite)和文本(fscanf/fprintf)文件的读写;模块化编程的头文件设计和函数封装;用户交互的菜单设计和输入验证;完整实现了学生信息的增删改查及持久化存储功能。

2502_94085702的博客 934

C语言30天速通:零基础到项目实战与就业接单指南

C语言作为一门经典的编程语言,其核心价值在于提供了对计算机底层内存和硬件的直接控制能力,是理解计算机系统原理和进行系统级开发的基石。从语法基础到指针、内存管理等核心概念,其学习路径遵循从基础到进阶的工程实践逻辑。掌握C语言不仅能构建扎实的编程思维,更是进入嵌入式开发、操作系统、高性能计算等核心领域的关键。对于初学者而言,一套结构清晰、注重实战的学习方案至关重要。本文聚焦于【30天速通】与【实战项目】两大核心,提供了一条从零基础到具备完成控制台应用、管理文件数据、进行动态内存分配等能力的系统化路径。该路径通过

weixin_30820077的博客 422

C语言指针学习的第一天

学习指针的第二天

ouynagda的博客 354

【架构师总结】30天C语言入门及5个项目实战教学大纲规划02

3️⃣ 尝试修改Hello World程序输出彩色文字(使用。,专为Visual Studio 2022优化,整合。1️⃣ 安装VS2022(需勾选“C++桌面开发”)以下是为零基础学习者定制的。

老韩的Linux云计算架构师进阶之路 1165
上一篇: 28. 【C语言】通用数据操作:`void *` 与类型无关编程
下一篇: 30. 【C语言】链表进阶:双向链表与循环链表
gugucoding
博客等级 码龄140天 7粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值