《线性表、顺序表与链表》教案(C语言版本)

news2025/4/15 13:13:12

🌟 各位看官好,我是maomi_9526

🌍 种一棵树最好是十年前,其次是现在!

🚀 今天来学习C语言的相关知识。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

目录

教学目标

教学重点与难点

教学方法

教学大纲

1. 线性表(Linear List)

1. 线性表的定义

2. 线性表的基本特性

3. 线性表的常见实现方式

3.1 顺序存储(顺序表)

3.2 链式存储(链表)

4. 顺序表与链表的比较

5. 顺序表操作

6.链表基本操作

2. 顺序表(Sequence Table)

2.1 静态顺序表与动态顺序表

3. 顺序表 vs 链表

对比分析

课堂练习

作业设计

教学总结


教学目标
  1. 理解线性表的逻辑结构和物理实现方式。

  2. 掌握顺序表和链表的实现原理及核心操作。

  3. 能够通过代码实现顺序表和链表的基本功能。

  4. 分析顺序表与链表的性能差异及适用场景。


教学重点与难点
  • 重点:顺序表的动态增容、链表的指针操作、时间复杂度分析。

  • 难点:链表节点的插入与删除逻辑、顺序表与链表的性能权衡。


教学方法
  • 理论讲解 + 代码演示 + 对比分析 + 课堂练习


教学大纲


1. 线性表(Linear List)

1. 线性表的定义
  • 线性表:线性表是由n个具有相同特性的元素组成的有限序列,元素之间有明确的前后关系。每个元素有唯一的前驱和后继元素。线性表可以是顺序存储链式存储

    • 逻辑结构:线性表是逻辑上的顺序排列,指的是元素之间的关系是依次排列的,每个元素都有一个明确的前后关系。

    • 物理结构:线性表的元素在计算机内存中的存储方式,可以是连续的(顺序表)或者不连续的(链表)。

2. 线性表的基本特性
  • 元素的顺序性:线性表中元素的顺序关系决定了元素之间的前后依赖关系,通常我们按从第一个元素到最后一个元素的顺序进行访问。

  • 唯一性:每个元素在序列中有唯一的前驱和后继元素,只有第一个元素没有前驱,最后一个元素没有后继。

  • 有限性:线性表包含一个有限的元素集合,且元素数量固定。


3. 线性表的常见实现方式

线性表有两种常见的物理存储方式:顺序存储链式存储

3.1 顺序存储(顺序表)

顺序存储使用一段连续的内存空间存储数据,通常使用数组来实现。数据元素在内存中的地址是连续的,因此可以通过索引来直接访问。

  • 优点

    • 高效的随机访问:可以通过索引直接访问任意位置的元素,时间复杂度为 O(1)。

    • 空间效率高:在内存中是连续存储,相对来说能够更高效地利用内存。

  • 缺点

    • 插入和删除效率较低:在顺序表中间插入或删除元素时,需要移动大量的元素,时间复杂度为 O(N)。

    • 固定容量问题:顺序表的容量一旦固定,后期无法动态调整空间,需要使用动态扩容,但扩容可能导致空间浪费。

3.2 链式存储(链表)

链表使用非连续的内存空间存储数据,每个元素包含一个数据域和一个指向下一个元素的指针(或者同时包含指向前后节点的指针),因此数据元素的地址不一定是连续的。

  • 优点

    • 插入和删除效率高:链表只需要调整指针即可完成插入和删除操作,时间复杂度为 O(1)(不需要移动元素)。

    • 动态内存分配:链表不需要预先定义大小,内存空间可以根据实际需求动态分配,避免了空间浪费问题。

  • 缺点

    • 随机访问效率低:访问链表中的元素需要从头节点开始依次遍历,时间复杂度为 O(N)。

    • 指针开销:每个节点需要额外的指针空间存储前驱或后继元素。


4. 顺序表与链表的比较

顺序表和链表是两种常见的线性表实现方式,它们的优缺点适用于不同的应用场景。

特性顺序表链表
存储方式使用连续的内存块(数组)存储元素使用不连续的内存块(通过指针链接)
随机访问O(1)O(N)
插入/删除操作O(N)(需要移动元素)O(1)(只需修改指针)
扩容/缩容动态扩容(可能浪费空间)不需要扩容,按需分配空间
内存空间使用存储空间固定,可能造成浪费每个节点动态分配内存
应用场景适合频繁访问的场景适合频繁插入和删除的场景
内存开销低,只有存储元素的数据较高,每个节点还需要存储指针

