1.基本概念
由零个或多个字符组成的有限序列。
2.抽象数据类型(ADT)
数据对象:串的数据对象约束为字符集
数据关系:与线性表类似,除第一个和最后一个元素外,其余元素有且仅有一个直接前驱和直接后驱。
基本操作:
StrAssign(*T, chars): 生成一个其值等于chars的串T。
StrCopy(*T, S): 由串S复制得串T。
StrEmpty(S):若串S为空串,则返回TRUE,否则返回FALSE。
StrCompare(S, T): 若S>T,则返回值为1;若S=T,则返回值为0;若S<T,则返回值为-1。
StrLength(S): 返回串S的元素个数。
ClearString(*S): 清空S。
Contact(*T, S1, S2): 用T返回由S1和S2联接而成的新串。
SubString(*Sub, S, pos, len): 用Sub返回串S的第pos个字符起长度为len的子串。
Index(S, T, pos): 若主串S中存在与非空串T值相同的子串,则返回它在主串中第pos个字符之后第一次出现的位置;否则返回0。
Replace(*S ,T ,V): 用V替换主串S中出现的所有与T相等的不重叠的子串。
StrInsert(*S, pos, T): 在串S的第pos个字符之前插入串T。
StrDelete(*S, pos, len): 从串S中删除第pos个字符起长度为len的子串。
3.顺序存储
串的顺序存储方式可以用定长数组存储表示,也可以用堆分配存储表示。两者都具有顺序存储结构的特点,处理方便,但前者有个缺陷,由于数组长度限定,可能会出现数据溢出的错误。因此相比之下,第二种方法更为灵活。在此主要介绍堆分配存储方式。
#define ERROR 0
#define OK 1
#define TRUE 1
#define FALSE 0
#include <stdlib.h>
#include <stdio.h>
#define MAXSIZE 100
//串的堆分配存储表示
typedef struct
{
char*ch; //串的存储基地址
int length; //串的长度
}HString;
int StrAssign(HString*T,char *chars) //新建串
{
int i=0;
char*c=chars;
if (T->ch) free(T->ch);
while (*c)
{
c++;
i++;
}
T->length=i; //确定字符串长度
if (i==0)
{
T->ch=NULL;
}else
{
if (!(T->ch=(char*)malloc(sizeof(char)*(i+1)))) return ERROR; //申请存储空间,多预留一个空间,用于放置结束符号'\0',方便之后输出
for (i=0;i<T->length;i++)
{
T->ch[i]=chars[i];
}
T->ch[i]='\0';
}
return OK;
}
int StrCopy(HString*T,char*S) //复制串
{
int i=0,j;
char*c=S;
while (*c)
{
c++;
i++;
}
if (T->ch) free(T->ch);
if (!(T->ch=(char*)malloc(sizeof(char)*i+1))) return ERROR; //申请存储空间,多预留一个空间,用于放置结束符号'\0',方便之后输出
for (j=0;j<i;j++) //逐个字符复制
{
T->ch[j]=S[j];
}
T->length=i;
T->ch[j]='\0';
return OK;
}
int StrEmpty(HString S) //验证串是否为空
{
if (S.length==0) return TRUE;
return FALSE;
}
int StrLength(HString S) //返回串长度
{
return S.length;
}
int ClearString(HString *S) //清空串
{
if (S->ch)
{
free(S->ch);
S->ch=NULL;
}
S->length=0;
return OK;
}
int StrCompare(HString S,HString T) //比较两个串的大小
{
int i;
for (i=0;i<S.length && i<T.length;i++)
{
if (S.ch[i]>T.ch[i]) return 1;
if (S.ch[i]<T.ch[i]) return -1;
}
if (S.length>T.length) i=1;
else if (S.length<T.length) i=-1;
else i=0;
return i;
}
int Contact(HString*T,HString S1,HString S2) //连接两个串
{
int i,j;
if (T->ch) free(T->ch);
if (!(T->ch=(char*)malloc(sizeof(char)*(S1.length+S2.length+1)))) return ERROR;
for (i=0;i<S1.length;i++) T->ch[i]=S1.ch[i]; //首先粘贴第一个串
for (j=0;j<S2.length;j++) T->ch[i+j]=S2.ch[j]; //接着粘贴第二个串
T->ch[i+j]='\0';
T->length=S1.length+S2.length;
}
int SubString(HString*Sub,HString S,int pos,int len) //取子串
{
int i;
if (pos<1 || pos>S.length || len<0 || len>(S.length-pos+1)) //若取串的位置或长度不合法,则返回错误
return ERROR;
if (Sub->ch) free(Sub->ch);
if (!(Sub->ch=(char*)malloc(sizeof(char)*(len+1)))) return ERROR;
for (i=0;i<len;i++)
{
Sub->ch[i]=S.ch[pos+i-1];
}
Sub->ch[i]='\0';
Sub->length=len;
return OK;
}
int Index(HString S,HString T,int pos) //查找子串位置
{
int i,j;
if (pos<1 || pos>S.length || T.length==0) return ERROR; //若子串为空或查找的起始位置不合理,则返回错误
if (T.length>S.length) return FALSE;
j=pos;
do{
for (i=0;i<T.length;i++) //遍历主串和子串,对比每个元素
{
if (S.ch[j+i-1]!=T.ch[i]) //如果有元素不等,则主串的匹配初始位置加1,并重新开始匹配
{j++;break;}
}
}while(i<T.length && j<S.length); //如果查找成功或未找到,则退出循坏
if (i>=T.length) return j-pos+1; //若查找成功,返回从起始位置开始的坐标
else return FALSE;
}
int Replace(HString*S,HString T,HString V) //替换子串
{
int i,j=1;
HString s,Sub,str,S1;
s.ch=NULL;Sub.ch=NULL;str.ch=NULL;S1.ch=NULL;
s.length=0;Sub.length=0;str.length=0;S1.length=0;
if ( S->length ==0 || T.length==0) return ERROR; //如果主串为空或要替换的子串长度为空,则返回错误
i=Index(*S,T,j); //第一次查找被替换子串的位置
if (i==0) return FALSE; //如果主串中没有要替换的子串,则返回失败
while(i!=0) //如果有替换的子串
{
SubString(&Sub,*S,j,i-1); //记录被替换子串前面的串
Contact(&s,Sub,V); //将前面的串和新串相连,完成串的替换
Contact(&str,S1,s); //添加到替换过的串后面
StrCopy(&S1,str.ch);
j+=i+T.length-1; //重新定位查找位置
i=Index(*S,T,j); //查找下一个被替换子串的位置
}
if (j<=S->length) //把最后一个替换子串后面的串的内容连接到替换过的串尾部
{
SubString(&Sub,*S,j,S->length-j+1);
Contact(&str,S1,Sub);
StrCopy(&S1,str.ch);
}
*S=S1; //更新原来的串
return OK;
}
int StrInsert(HString*S,int pos,HString T) //插入子串
{
HString Sub1,S1,Sub2;
Sub1.ch=NULL;S1.ch=NULL;Sub2.ch=NULL;
Sub1.length=0;S1.length=0;Sub2.length=0;
if (pos<1 || pos>S->length+1 || T.length==0) return ERROR; //如果插入的子串为空或插入的位置非法,则返回错误
SubString(&Sub1,*S,1,pos-1); //记录插入位置之前的串
Contact(&S1,Sub1,T); //将插入位置前的串和插入的串连接起来
SubString(&Sub2,*S,pos,S->length-pos+1); //记录剩下的串
Contact(S,S1,Sub2); //将剩下的串连接到尾部
return OK;
}
int StrDelete(HString*S,int pos,int len) //删除子串
{
HString Sub1,Sub2;
Sub1.ch=NULL;Sub2.ch=NULL;
Sub1.length=0;Sub2.length=0;
Sub1.ch=NULL;Sub2.ch=NULL;
if (pos<1 || pos>S->length || len>S->length-pos+1) return ERROR; //如果删除位置不合理或删除的长度超过主串的长度,则返回错误
SubString(&Sub1,*S,1,pos-1); //记录删除位置之前的串
SubString(&Sub2,*S,pos+len,S->length-pos-len+1); //记录删除子串后面的剩余串
Contact(S,Sub1,Sub2); //将前面的串和后面的串相连
return OK;
}
int main()
{
HString S,T,Sub,V,S1,S2;
S.ch=NULL;
T.ch=NULL;
Sub.ch=NULL;
V.ch=NULL;
S1.ch=NULL;
S2.ch=NULL;
char s[MAXSIZE];
int pos,choice,len;
do{
printf("输入操作: 0.退出 1.返回串长度 2.插入串 3.删除串 4.获取子串 5.清空串 6.创建串 7.查找串 8.验证串是否为空 9.替换串 10.连接串 11.比较串 12.复制串\n");
scanf("%d",&choice);
switch (choice)
{
case 1:
{
printf("%d\n",StrLength(S));
break;
}
case 2:
{
printf("输入插入的位置\n");
scanf("%d",&pos);
printf("输入插入的串\n");
scanf("%s",s);
StrAssign(&T,s);
StrInsert(&S,pos,T);
printf("S=%s\n",S.ch);
break;
}
case 3:
{
printf("输入删除的位置\n");
scanf("%d",&pos);
printf("输入删除的长度\n");
scanf("%d",&len);
StrDelete(&S,pos,len);
printf("S=%s\n",S.ch);
break;
}
case 4:
{
printf("输入子串的起始位置\n");
scanf("%d",&pos);
printf("输入子串的长度\n");
scanf("%d",&len);
SubString(&Sub,S,pos,len);
printf("Sub=%s\n",Sub.ch);
break;
}
case 5:
{
ClearString(&S);
printf("S=%s\n",S.ch);
break;
}
case 6:
{
printf("输入串的内容\n");
scanf("%s",s);
StrAssign(&S,s);
printf("S=%s\n",S.ch);
break;
}
case 7:
{
printf("输入在主串中的起始查找位置\n");
scanf("%d",&pos);
printf("输入查找的子串\n");
scanf("%s",s);
StrAssign(&T,s);
printf("Index=%d\n",Index(S,T,pos));
break;
}
case 8:
{
printf("%d\n",StrEmpty(S));
break;
}
case 9:
{
printf("输入被替换的子串\n");
scanf("%s",s);
StrAssign(&T,s);
printf("输入替换的子串\n");
scanf("%s",s);
StrAssign(&V,s);
Replace(&S,T,V);
printf("S=%s\n",S.ch);
break;
}
case 10:
{
printf("输入第一个子串\n");
scanf("%s",s);
StrAssign(&S1,s);
printf("输入第二个的子串\n");
scanf("%s",s);
StrAssign(&S2,s);
Contact(&T,S1,S2);
printf("T=%s\n",T.ch);
break;
}
case 11:
{
printf("输入第一个子串\n");
scanf("%s",s);
StrAssign(&S1,s);
printf("输入第二个的子串\n");
scanf("%s",s);
StrAssign(&S2,s);
printf("result=%d\n",StrCompare(S1,S2));
break;
}
case 12:
{
printf("输入要复制的串\n");
scanf("%s",s);
StrCopy(&S,s);
printf("S=%s\n",S.ch);
break;
}
}
}while(choice!=0);
return 0;
}
4.链式存储
串也可以使用链式存储方式进行表示,每个结点可以存放多个字符。链表中的最后一个结点不一定全被串值占满,此时通常不上其他非串值字符。为方便串的连接操作,除头指针外还可附设一个尾指针指示链表中的最后一个结点。链式存储结构的操作与线性表类似,但总体上链式存储结构不如顺序存储结构灵活。