串(字符串)和数组是数据结构中的两个重要分支,它们在程序设计中承担着不同但互补的角色。串专门处理字符数据,而数组则提供了多维数据的存储和访问机制。本文将深入探讨这两种数据结构的理论基础、实现方法和核心算法。

文章目录

  • 1.串的基本概念
    • 串的抽象数据类型定义
    • 串的核心特性
  • 2.串的存储实现
    • 顺序存储结构
    • 链式存储结构
    • 索引存储结构
      • 带长度的索引表
      • 带末指针的索引表
      • 带特征位的索引表
  • 3.串的核心算法实现
    • 串连接运算
    • 求子串运算
  • 4.模式匹配算法
    • 简单模式匹配算法
    • 链式存储的模式匹配
    • KMP算法优化
  • 5.数组的抽象数据类型
    • 多维数组的地址计算
  • 6.特殊矩阵的压缩存储
    • 稀疏矩阵
    • 稀疏矩阵转置
    • 快速转置算法
  • 7.存储效率对比
    • 不同存储方式的空间复杂度
    • 算法效率分析
  • 8.应用场景与选择策略
    • 串的应用领域
    • 数组的应用领域
    • 选择指导原则
  • 9.总结

1.串的基本概念

串是由零个或多个字符组成的有限序列,是一种特殊的线性表,其数据元素仅由字符构成。串在文本处理、编译器设计、生物信息学等领域有着广泛应用。

串的抽象数据类型定义

ADT String {数据对象: D = {cᵢ | cᵢ ∈ CharacterSet, i = 1,2,...,n, n ≥ 0}数据关系: R = {<cᵢ,cᵢ₊₁> | cᵢ,cᵢ₊₁ ∈ D, i = 1,2,...,n-1}操作集合:StrAssign(&T, chars)        // 串赋值StrCopy(&T, S)              // 串复制  StrEmpty(S)                 // 判空StrCmp(S, T)                // 串比较StrLength(S)                // 求串长StrCat(&T, S)               // 串连接SubStr(&Sub, S, pos, len)   // 求子串StrIndex(S, T, pos)         // 子串定位Replace(&S, T, V)           // 串替换StrInsert(&S, pos, T)       // 串插入StrDelete(&S, pos, len)     // 串删除
} ADT String

这个定义包含了串的11种基本运算,涵盖了字符串处理的所有核心操作。

串的核心特性

  1. 有限性:串的长度是有限的
  2. 有序性:字符在串中的位置是有意义的
  3. 同质性:所有元素都是字符类型
  4. 可为空:空串(长度为0)是有效的串

2.串的存储实现

顺序存储结构

顺序存储是串最常用的存储方式,将字符依次存放在连续的存储单元中。

#define MAXSIZE 100typedef struct {char ch[MAXSIZE];  // 存放串值的字符数组int length;        // 串的实际长度
} SeqString;

优点

  • 随机访问效率高,时间复杂度O(1)
  • 存储密度高,没有额外指针开销
  • 实现简单,便于理解和调试

缺点

  • 需要预先分配固定大小的存储空间
  • 插入和删除操作可能需要移动大量字符
  • 存在空间浪费或溢出风险

链式存储结构

链式存储将每个字符存储在单独的节点中,通过指针连接。

typedef struct linknode {char data;              // 字符数据域struct linknode *next;  // 指向下一个字符的指针
} LinkString;

特点分析

  • 存储灵活:动态分配,无固定长度限制
  • 插入删除高效:在已知位置插入删除为O(1)
  • 空间开销大:每个字符需要额外的指针空间
  • 访问效率低:随机访问需要O(n)时间

索引存储结构

索引存储适用于管理多个串的场景,通过索引表记录串的元数据。

带长度的索引表

#define MAXSIZE 1024typedef struct {char name[MAXSIZE];  // 串名称int length;          // 串长度char *start_addr;    // 串值起始地址
} LengthNode;

带末指针的索引表

typedef struct {char name[MAXSIZE];char *start_addr, *end_addr;  // 起始和结束地址
} EndNode;

带特征位的索引表

typedef struct {char name[MAXSIZE];int tag;  // 特征位:0-短串直接存储,1-长串存储地址union {char *start_addr;   // 长串地址char value[4];      // 短串直接存储} uval;
} TagNode;