5. 顺序表操作
  • 初始化:创建一个顺序表,设定初始容量。

  • 插入:将一个元素插入到指定位置,涉及移动元素。

  • 删除:删除指定位置的元素,涉及移动元素。

  • 查找:根据索引访问元素。

  • 扩容:当顺序表的空间不足时,需要扩容并将现有数据复制到新数组中。

 代码示例(插入与删除):

// 在位置index插入元素
void insert(DynamicArray* arr, int index, int value) {
    if (index < 0 || index > arr->size) {
        printf("Index out of bounds\n");
        return;
    }
    if (arr->size == arr->capacity) {
        resize(arr, 2 * arr->capacity);
    }
    // 元素后移
    for (int i = arr->size; i > index; i--) {
        arr->array[i] = arr->array[i - 1];
    }
    arr->array[index] = value;
    arr->size++;
}

// 删除位置index的元素
void delete(DynamicArray* arr, int index) {
    if (index < 0 || index >= arr->size) {
        printf("Index out of bounds\n");
        return;
    }
    // 元素前移
    for (int i = index; i < arr->size - 1; i++) {
        arr->array[i] = arr->array[i + 1];
    }
    arr->size--;
}
6.链表基本操作
  • 初始化:创建一个空链表,通常使用虚拟头节点(dummy node)来简化操作。

  • 插入:在指定位置插入一个元素,修改指针链接。

  • 删除:删除指定位置的元素,修改指针链接。

  • 查找:遍历链表查找指定元素。

  • 反转:将链表的元素顺序反转,调整指针的指向。

代码示例(单向链表):

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

struct ListNode {
    int val;
    struct ListNode* next;
};

// 创建链表的头插法
struct ListNode* create_linked_list_head(int* values, int size) {
    struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->next = NULL;
    for (int i = 0; i < size; i++) {
        struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
        new_node->val = values[i];
        new_node->next = dummy->next;
        dummy->next = new_node;
    }
    return dummy->next;
}

// 创建链表的尾插法
struct ListNode* create_linked_list_tail(int* values, int size) {
    struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = dummy;
    for (int i = 0; i < size; i++) {
        struct ListNode* new_node = (struct ListNode*)malloc(sizeof(struct ListNode));
        new_node->val = values[i];
        tail->next = new_node;
        tail = tail->next;
    }
    return dummy->next;
}

void print_linked_list(struct ListNode* head) {
    struct ListNode* current = head;
    while (current != NULL) {
        printf("%d ", current->val);
        current = current->next;
    }
    printf("\n");
}

int main() {
    int values[] = {1, 2, 3};
    struct ListNode* head = create_linked_list_tail(values, 3);
    print_linked_list(head);  // 输出:1 2 3
    return 0;
}

代码示例(反转链表、删除节点):

// 反转链表
struct ListNode* reverse_linked_list(struct ListNode* head) {
    struct ListNode* prev = NULL;
    struct ListNode* curr = head;
    while (curr != NULL) {
        struct ListNode* next_node = curr->next;
        curr->next = prev;
        prev = curr;
        curr = next_node;
    }
    return prev;
}

// 删除链表中所有值为val的节点
struct ListNode* remove_elements(struct ListNode* head, int val) {
    struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummy->next = head;
    struct ListNode* current = dummy;
    while (current->next != NULL) {
        if (current->next->val == val) {
            struct ListNode* temp = current->next;
            current->next = temp->next;
            free(temp);
        } else {
            current = current->next;
        }
    }
    return dummy->next;
}

6.代码示例(C语言):

#include <stdio.h>

#define MAX_SIZE 10

// 顺序表(数组实现)
int linear_list[MAX_SIZE] = {1, 2, 3, 4, 5};

// 链表(链式存储)
struct Node {
    int data;
    struct Node* next;
};

void print_linear_list() {
    for (int i = 0; i < 5; i++) {
        printf("%d ", linear_list[i]);
    }
    printf("\n");
}

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

