顺序串和链串

串是字符串的简称,串是由零个或多个字符组成的有限序列

串是一种特殊的线性表,其特殊性体现在ElemType固定为字符类型。

串的基本概念

串的逻辑表示

“ a 1 a 2 a 3 . . . a n ” ( n ≥ 0 ) “a_1a_2a_3...a_n”( n \ge 0 ) a1a2a3...an(n0)

注意双引号不是串的内容

串长

串中所含字符的个数

空串

含零个字符的串,串长为0,空串可以用希腊字母Φ表示

空格串

只包含空格(对应ASCII码为32)的串,空格串有内容有长度

注意空格串和空串的区别

串相等

当且仅当两个串的长度相等并且对应位置上的字符都相同时,这两个串才是相等的。

所有的空串都是相等的

子串:

一个串中任意个连续字符组成的子序列(含空串)称为该串的子串,例如"abcd"的子串有

  1. Φ
  2. "a""b""c""d"
  3. "ab""bc""cd"
  4. "abc""bcd"
  5. "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.kmin(m,n)使ai=bi(i=1,2,...,k1)ak<bk

复制

连接

求子串

子串插入

子串删除

子串替换

串输出

上面这些基本操作,一般的高级语言会提供现成的库函数,不需要程序员自己造轮子。具体内容请查阅标准参考手册。

串的顺序存储结构

用一组地址连续的存储单元来存储串中的字符序列。采用定长数组来定义。

#define MAXLEN 100

typedef struct _string{
	char ch[MAXLEN];
	int len;
}string;

例 串的比较

string类型的顺序串编写一个比较函数Strcmp,能够比较两个顺序串大小并返回一个整型值,Strcmp的行为应与<string.h>定义的strcmp函数一致

基本思路:

  1. 求出两个串长的最小值min_len,比较两个串在min_len长度范围内的字符
    若s的字符 < t的字符,返回1
    若s的字符 > t的字符,返回-1
    若s的字符 = t的字符,比较下一个位置的字符
  2. 若在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;
	}
}
posted @ 2020-07-26 14:24  LanceHansen  阅读(403)  评论(0编辑  收藏  举报