这种设计优化了短串的存储效率,避免了指针的额外开销。

3.串的核心算法实现

串连接运算

串连接是将两个串依次拼接成一个新串的操作。

SeqString *StrCat(SeqString *s, SeqString *t) {SeqString *r = (SeqString*)malloc(sizeof(SeqString));// 检查长度溢出if (s->length + t->length >= MAXSIZE) {printf("串长度溢出\n");free(r);return NULL;}int i;// 复制第一个串for (i = 0; i < s->length; i++) {r->ch[i] = s->ch[i];}// 连接第二个串for (i = 0; i < t->length; i++) {r->ch[s->length + i] = t->ch[i];}r->ch[s->length + t->length] = '\0';  // 添加字符串结束符r->length = s->length + t->length;return r;
}

时间复杂度:O(m + n),其中m、n分别为两个串的长度
空间复杂度:O(m + n),需要分配新的存储空间

求子串运算

从主串中提取指定位置和长度的子串。

SeqString *SubStr(SeqString *s, int pos, int len) {// 参数合法性检查if (pos < 0 || pos >= s->length || len < 0 || pos + len > s->length) {printf("参数超出有效范围\n");return NULL;}SeqString *t = (SeqString*)malloc(sizeof(SeqString));if (t == NULL) {printf("内存分配失败\n");return NULL;}// 提取子串for (int k = 0; k < len; k++) {t->ch[k] = s->ch[pos + k];}t->ch[len] = '\0';t->length = len;return t;
}

关键点

  • 位置索引从0开始
  • 需要严格检查边界条件
  • 返回的是新分配的串对象

4.模式匹配算法

模式匹配是在主串中查找模式串位置的核心算法,是串处理的重点内容。

简单模式匹配算法

也称为朴素算法或暴力匹配算法。