int main() {
    print_linear_list();  // 输出线性表
    return 0;
}

2. 顺序表(Sequence Table)

2.1 静态顺序表与动态顺序表
  1. 静态顺序表(Static Sequence Table)

    • 定义:静态顺序表是使用固定大小的数组来存储数据元素。其大小在编译时就已经确定,运行时不能更改。

    • 特点

      • 大小固定,一旦定义了数组的大小,就无法动态调整。

      • 存储元素的内存地址在编译时就被分配好了,内存空间是连续的。

      • 适合数据量已知并且不会频繁变化的场景。

    • 优点

      • 存储效率高,访问元素的时间复杂度为O(1)。

    • 缺点

      • 固定容量可能会造成内存浪费或空间不足的情况。

      • 插入和删除元素时可能会产生大量的数据搬移,时间复杂度为O(N)。

    示例(C语言)

    #include <stdio.h>
    
    #define MAX_SIZE 10
    
    int static_array[MAX_SIZE] = {1, 2, 3, 4, 5};
    
    void print_static_array() {
        for (int i = 0; i < 5; i++) {
            printf("%d ", static_array[i]);
        }
        printf("\n");
    }
    
    int main() {
        print_static_array();  // 输出:1 2 3 4 5
        return 0;
    }
    
  2. 动态顺序表(Dynamic Sequence Table)

    • 定义:动态顺序表是一个支持动态扩容的数组。当数组的存储空间不足时,系统会自动分配更大的内存空间,并将原有元素复制到新数组中。

    • 特点

      • 容量是动态可调的,通常根据需要扩展数组的容量(例如扩容为原来的2倍)。

      • 数据的内存地址可能会发生变化,因为重新分配了更大的内存空间。

      • 适合数据量不确定或需要经常变化的场景。

    • 优点

      • 容量可动态增长,不会浪费内存空间。

      • 避免了固定容量数组的空间不足问题。

    • 缺点

      • 扩容操作可能会引起性能下降,尤其是在频繁扩容时,需要进行内存的重新分配和数据的搬移。

    示例(C语言实现动态顺序表)

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct {
        int* array;     // 动态分配的数组
        int size;       // 当前元素个数
        int capacity;   // 数组容量
    } DynamicArray;
    
    // 初始化动态数组
    void init(DynamicArray* arr) {
        arr->capacity = 2;  // 初始容量为2
        arr->size = 0;
        arr->array = (int*)malloc(arr->capacity * sizeof(int));
    }
    
    // 扩容:将数组容量翻倍
    void resize(DynamicArray* arr, int new_capacity) {
        arr->array = (int*)realloc(arr->array, new_capacity * sizeof(int));
        arr->capacity = new_capacity;
    }
    
    // 向动态数组中添加元素
    void append(DynamicArray* arr, int value) {
        if (arr->size == arr->capacity) {
            // 扩容
            resize(arr, 2 * arr->capacity);
        }
        arr->array[arr->size] = value;
        arr->size++;
    }
    
    // 打印数组内容
    void print_array(DynamicArray* arr) {
        for (int i = 0; i < arr->size; i++) {
            printf("%d ", arr->array[i]);
        }
        printf("\n");
    }
    
    // 释放动态数组内存
    void free_array(DynamicArray* arr) {
        free(arr->array);
    }
    
    int main() {
        DynamicArray arr;
        init(&arr);
    
        // 添加元素
        append(&arr, 1);
        append(&arr, 2);
        append(&arr, 3);  // 扩容时容量变为4
        append(&arr, 4);
    
        printf("动态顺序表的内容:\n");
        print_array(&arr);  // 输出:1 2 3 4
    
        free_array(&arr);   // 释放内存
        return 0;
    }
    
  • 静态顺序表:内存空间是固定的,适用于数据量确定的情况,访问效率高,但插入和删除操作可能需要大量的数据搬移,且扩容困难。

  • 动态顺序表:内存空间是可扩展的,适用于数据量不确定或变化较大的情况,动态扩容避免了固定容量带来的问题,但在扩容时可能会影响性能。


3. 顺序表 vs 链表

对比分析
特性顺序表链表
存储方式连续内存离散内存,通过指针链接
随机访问O(1)O(N)
插入/删除O(N)(需移动元素)O(1)(只需调整指针)
空间管理动态扩容可能浪费空间按需分配,无空间浪费
缓存局部性高(连续存储)低(节点分散)

