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

数据结构:线性表

📌目录

  • 🔗 一,线性表的定义与特点
  • 📝 二,案例引入
  • 📋 三,线性表的类型定义
  • 📊 四,线性表的顺序表示与实现
    • (一)线性表的顺序存储表示
    • (二)顺序表中基本操作的实现
  • 🔗 五,线性表的链式表示与实现
    • (一)单链表的定义与表示
    • (二)单链表基本操作的实现
    • (三)循环链表
  • ⚖️ 六,顺序表和链表的比较
    • (一)空间性能的比较
    • (二)时间性能的比较
  • 💡 七,线性表的应用
    • (一)线性表的合并
    • (二)有序表的合并
  • 🛠️ 案例分析与实现


在这里插入图片描述

🔗 一,线性表的定义与特点

线性表是最基本、最常用的数据结构之一,其定义可概括为:由n(n≥0)个具有相同特性的数据元素组成的有限序列

核心特点包括:

  • 有序性:元素之间存在明确的先后顺序,第一个元素无前驱,最后一个元素无后继,其余元素有且仅有一个前驱和一个后继(如数组[1,2,3]中,2的前驱是1,后继是3)。
  • 同质性:所有元素属于同一数据类型(如整数列表、字符串列表)。
  • 有限性:元素个数n为非负整数,当n=0时称为“空表”。

线性表是现实世界中“序列关系”的抽象,例如学生点名册(按学号排序)、购物清单(按添加顺序)等均符合其特征。

📝 二,案例引入

假设学校教务处需要管理1000名学生的成绩信息,要求支持以下操作:

  1. 查看第500名学生的成绩;
  2. 在第300名学生后插入一名转学生的成绩;
  3. 删除第100名学生的成绩(该生退学);
  4. 将所有成绩按从高到低排序。

若采用顺序表(数组)存储,查看操作可直接通过下标完成(高效),但插入/删除需移动大量元素(低效);若采用链表存储,插入/删除只需修改指针(高效),但查看第500名学生需从头遍历(低效)。

这个案例揭示了线性表两种存储结构的核心差异——选择合适的实现方式,直接影响程序的效率。

📋 三,线性表的类型定义

线性表的抽象数据类型(ADT)定义如下,包含数据集合及核心操作:

ADT List {数据:线性表L = (a₁, a₂, ..., aₙ),其中aᵢ为数据元素,i为位序(1≤i≤n)操作:1. InitList(&L):初始化空表L2. ListLength(L):返回表L的长度n3. GetElem(L, i, &e):用e返回L中第i个元素4. LocateElem(L, e):返回e在L中首次出现的位序(若不存在返回0)5. ListInsert(&L, i, e):在L的第i个位置插入元素e6. ListDelete(&L, i, &e):删除L的第i个元素,用e返回其值7. PrintList(L):遍历并输出L中所有元素8. DestroyList(&L):销毁表L,释放内存
}

这些操作构成了线性表的基本功能集,具体实现需结合存储结构设计。

📊 四,线性表的顺序表示与实现

(一)线性表的顺序存储表示

顺序表是线性表的顺序存储结构,其核心思想是:用一段连续的内存空间依次存储线性表的元素,类似数组。

  • 存储原理:假设每个元素占用size字节,第一个元素地址为LOC(a₁),则第i个元素的地址为:
    LOC(aᵢ) = LOC(a₁) + (i-1)×size
    即通过“基地址+偏移量”可直接访问任意元素,称为“随机存取”特性。

  • 代码表示(C语言):

    #define MAXSIZE 100  // 最大容量
    typedef int ElemType; // 元素类型(示例为int)
    typedef struct {ElemType data[MAXSIZE]; // 存储元素的数组int length;             // 当前长度(≤MAXSIZE)
    } SqList; // 顺序表类型
    

(二)顺序表中基本操作的实现

  1. 初始化

    void InitList(SqList *L) {L->length = 0; // 空表长度为0
    }
    

    时间复杂度:O(1)

  2. 插入操作(在第i个位置插入e):

    // 步骤:1. 检查i的合法性(1≤i≤length+1);2. 从后往前移动元素;3. 插入e并更新长度
    int ListInsert(SqList *L, int i, ElemType e) {if (i < 1 || i > L->length + 1 || L->length == MAXSIZE)return 0; // 插入失败for (int j = L->length; j >= i; j--) {L->data[j] = L->data[j-1]; // 元素后移}L->data[i-1] = e; // 插入元素(数组下标从0开始)L->length++;return 1; // 插入成功
    }
    

    时间复杂度:最坏情况(插入到表头)需移动n个元素,故为O(n);平均复杂度O(n)。

  3. 删除操作(删除第i个元素,用e返回):

    // 步骤:1. 检查i的合法性(1≤i≤length);2. 保存被删元素;3. 从前往后移动元素;4. 更新长度
    int ListDelete(SqList *L, int i, ElemType *e) {if (i < 1 || i > L->length)return 0; // 删除失败*e = L->data[i-1]; // 保存被删元素for (int j = i; j < L->length; j++) {L->data[j-1] = L->data[j]; // 元素前移}L->length--;return 1; // 删除成功
    }
    

    时间复杂度:同插入操作,最坏与平均均为O(n)。

  4. 按位查找

    ElemType GetElem(SqList L, int i) {return L.data[i-1]; // 直接通过下标访问
    }
    

    时间复杂度:O(1)(随机存取的优势)。

