顺序串和链串
串是字符串的简称,串是由零个或多个字符组成的有限序列。
串是一种特殊的线性表,其特殊性体现在ElemType
固定为字符类型。
串的基本概念
串的逻辑表示
“ a 1 a 2 a 3 . . . a n ” ( n ≥ 0 ) “a_1a_2a_3...a_n”( n \ge 0 ) “a1a2a3...an”(n≥0)
注意双引号不是串的内容
串长
串中所含字符的个数
空串
含零个字符的串,串长为0,空串可以用希腊字母Φ表示
空格串
只包含空格(对应ASCII码为32)的串,空格串有内容有长度
注意空格串和空串的区别
串相等
当且仅当两个串的长度相等并且对应位置上的字符都相同时,这两个串才是相等的。
所有的空串都是相等的
子串:
一个串中任意个连续字符组成的子序列(含空串)称为该串的子串,例如"abcd"
的子串有
- Φ
"a"
,"b"
,"c"
,"d"
"ab"
,"bc"
,"cd"
"abc"
,"bcd"
"abcd"
真子串
是指不包含自身的所有子串
串的基本操作
求串长
C语言用\0
字符标记串的结束,在求串长时需要从头开始遍历,时间复杂度为O(n);若单独存储串的长度即可省去遍历操作,时间复杂度为O(1)
比较
给定两个串
s
=
"
a
1
a
2
.
.
.
a
n
"
t
=
"
b
1
b
2
.
.
.
b
m
"
s= "a_1a_2...a_n"\\ t="b_1b_2...b_m"
s="a1a2...an"t="b1b2...bm"
当满足下列条件之一,有s<t
1.
n
<
m
,
且
a
i
=
b
i
(
i
=
1
,
2
,
.
.
.
,
n
)
1.n<m,且a_i = b_i(i = 1,2,...,n)
1.n<m,且ai=bi(i=1,2,...,n)
2.
存
在
某
个
k
≤
min
(
m
,
n
)
,
使
得
a
i
=
b
i
(
i
=
1
,
2
,
.
.
.
,
k
−
1
)
,
a
k
<
b
k
2.存在某个k\le \min(m,n),使得a_i=b_i(i=1,2,...,k-1),a_k<b_k
2.存在某个k≤min(m,n),使得ai=bi(i=1,2,...,k−1),ak<bk
复制
连接
求子串
子串插入
子串删除
子串替换
串输出
…
上面这些基本操作,一般的高级语言会提供现成的库函数,不需要程序员自己造轮子。具体内容请查阅标准参考手册。
串的顺序存储结构
用一组地址连续的存储单元来存储串中的字符序列。采用定长数组来定义。
#define MAXLEN 100
typedef struct _string{
char ch[MAXLEN];
int len;
}string;
例 串的比较
给string
类型的顺序串编写一个比较函数Strcmp
,能够比较两个顺序串大小并返回一个整型值,Strcmp
的行为应与<string.h>
定义的strcmp
函数一致
基本思路:
- 求出两个串长的最小值
min_len
,比较两个串在min_len
长度范围内的字符
若s的字符 < t的字符,返回1
若s的字符 > t的字符,返回-1
若s的字符 = t的字符,比较下一个位置的字符 - 若在
min_len
范围内的字符均相同,比较长度
长度相等,返回0
s的长度 > t的长度,返回1
s的长度 < t的长度,返回-1
int
Strcmp( string *s1, string *s2 )
{
int i;
int min_len;
min_len = s1->len > s2->len ? s2->len : s1->len;
for( i = 0; i < min_len; i++ ){
if( s1->ch[i] < s2->ch[i] ){
return 1;
}else if( s1->ch[i] > s2->ch[i] ){
return -1;
}
}
if( s1->len == s2->len ){
return 0;
}else if( s1->len > s2->len ){
return 1;
}else{
return -1;
}
}
操作顺序串时需警惕数组越界的问题,尤其在对两串进行复制,连接,子串插入,子串替换这几个操作时更要小心。一般的操作步骤是先检查长度,截断多余的部分以保证程序正常运行,显然在截断的过程中造成了数据丢失。对于这种不能事先确定串长的变化范围的问题,一般会使用动态的链式存储结构来存储串。
串的链式存储结构
串的链式存储结构也被称为链串,组织形式和链表相似。
链串1
typedef struct _strnode{
char ch;
struct _lstring *next;
}strnode;
如果在一个节点只存储一个字符,会浪费结点3个字节的空间,存储密度为12.5%
为了提高存储密度,我们可以将灰色部分的空间利用起来
链串2
typedef struct _strnode{
char ch[4];
struct _lstring *next;
}strnode;
此时存储密度提高到了50%。
在链串1中,串结束的标志是链表尾的NULL
指针。但在链串2中,需要单独设置一个结束标志字符,如#
或\0
链串2的存储密度提高了4倍,付出的代价就是操作变得更复杂了:结点和结点之间是链式结构的操作,结点内部的字符又是顺序结构的操作。所以我们要根据串的某个操作的频度灵活选择合适的数据域大小。
如果串频繁的执行子串插入替换等操作,应该设置单个char以简化操作
如果串频繁执行连接操作,使用第二种结构会更节约空间。
例 子串替换
给定一个含子串ab
的串,请将第一个出现的ab
替换为xyz
给出的条件是2换3,如果使用顺序串,要花时间把a后面的字符往后移一位,时间复杂度为O(n)
如果使用链串,只需要将新结点插入到ab中间即可。同时为了简化操作应使用链串1的结构
void
replace( strnode *s )
{
int exit = FALSE;
strnode *p;
for( s = s->next; s->next != NULL; s = s->next ){
if( s->ch == 'a' && s->next->ch == 'b' ){
exit = TRUE;
break;
}
}
if( exit ){
s->ch = 'x';
s->next->ch = 'z';
p = ( strnode* )malloc( sizeof( strnode ) );
p->ch = 'y';
p->next = s->next;
s->next = p;
}
}