int SimpleIndex(SeqString *s, SeqString *t) {int i = 0, j = 0;while (i < s->length && j < t->length) {if (s->ch[i] == t->ch[j]) {i++;  // 继续比较下一个字符j++;} else {i = i - j + 1;  // 回溯到下一个起始位置j = 0;          // 模式串重新开始匹配}}if (j == t->length) {return i - t->length;  // 匹配成功,返回起始位置} else {return -1;  // 匹配失败}
}

性能分析

  • 最好情况:O(n),第一次就匹配成功
  • 最坏情况:O(mn),每次都在最后一个字符失配
  • 平均情况:接近O(n)

链式存储的模式匹配

LinkString *LinkIndex(LinkString *s, LinkString *t) {LinkString *first = s;    // 记录主串起始比较位置LinkString *sptr = first; // 主串当前比较位置LinkString *tptr = t;     // 模式串当前比较位置while (sptr && tptr) {if (sptr->data == tptr->data) {sptr = sptr->next;  // 字符匹配,继续比较tptr = tptr->next;} else {first = first->next;  // 回溯到下一个起始位置sptr = first;tptr = t;}}return (tptr == NULL) ? first : NULL;
}

KMP算法优化

KMP算法通过预处理模式串,避免了不必要的回溯,显著提高了匹配效率。

// 计算next数组
void GetNext(SeqString *t, int next[]) {int i = 0, j = -1;next[0] = -1;while (i < t->length - 1) {if (j == -1 || t->ch[i] == t->ch[j]) {i++;j++;next[i] = j;} else {j = next[j];  // 利用已计算的next值}}
}// KMP模式匹配
int KMPIndex(SeqString *s, SeqString *t) {int next[t->length];GetNext(t, next);int i = 0, j = 0;while (i < s->length && j < t->length) {if (j == -1 || s->ch[i] == t->ch[j]) {i++;j++;} else {j = next[j];  // 模式串智能移动}}return (j == t->length) ? i - t->length : -1;
}

KMP算法优势

  • 时间复杂度:O(m + n),其中m为主串长度,n为模式串长度
  • 空间复杂度:O(n),用于存储next数组
  • 无回溯:主串指针不回退,提高了效率

5.数组的抽象数据类型

数组是相同数据类型元素的集合,支持多维索引访问。

ADT Array {数据对象: D = {aᵢ₁ᵢ₂...ᵢₙ | 0 ≤ iⱼ < boundⱼ, j = 1,2,...,n}数据关系: R = {相邻关系由下标序偶确定}操作集合:InitArray(&A, n, bound1, ..., boundn)  // 构造数组DestroyArray(&A)                       // 销毁数组  Value(A, &e, index1, ..., indexn)      // 取元素Assign(&A, e, index1, ..., indexn)     // 赋值元素
} ADT Array

多维数组的地址计算

以二维数组为例,假设数组A[m][n]:

行优先存储

Address(A[i][j]) = BaseAddress + (i × n + j) × sizeof(ElementType)

列优先存储

Address(A[i][j]) = BaseAddress + (j × m + i) × sizeof(ElementType)

6.特殊矩阵的压缩存储

稀疏矩阵

稀疏矩阵是大部分元素为零的矩阵,使用三元组表示法可以大大节省存储空间。

#define MAXSIZE 100typedef struct {int row, col;    // 行号和列号int value;       // 元素值
} Triple;typedef struct {int rows, cols, nums;      // 行数、列数、非零元个数Triple data[MAXSIZE];      // 三元组数组
} SparseMatrix;

稀疏矩阵转置

SparseMatrix *TransposeMatrix(SparseMatrix *a) {SparseMatrix *b = (SparseMatrix*)malloc(sizeof(SparseMatrix));b->rows = a->cols;b->cols = a->rows; b->nums = a->nums;if (a->nums == 0) {return b;}int currentB = 0;// 按列号顺序转置for (int col = 0; col < a->cols; col++) {for (int p = 0; p < a->nums; p++) {if (a->data[p].col == col) {b->data[currentB].row = a->data[p].col;b->data[currentB].col = a->data[p].row;b->data[currentB].value = a->data[p].value;currentB++;}}}return b;
}

算法分析

  • 时间复杂度:O(n × t),其中n为原矩阵列数,t为非零元个数
  • 空间复杂度:O(t),与非零元个数成正比
  • 适用性:当非零元个数远小于矩阵总元素数时,压缩效果显著

快速转置算法

通过预先计算每列的元素个数和起始位置,实现O(n + t)的转置。

SparseMatrix *FastTranspose(SparseMatrix *a) {SparseMatrix *b = (SparseMatrix*)malloc(sizeof(SparseMatrix));int colSize[a->cols];    // 每列非零元个数int colStart[a->cols];   // 每列在转置矩阵中的起始位置b->rows = a->cols;b->cols = a->rows;b->nums = a->nums;if (a->nums == 0) return b;// 统计每列非零元个数for (int col = 0; col < a->cols; col++) {colSize[col] = 0;}for (int t = 0; t < a->nums; t++) {colSize[a->data[t].col]++;}// 计算每列起始位置colStart[0] = 0;for (int col = 1; col < a->cols; col++) {colStart[col] = colStart[col-1] + colSize[col-1];}// 执行转置for (int p = 0; p < a->nums; p++) {int col = a->data[p].col;int q = colStart[col];b->data[q].row = a->data[p].col;b->data[q].col = a->data[p].row;b->data[q].value = a->data[p].value;colStart[col]++;}return b;
}

7.存储效率对比

不同存储方式的空间复杂度

存储方式空间复杂度适用场景
完全存储O(m×n)密集矩阵
三元组O(t)稀疏矩阵(t<<m×n)
十字链表O(t)动态稀疏矩阵
压缩存储O(n)对角、三角矩阵

算法效率分析

操作完全存储三元组存储
随机访问O(1)O(t)
矩阵转置O(m×n)O(n×t)
矩阵加法O(m×n)O(t1+t2)
矩阵乘法O(m×n×k)O(t1×t2×k)

8.应用场景与选择策略

串的应用领域

  1. 文本处理:编辑器、搜索引擎
  2. 编译器:词法分析、语法分析
  3. 生物信息学:DNA序列分析
  4. 网络安全:模式匹配、入侵检测

数组的应用领域

  1. 科学计算:数值分析、图像处理
  2. 图形学:矩阵变换、3D渲染
  3. 机器学习:特征矩阵、权重存储
  4. 数据库:索引结构、关系存储

选择指导原则

串存储选择

  • 顺序存储:长度相对固定,需要高效随机访问
  • 链式存储:长度变化频繁,插入删除操作多
  • 索引存储:管理大量不同长度的串

数组存储选择

  • 完全存储:元素密集,需要频繁随机访问
  • 压缩存储:具有特殊模式(对称、三角等)
  • 稀疏存储:大部分元素为零或默认值

9.总结

串和数组作为基础数据结构,各自具有独特的特点和应用价值:

串的核心价值

  • 专门处理字符数据,支持丰富的文本操作
  • KMP等高效模式匹配算法在文本处理中不可或缺
  • 为编译器、搜索引擎等系统提供基础支撑

数组的核心价值

  • 提供多维数据的统一存储和访问机制
  • 压缩存储技术大幅提高空间效率
  • 为科学计算、图形处理等领域提供基础设施

理解这两种数据结构的设计思想和实现技巧,不仅有助于提高程序设计能力,更为学习更复杂的数据结构和算法奠定了坚实基础。在实际应用中,应根据具体的数据特征和操作需求,选择合适的存储方式和算法实现。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试之JVM

类的生命周期 加载、链接、初始化&#xff08;是类的初始化&#xff09;、使用&#xff08;对象的初始化&#xff09;、卸载&#xff08;GC&#xff09; 链接&#xff1a;验证、准备、解析 类加载 JDK9的升级点&#xff1a;扩展类加载器改成了平台类加载器。 java中很多的包分…

webpack开发模式与生产模式(webpack --mode=development/production“, )

webpack开发模式与生产模式的区别webpack的development&#xff08;开发模式&#xff09;和production&#xff08;生产模式&#xff09;是两种常见的构建环境配置&#xff0c;主要区别体现在构建速度、代码优化和调试支持等方面。开发模式 (development)目标&#xff1a;注重开…

当自然语言遇上数据库:Text2Sql.Net的MCP革命如何重新定义开发者与数据的交互方式

想象一下&#xff0c;在IDE中对AI助手说"帮我找出本月销售额最高的前10个产品"&#xff0c;然后它不仅能理解你的意图&#xff0c;还能直接生成并执行SQL查询&#xff0c;返回准确结果——这不是科幻&#xff0c;而是Text2Sql.Net的MCP集成带来的现实。 &#x1f3af…

2025流程图模板和工具深度评测:AI如何提升绘图效率80%?

引言&#xff1a;流程图模板的价值革命 在数字化办公的浪潮中&#xff0c;流程图已从单纯的"业务说明工具"进化为跨部门协作的"视觉语言"。据智研咨询2025年报告显示&#xff0c;规范使用流程图模板可使团队沟通效率提升40%&#xff0c;错误率降低58%。无…

WebSocket实时通信系统——js技能提升

2. WebSocket实时通信系统 功能概述 实现完整的WebSocket通信系统&#xff0c;支持实时消息推送、连接管理、心跳检测和自动重连。 技术难点 WebSocket连接生命周期管理消息序列化和反序列化心跳机制和连接保活错误处理和重连策略多组件状态同步 实现思路 2.1 WebSocket管理器 …

Spring AI 入门指南:三步将AI集成到Spring Boot应用

无需深入AI底层实现&#xff0c;Java开发者也能快速构建智能应用本文将介绍如何使用 Spring AI 在 Spring Boot 项目中快速集成 AI 能力。通过三步操作——添加依赖、配置 API 凭证和编写调用代码&#xff0c;Java 开发者可以轻松构建 AI 应用。一、Spring AI 简介Spring AI 是…

OOM问题排查思路及解决方案

OOM问题原因&#xff1a; 根本原因是创建的对象数量超过JVM堆内存容量&#xff0c;且这些对象无法被GC回收场景&#xff1a; 1.本地缓存了用户态&#xff0c;用户量急剧上升导致内存溢出&#xff0c;如使用HashMap本地缓存10万用户数据&#xff0c;每 个用户对象约2KB&#xf…

梨花教育暖心鹏城:深圳市养老护理院里“时光绽放”,用声音点亮银发精神之光

2025年8月24日&#xff0c;在深圳这座充满活力与梦想的城市&#xff0c;一场温暖人心的公益活动在深圳市养老护理院温情上演。梨花教育策划并组织了“梨花・时光绽放”公益活动&#xff0c;旨在通过声音的魅力&#xff0c;为市养老护理院的老人们送去关怀与欢乐&#xff0c;丰富…

力扣100+补充大完结

力扣100分类一、Java基础代码模板1. 基础输入输出模板import java.util.Scanner;class Solution {public static int linkedListOperation() {// 链表操作实现return 0;}public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.next…

SSM从入门到实战:3.3 SpringMVC数据绑定与验证

&#x1f44b; 大家好&#xff0c;我是 阿问学长&#xff01;专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高的教辅资料推荐等&#xff0c;欢迎关注交流&#xff01;&#x1f680; &#x1f4d6; 本文概述 本文是SSM框架系列SpringMVC基础篇的第三篇&#xff0…

ctfshow_萌新web16-web20-----文件包含日志注入

_萌新web16解开md5?c36d_萌新web17-----文件包含禁用了php关键字&#xff0c;这个题禁了远程文件包含,进行日志注入发现日志中有user-agent信息&#xff0c;因此我们可以在user-agent中写入木马抓包burpsuitUser-agent:<?php eval($_POST[cmd])?>抓包然后连接蚁剑_萌新…

Flink的CheckPoint与SavePoint

Flink的Checkpoint&#xff08;检查点&#xff09;和Savepoint&#xff08;保存点&#xff09;是两种不同的状态快照机制&#xff0c;主要区别如下&#xff1a;1. ‌Checkpoint‌‌核心功能‌&#xff1a;周期性触发的容错机制&#xff0c;用于故障恢复时保证状态一致性57。‌触…

Ansible 自动化运维工具:介绍与完整部署(RHEL 9)

Ansible 自动化运维工具&#xff1a;介绍与完整部署&#xff08;RHEL 9&#xff09;Ansible 的介绍与安装 一、自动化运维的必要性 传统手动运维依赖图形/命令行界面、检查清单或记忆执行任务&#xff0c;存在以下核心问题&#xff1a; 易出错&#xff1a;易跳过步骤或执行错误…

构建生产级 RAG 系统:从数据处理到智能体(Agent)的全流程深度解析

文章目录一、 整体架构设计&#xff1a;迈向智能体&#xff08;Agent&#xff09;驱动的 RAG二、 数据准备与预处理&#xff1a;构建高质量知识库2.1 数据加载与初步提取2.2 多策略分块 (Multi-Strategy Chunking)逻辑分块&#xff1a;按故障章节和关键说明传统分块&#xff1a…

Duplicate Same Files Searcher v10.7.0,秒扫全盘重复档,符号链接一键瘦身

[软件名称]: Duplicate Same Files Searcher v10.7.0 [软件大小]: 3.3 MB [软件大小]: 夸克网盘 | 百度网盘 软件介绍 Duplicate Same Files Searcher&#xff08;重复文件搜索&#xff09;是一款强大且专业的重复文件查找与清理工具。通过使用该软件&#xff0c;用户可以方…

C/C++ 数据结构 —— 树(2)

​ &#x1f381;个人主页&#xff1a;工藤新一 ​ &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; ​ &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 ​ &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章…

EEA架构介绍

前言 本文主要对EEA架构的理解进行了记录&#xff0c;以加深理解及方便后续查漏补缺。 EEA架构 硬件架构 EEA架构作用 提升算力利用率、数据统一交互&#xff0c;实现整车功能协同、缩短线束、降低重量、降低故障率、提升装配自动化 EEA架构发展趋势 分布式–>域集中式–>…

【目标跟踪】《FastTracker: Real-Time and Accurate Visual Tracking》论文阅读笔记

0.参考 论文:https://arxiv.org/pdf/2508.14370v1 代码:github.com/HamidrezaHashempoor/FastTracker, huggingface.co/datasets/HamidrezaHashemp/FastTracker-Benchmark. 1.摘要 提高多目标跟踪在多物体跟踪上的性能(从前主要是针对行人场景做的优化)。 该方法包含两…

C++ 内存安全与智能指针深度解析

C 内存安全与智能指针深度解析面试官考察“野指针”&#xff0c;实际上是在考察你对 C “资源所有权” (Ownership) 和 “生命周期管理” (Lifetime Management) 的理解。现代 C 的答案不是“如何手动避免”&#xff0c;而是“如何自动化管理”。第一部分&#xff1a;核心知识点…

Vue SFC Playground 如何正确引入 naive-ui

网罗开发&#xff08;小红书、快手、视频号同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…