🔗 五,线性表的链式表示与实现

(一)单链表的定义与表示

单链表是线性表的链式存储结构,其核心思想是:用离散的内存块(节点)存储元素,节点间通过指针连接形成序列

  • 节点结构:每个节点包含两部分:

    • 数据域:存储元素值;
    • 指针域:存储下一个节点的地址(或NULL表示末尾)。
  • 代码表示(C语言):

    typedef int ElemType;
    typedef struct LNode {ElemType data;          // 数据域struct LNode *next;     // 指针域(指向后继节点)
    } LNode, *LinkList; // LinkList为指向LNode的指针类型
    
  • 链表的头指针:通常用LinkList L表示单链表,L=NULL时为空表;为方便操作,常增设头节点(不存储数据,仅作为链表起点),此时空表表示为L->next=NULL

(二)单链表基本操作的实现

  1. 初始化(带头节点)

    LinkList InitList() {LinkList L = (LNode*)malloc(sizeof(LNode)); // 分配头节点L->next = NULL; // 头节点指针域为NULLreturn L;
    }
    

    时间复杂度:O(1)

  2. 插入操作(在第i个节点前插入e):

    // 步骤:1. 找到第i-1个节点(前驱);2. 创建新节点;3. 新节点指针指向i节点;4. 前驱指针指向新节点
    int ListInsert(LinkList L, int i, ElemType e) {LNode *p = L;int j = 0;while (p && j < i-1) { // 寻找第i-1个节点p = p->next;j++;}if (!p || j > i-1) return 0; // i不合法LNode *s = (LNode*)malloc(sizeof(LNode)); // 创建新节点s->data = e;s->next = p->next; // 新节点指向i节点p->next = s;       // 前驱节点指向新节点return 1;
    }
    

    时间复杂度:最坏需遍历整个链表,故为O(n)。

  3. 删除操作(删除第i个节点,用e返回值):

    int ListDelete(LinkList L, int i, ElemType *e) {LNode *p = L;int j = 0;while (p->next && j < i-1) { // 寻找第i-1个节点p = p->next;j++;}if (!p->next || j > i-1) return 0; // i不合法LNode *q = p->next; // q指向待删除节点*e = q->data;       // 保存数据p->next = q->next;  // 前驱节点跳过qfree(q);            // 释放内存return 1;
    }
    

    时间复杂度:O(n)。

  4. 按位查找

    LNode* GetElem(LinkList L, int i) {LNode *p = L->next;int j = 1;while (p && j < i) { // 从第1个节点开始遍历p = p->next;j++;}return p; // 若i超出范围,返回NULL
    }
    

    时间复杂度:O(n)(需顺序遍历)。

(三)循环链表

循环链表是单链表的变种,其特点是:最后一个节点的指针域不指向NULL,而是指向头节点,形成环形结构。

  • 优势

    • 从任意节点出发可遍历整个链表(适合多端操作场景);
    • 判断链表结束的条件是“指针是否回到头节点”(p->next == L)。
  • 操作示例:合并两个循环链表(A和B):

    void MergeList(LinkList A, LinkList B) {LNode *p = A->next; // 保存A的第一个节点LNode *q = B->next; // 保存B的第一个节点A->next = q->next;  // A的头节点连接B的第一个节点free(q);            // 释放B的头节点B->next = p;        // B的尾节点连接A的第一个节点
    }
    

⚖️ 六,顺序表和链表的比较

(一)空间性能的比较

维度顺序表链表
存储空间需预先分配固定大小(可能浪费或溢出)动态分配,按需申请(无浪费)
存储密度1(数据域占100%空间)<1(需额外存储指针域)
内存碎片连续空间,碎片少离散分配,可能产生碎片

(二)时间性能的比较

操作顺序表链表结论
按位查找O(1)(随机存取)O(n)(顺序存取)顺序表更适合频繁查找场景
插入/删除O(n)(需移动元素)O(1)(仅改指针)链表更适合频繁插入/删除场景
遍历整个表O(n)O(n)效率相同
初始化O(1)O(1)效率相同

💡 七,线性表的应用

(一)线性表的合并

问题:将两个线性表A和B合并为新表C,要求C包含A和B的所有元素(允许重复)。

实现思路

  1. 初始化C为空表;
  2. 将A的所有元素依次插入C;
  3. 将B的所有元素依次插入C。

顺序表实现(时间复杂度O(n+m),n、m为A、B长度):

void MergeList(SqList A, SqList B, SqList *C) {int i = 0, j = 0, k = 0;while (i < A.length && j < B.length) {C->data[k++] = A.data[i++];C->data[k++] = B.data[j++];}// 处理剩余元素while (i < A.length) C->data[k++] = A.data[i++];while (j < B.length) C->data[k++] = B.data[j++];C->length = k;
}

(二)有序表的合并

问题:将两个非递减有序表A和B合并为一个新的非递减有序表C(去重)。

实现思路

  1. 初始化指针i(A的起点)、j(B的起点);
  2. 比较A[i]和B[j],将较小值插入C(若相等则只插一次);
  3. 处理剩余元素。

链表实现(时间复杂度O(n+m)):

LinkList MergeSortedList(LinkList A, LinkList B) {LinkList C = InitList(); // 初始化结果表LNode *p = A->next, *q = B->next, *r = C;while (p && q) {if (p->data < q->data) {r->next = p; r = p; p = p->next;} else if (p->data > q->data) {r->next = q; r = q; q = q->next;} else { // 去重:只保留一个r->next = p; r = p; p = p->next;LNode *temp = q; q = q->next; free(temp);}}r->next = p ? p : q; // 拼接剩余元素free(A); free(B); // 释放原表头节点return C;
}

🛠️ 案例分析与实现

问题:设计一个学生成绩管理系统,支持添加、删除、查询、排序功能。

方案选择

  • 若查询频繁(如按学号查成绩),选择顺序表(O(1)查找);
  • 若频繁添加/删除(如转学、退学),选择链表(O(1)插入/删除)。

核心代码(链表实现)

// 添加学生(尾插法)
void AddStudent(LinkList L, ElemType id, ElemType score) {LNode *p = L;while (p->next) p = p->next; // 找到尾节点LNode *s = (LNode*)malloc(sizeof(LNode));s->data = score; // 简化:data存储成绩,实际可扩展为结构体s->id = id;      // 新增id字段s->next = NULL;p->next = s;
}// 按学号查询成绩
ElemType QueryScore(LinkList L, ElemType id) {LNode *p = L->next;while (p && p->id != id) p = p->next;return p ? p->data : -1; // -1表示未找到
}

总结:线性表的两种实现各有优劣,实际开发中需根据业务场景的“操作频率”选择——查多改少用顺序表,改多查少用链表


文章转载自:
http://amperage.lbooon.cn
http://appraisingly.lbooon.cn
http://christchurch.lbooon.cn
http://aldebaran.lbooon.cn
http://anabolic.lbooon.cn
http://cavally.lbooon.cn
http://choroideremia.lbooon.cn
http://centre.lbooon.cn
http://backwoodsy.lbooon.cn
http://bellyfat.lbooon.cn
http://benzoline.lbooon.cn
http://acock.lbooon.cn
http://aquakinetics.lbooon.cn
http://carpus.lbooon.cn
http://cabezon.lbooon.cn
http://camille.lbooon.cn
http://barcarole.lbooon.cn
http://cherokee.lbooon.cn
http://alguazil.lbooon.cn
http://believer.lbooon.cn
http://cakewalk.lbooon.cn
http://american.lbooon.cn
http://arachis.lbooon.cn
http://anatolia.lbooon.cn
http://absorb.lbooon.cn
http://bisulfate.lbooon.cn
http://bedroom.lbooon.cn
http://assertative.lbooon.cn
http://caritas.lbooon.cn
http://cessionary.lbooon.cn
http://www.dtcms.com/a/280872.html

相关文章:

  • 服务器端安全检测与防御技术概述
  • BGP机房和传统机房之间都有哪些区别?
  • Sentinel热点参数限流完整示例实现
  • 力扣面试150题--排序链表
  • WebApplicationType.REACTIVE 的webSocket 多实例问题处理
  • MySQL数据库----约束
  • C# 构建动态查询表达式(含查询、排序、分页)
  • C语言基础第6天:分支循环
  • Ubuntu24 辅助系统-屏幕键盘的back按键在网页文本框删除不正常的问题解决方法
  • CentOS7 Docker安装MySQL全过程,包括配置远程连接账户
  • fastApi连接数据库
  • 如何正确分配及设置香港站群服务器IP?
  • 深入解析 Java AQS (AbstractQueuedSynchronizer) 的实现原理
  • LeetCode 3136.有效单词:遍历模拟
  • [实战] 基8 FFT/IFFT算法原理与实现(完整C代码)
  • 【每天一个知识点】多模态信息(Multimodal Information)
  • 【知识扫盲】tokenizer.json中的vocab和merges是什么?
  • 【机器学习】第二章 Python入门
  • 【Unity】MiniGame编辑器小游戏(十四)基础支持模块(游戏窗口、游戏对象、物理系统、动画系统、射线检测)
  • 数学中的教学思想
  • MySQL 8.0 OCP 1Z0-908 题目解析(24)
  • P3842 [TJOI2007] 线段
  • Sharding-JDBC 分布式事务实战指南:XA/Seata 方案解析
  • sqli-labs靶场通关笔记:第18-19关 HTTP头部注入
  • 【C++】初识C++(1)
  • 课题学习笔记1——文本问答与信息抽取关键技术研究论文阅读(用于无结构化文本问答的文本生成技术)
  • Java 大视界 -- Java 大数据机器学习模型在金融风险传染路径分析与防控策略制定中的应用(347)
  • QT——QList的详细讲解
  • Redis的下载安装+基础操作+redis客户端的安装
  • 使用 1Panel PHP 运行环境部署 WordPress