数据结构篇——串(String)

一、引入


在计算机中的处理的数据内容大致可分为以整形、浮点型等的数值处理和字符、字符串等的非数值处理。

今天我们主要学习的就是字符串数据。本章主要围绕"串的定义、串的类型、串的结构及其运算"来进行串介绍与学习。

二、串的定义


2.1、串的基本定义


**串(string)是由零个或多个字符组成的有限序列,**也是一种内容受限的线性表。其特殊性体现在数据元素是一个字符。一般表示为:

cpp 复制代码
S="abcdefg";

其中,S是串的名,双引号内元素的个数为串的长度 ,0个元素的串被称为空串,其长度为0;

Tips: 字符串中的"空格"也算是串的一个元素,当一个串的元素只有空格时,这个串称为**"空格串"**

2.2、子串以及串相等的条件


在一个串中,任意几个连续字符所组成的序列称之为该串的子串,包含子串的串叫做主串。子串在主串中的位置通常用子串的第一个字符在主串中的位置表示。

例如下图的四个串:

它们的长度分别为3、4、7、8.且a、吧、都是c和d的子串。其中a在c、d中的位置都是1.而b在c中的位置为4,在d中的位置为5。

那么,怎么判断两个串是否相等呢?一般来说,只有当两个串的长度相等且各个位置对应的字符都相等时才相等。像上图中的a、b、c、d彼此都不相等。

三、串的类型定义和储存结构


3.1、串的类型定义与基本操作


串的逻辑结构与先信标相似,但其基本操作的对象却有较大的区别。串的操作主要集中在"子串"这样的一个部分整体而不是单个元素。

其常见的基本操作如下:

|----------------------------|----------------------------------------------------------|----------------------------------------------|
| 函数 | 初始条件 | 操作结果 |
| StrAssign(&T,chars) | chars是字符串常量 | 生成一个其值等于chars的串T |
| StrCopy(&T,S) | 串S存在 | 由串S复制得到串T |
| StrEmpty(S) | 串S存在 | 判断串S是否为空串 |
| StrCompare(S,T) | 串S、T存在 | 比较S、T的大小。分别返回>0、=1、<0的值 |
| StrLength | 串S存在 | 返回串S的长度(元素个数) |
| ClearString | 串S存在 | 将S清为空串 |
| Concat(&T,s1,s2) | 串s1、s2存在 | 将s1、s2拼接并由T返回 |
| SubString(&Sub,S,pos,len) | 串S存在,1<=pos<=StrLength(S)且0<=len<=StrLength(S)-pos+1 | 用sub返回串S的第pos个字符起长度为len的子串 |
| Index(S,T,pos) | 串S、T存在,T非空串,1<=pos<=StrLength(S). | 若S、T中有相同的子串,则返回它在主串S中的第pos个字符后第一次出现的位置,否则返回0 |
| Replace(&s,T,V) | 串S、T存在,T非空串 | 用V替换主串S中出现的所有与T相等的不重叠子串 |
| StrInsert(&S,pos,T) | 串S、T存在,1<=pos<=StrLength(S)+1. | 在串S的第pos个字符前插入串T |
| StrDelete(&S,pos,len) | 串S存在,1<=pos<=StrLength(S)-len+1 | 从S中删除第pos个字符起长度为len的子串 |
| DestoryString(&S) | 串S存在 | 销毁串S |

3.2、串的储存结构


同其他数据结构一样,串也是有着最为常见的两种储存结构------顺序和链式。但考虑到存储效率和算法方便性,串多采用链式存储。

3.2.1、顺序存储


1、定长顺序存储:

类似于线性表,用一组地址连续的存储单元存储串值的字符序列,按照预定义的大小,为每个串变量分配一个固定长度的存储区。则可用定长数组如下表示:

cpp 复制代码
#define MAXLEN 255    //定义串的最大长度
typedef struct{
    char ch[MAXLEN+1];    //存储串的一维数组
    int length;            //记录串的长度
} SSting;

但这种存储方式如同它的名字一样,是存储长度是固定的。串的实际长度只能小于等于MAXLEN,超过预定义长度的串值会被舍去,称为截断。串长有两种表示方法: 一是如上述定义描述的那样,用一个额外的变量len来存放串的长度;二是在串值后面加一一个不计入串长的结束标记字符"\0",此时的串长为隐含值。

但是现实生活中所遇到的数据长度都是不固定的。这时候内存的动态分布就显得格外重要。这时候就印出了一个新的顺序存储结构------堆分配存储。

2、堆分配存储:

在c语言中存在一个称之为堆(Heap)的自由存储区,可以为每个新产生的串动态分配一块实际串长所需要的存储空间,若分配成功,则返回指向起始地址的指针作为串的基址,同时为了方便处理,约定串长也作为存储结构的一部分。定义如下:

cpp 复制代码
typedef struct{
    char *ch;    //若是非空串,则按串长分配存储区,否则ch为NULL
    int length;
}HString;

3.2.2、链式存储


在顺序串中,我们发现,如果对其进行插入或者删除操作就显得十分麻烦。而链表结构在这方面就刚好能弥补这个弊端。但由于串的特殊性------结构中的每一个数据元素是一个字符,所以存在一个问题------每个结点中可以只存放一个字符,也可以存放多个字符。如图所示

所以,当结点大小大于1时,由于串长不一定是结点大小的整数倍,所以链表中最后一个结点不一定全被串值占满。此时通常补上"#"或其他非串值字符。

为了操作方便,当以链表存储串值的时候,除头指针外,还可附设一个尾指针指示链表中的最后一个结点,并给出当前串的长度。说明如下:

cpp 复制代码
#define CHUNKSIZE 80        //定义块大小
    //定义结点结构
typedef struct Chunk{
    char ch[CHUNKSIZE];
    struct Chunk *next;
}Chunk;

typedef struct{
    Chunk *head,*tail;    //串的头尾指针
    int length;        //串的长度
}LString

串值的链式存储结构对某些串操作有一定的方便之处,但总体来说,不如顺序结构灵活。它占用存储量大且操作复杂。

四、小结


本文主要介绍了串的定义及其存储结构。涉及到的串的匹配算法相对比较重要,所以将单独发布来学习。

如果我的内容对你有帮助,在下就厚着脸皮讨个点赞关注。如果你有更好的想法,还望留在评论区让我来参考学习。我将不胜感激并努力创作出更好的内容。

相关推荐
开心比对错重要8 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode
Doopny@9 小时前
数字组合(信息学奥赛一本通-1291)
数据结构·算法·动态规划
君莫愁。9 小时前
【Unity】搭建基于字典(Dictionary)和泛型列表(List)的音频系统
数据结构·unity·c#·游戏引擎·音频
原来是猿9 小时前
蓝桥备赛(13)- 链表和 list(上)
开发语言·数据结构·c++·算法·链表·list
总斯霖11 小时前
题解:士兵排列
数据结构·c++·算法
平谷一勺12 小时前
go切片定义和初始化
数据结构·golang·数组·切片
誓约酱12 小时前
(每日一题) 力扣 283 移动零
linux·c语言·数据结构·c++·算法·leetcode
橘子真甜~13 小时前
34.二叉树进阶3(平衡二叉搜索树 - AVL树及其旋转操作图解)
数据结构·c++·二叉搜索树·avl树·平衡搜索树
仟濹13 小时前
【算法 C/C++】一维前缀和
数据结构·c++·算法
tt55555555555513 小时前
每日一题——搜索二维矩阵
数据结构·算法·leetcode