课堂练习
  1. 顺序表练习:实现一个函数,删除顺序表中所有等于给定值的元素。

  2. 链表练习:合并两个有序链表,返回新的有序链表头节点。


作业设计

  1. 编码题

    • 实现动态顺序表的缩容功能(当元素数量小于容量的1/4时,容量减半)。

    • 实现双向链表的插入和删除操作。

  2. 分析题

    • 分析顺序表动态扩容均摊时间复杂度为何是O(1)。

    • 对比单向链表和双向链表在删除尾节点时的时间复杂度差异。


教学总结

通过代码实现和对比分析,学生应掌握以下内容:

  1. 顺序表和链表的实现原理及适用场景。

  2. 动态扩容的策略与时间复杂度分析。

  3. 链表指针操作的逻辑与常见算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[ctfshow web入门] web33

信息收集 相较于上一题&#xff0c;这题多了双引号的过滤。我猜测这一题的主要目的可能是为了不让使用$_GET[a]之类的语句&#xff0c;但是$_GET[a]也是一样的 没有括号可以使用include&#xff0c;没有引号可以使用$_GET 可以参考[ctfshow web入门] web32&#xff0c;其中的所…

三、TorchRec中的Optimizer

TorchRec中的Optimizer 文章目录 TorchRec中的Optimizer前言一、嵌入后向传递与稀疏优化器融合如下图所示&#xff1a;二、上述图片的关键步骤讲解&#xff1a;三、优势四、与传统优化器对比总结 前言 TorchRec 模块提供了一个无缝 API&#xff0c;用于在训练中融合后向传递和…

webrtc pacer模块(一) 平滑处理的实现

Pacer起到平滑码率的作用&#xff0c;使发送到网络上的码率稳定。如下的这张创建Pacer的流程图&#xff0c;其中PacerSender就是Pacer&#xff0c;其中PacerSender就是Pacer。这篇文章介绍它的核心子类PacingController及Periodic模式下平滑处理的基本流程。平滑处理流程中还有…

河北工程大学e2e平台,python

题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 联系&#x1f6f0;&#xff1a;18039589633

BeautifulSoup 踩坑笔记:SVG 显示异常的真正原因

“这图是不是糊了&#xff1f;”以为是样式缺了&#xff1f;试试手动复制差异在哪&#xff1f;想用对比工具一探究竟……简单到不能再简单的代码&#xff0c;有问题吗&#xff1f;最后的真相&#xff1a;viewBox vs viewbox&#xff0c;preserveAspectRatio vs preserveaspectr…

【browser-use+deepseek】实现简单的web-ui自动化

browser-use Web-UI 一、browser-use是什么 Browser Use 是一款开源Python库&#xff0c;专为大语言模型设计的智能浏览器工具&#xff0c;目的是让 AI 能够像人类一样自然地浏览和操作网页。它支持多标签页管理、视觉识别、内容提取&#xff0c;并能记录和重复执行特定动作。…

MOS管的发热原因和解决办法

发热来源 如上图&#xff0c;MOS管的工作状态有4种情况&#xff0c;分别是开通过程&#xff0c;导通过程&#xff0c;关断过程和截止过程。 导致发热的损耗主要有两种&#xff1a;开关损耗、导通损耗。 导通损耗 导通损耗比较好计算&#xff0c;根据驱动电压VGS值可以得到MOS…

科技项目验收测试怎么做?验收测试报告如何获取?

科技项目从研发到上市需要一个很长的周期&#xff0c;并且在上市之前还有一个至关重要的交付过程&#xff0c;那就是项目验收&#xff0c;验收需要通过验收测试来呈现。科技项目验收测试是确保项目成功交付的关键步骤&#xff0c;那么是如何进行的呢?企事业单位想要获取科技项…

AutoEval:现实世界中通才机器人操作策略的自主评估

25年3月来自 UC Berkeley 和 Nvidia 的论文“AutoEval: Autonomous Evaluation of Generalist Robot Manipulation Policies in the Real World”。 可规模化且可复现的策略评估一直是机器人学习领域长期存在的挑战。评估对于评估进展和构建更优策略至关重要&#xff0c;但在现…

基于SSM框架的房屋租赁小程序开发与实现

概述 一个基于SSM框架开发的微信小程序房屋租赁管理系统&#xff0c;该项目实现了用户管理、中介管理、房源信息管理等核心功能。 主要内容 一、管理员模块功能实现 ​​用户管理​​ 管理员可对通过微信小程序注册的用户信息进行修改和删除操作&#xff0c;确保用户数据的准…

oracle 表空间(Tablespace)

在 Oracle 11g 中&#xff0c;表空间&#xff08;Tablespace&#xff09; 是数据库存储架构的核心逻辑单元&#xff0c;其原理基于 逻辑存储与物理存储的分离&#xff0c;通过分层管理数据文件、段&#xff08;Segment&#xff09;、区&#xff08;Extent&#xff09;和数据块&…

基于YOLOv8的机场跑道异物检测识别系统:提升航空安全的新一代解决方案(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​ ​​​​​​​​​ ​​ 1. 机场跑道异物检测领域概述 机场跑道异物(Foreign Object Debris, FOD)是指存在于机场跑道、滑行道等关…

Android学习总结之OKHttp拦截器和缓存

深入理解 OkHttp 拦截器 1. 拦截器接口详解 Interceptor 接口是自定义拦截器的基础&#xff0c;它仅包含一个抽象方法 intercept。以下是对该方法参数和返回值的详细解释&#xff1a; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; import…

Wincc管对象的使用

Wincc管对象的使用 管对象的调用多边形管T形管双T形管管弯头管道大小调整 管对象的调用 打开【图形编辑器】 多边形管 多边形管如下&#xff1a; 一根管子的顶点数是两个&#xff0c;如果修改顶点数&#xff0c;管子就有多少个端点。 修改顶点数为5 此时点击端点然后拖动&#…

Linux-----驱动

一、内核驱动与启动流程 1. Linux内核驱动 Nor Flash: 可线性访问&#xff0c;有专门的数据及地址总线&#xff08;与内存访问方式相同&#xff09;。 Nand Flash: 不可线性访问&#xff0c;访问需要控制逻辑&#xff08;软件&#xff09;。 2. Linux启动流程 ARM架构: IRAM…

问问lua怎么写DeepSeek,,,,,

很坦白说&#xff0c;这十年&#xff0c;我几乎没办法从互联网找到这个这样的代码&#xff0c;互联网引擎找不到&#xff0c;我也没有很大的“追求”要传承&#xff0c;或者要宣传什么&#xff1b;直到DeepSeek的出现 兄弟&#xff0c;Deepseek现在已经比你更了解你楼下的超市…

基于神经环路的神经调控可增强遗忘型轻度认知障碍患者的延迟回忆能力

简要总结 这篇文章提出了一种名为CcSi-MHAHGEL的框架&#xff0c;用于基于多站点、多图谱fMRI的功能连接网络&#xff08;FCN&#xff09;分析&#xff0c;以辅助自闭症谱系障碍&#xff08;ASD&#xff09;的识别。该框架通过多视图超边感知的超图嵌入学习方法&#xff0c;整合…

C++学习之ORACLE③

1.集合运算符 查询部门号是10和20的员工信息&#xff1a; &#xff1f;思考有几种方式解决该问题 &#xff1f; SQL> select * from emp where deptno in(10, 20) SQL> select * from emp where deptno10 or deptno20 集合运算&#xff1a; Select * from emp …

UniAD:自动驾驶的统一架构 - 创新与挑战并存

引言 自动驾驶技术正经历一场架构革命。传统上&#xff0c;自动驾驶系统采用模块化设计&#xff0c;将感知、预测和规划分离为独立组件。而上海人工智能实验室的OpenDriveLab团队提出的UniAD&#xff08;Unified Autonomous Driving&#xff09;则尝试将这些任务整合到一个统一…

transformers 中的 input_ids 和 labels 是什么

transformers 中的 input_ids 和 labels 是什么 input_ids 是输入文本的数字化表示,而 labels 是模型训练的目标值 在自然语言处理(NLP)和使用 transformers 库进行模型训练时,tokenizer = AutoTokenizer.from_pretrained(model_path) 这行代码是用于从预训练模型路径加载…