【数据结构】串(一)—— 串的基础知识

【数据结构】串(一)—— 串的基础知识

    • 前言
    • 一、串类型的定义
    • 二、串的三种存储结构存
      • (1)定长顺序存储
      • (2)变长分配存储表示(堆分配)
      • (3)块链存储
        • 三种存储结构的总结
    • 三、串的操作
      • (1)串的初始化(以下基于顺序表):
      • (2)串的操作(代码):

前言

C语言字符串与数据结构中的串的区别:

C语言中有个字符串,数据结构中有个串,联系是二者都是存储字符的,区别在于前者我们直接定义数组存储,以 “ \0 ”结尾,如

char str[size];

而后者我们是把字符串看成一个线性表,定义成结构体形式,可以用链表,也可以是顺序表(结尾用length控制),以下是定义:如

typedef struct Str
{
    char elem[MAXSIZE+1];//elem数组用来存放串的元素
    int length;//定义一个串长度
}Str;

一、串类型的定义

串(string)(或字符串)是由零个或多个字符组成的有序序列,一般记为

 S=”a1a2…an”   (n>=0)

子串:串中任意连续的字符组成的子序列

串的逻辑结构:与线性表相类似,是限定了元素为字符的线性表。

线性表与串的操作的不同:线性表的操作主要针对表内的某一个元素;串的操作主要针对串内的一个子串;

char str[] = "abcdef";

注:数组str的长度为7;串的长度为6(不包含“\0”);

主串:包含子串的串相应的称为主串。
串的表示:
串有两种表示形式:顺序存储表示和链式存储表示。
顺序存储表示:串的顺序存储结构简称为顺序串,顺序串中的字符序列被顺序地存放在一组连续的存储单元中,主要有三种实现顺序串的方式。

二、串的三种存储结构存

储串的结构有三种:

  1. 定长顺序存储
  2. 堆分配存储
  3. 块链存储

(1)定长顺序存储

定长顺序存储的结构体:

typedef struct Str
{
    char elem[MAXSIZE+1];//elem数组用来存放串的元素
    int length;//定义一个串长度
}Str;

采用固定长度的数组(即静态数组)存储串。
例如:char a[7] = “abcdfg”;
此方式存储串时,需要预估串的长度提前申请足够的存储空间。目标串如果超过了数组申请的长度,超出部分会被自动舍弃(称为“截断”)。
例如:char a[3] = “abcdfg”;//实际上数组中只存储了 “abc” ,后边的被截断。

(2)变长分配存储表示(堆分配)

结构体定义:

typedef struct
{
	char *ch;  		// 指向动态分配存储区首地址的字符指针
	int length;		// 串长度
}Str;

采用动态数组存储串。

在C语言中,存在着一个被称之为“堆”的自由存储区,用 malloc 函数和 free 函数管理,malloc 函数负责申请空间,free 函数负责释放空间。

例如:

char * a = (char*)malloc(5*sizeof(char));//创建 a 数组,动态申请5个 char 类型数据的存储空间

使用堆分配存储的优势在于:当发现申请的空间不够用时,可以通过 realloc() 函数重新申请更大的存储空间。

例如:

a = (char*)realloc(a, 10*sizeof(char));//前一个参数指申请空间的对象;第二个参数,重新申请空间的大小

使用 malloc 函数申请的存储空间,不会自动释放,需要程序员调用 free() 函数手动释放。如果不手动释放,当程序执行彻底结束,由操作系统进行回收。

例如:

free(a);//释放动态数组a申请的空间

(3)块链存储

块链存储,其实就是借用链表的存储结构来存储串。一般情况下使用单链表就足够了,而且不需要增设头结点。

在构建链表时,每个结点可以存放一个字符,也可以存放多个字符。

【数据结构】串(一)—— 串的基础知识_第1张图片

链表中最后一个结点的数据域不一定全被串值占满,通常会补上 “#” 或者其他特殊的字符和字符串中的字符区分开。

每个结点设置字符数量的多少和存储的串的长度、可以占用的存储空间以及程序实现的功能相关。

//块链存储
typedef struct Chunk{
	char ch[CHUNKSIZE];
	struct Chunk *next;
}Chunk;
typedef struct{
	Chunk *head,*tail;		//串的头尾指针 
	int curlen;				//串的当前长度 
}LString;
三种存储结构的总结

在平时编写程序,经常会用到例如:char *a = ”abcd”;这种方式表示字符串,和上面三种存储方式最主要的区别是:这种方式用于表示常量字符串,只能使用,不能对字符串内容做修改(否则程序运行出错);而以上三种方式都可以对字符串进行删改的操作。

例如:

#include 
int main() {
    char* a="abcd";
    a[1]='b';
    return 0;
}

程序编译可以通过,运行失败,改成下面堆分配存储的方式就对了:

#include 
#include 
#include 
int main() {
    char * a=(char*)malloc(4*sizeof(char));
    strcpy(a, "abcd");
    a[1]='e';
    printf("%s",a);
    return 0;
}

三种存储表示方式中,最常用的是堆分配存储,因为它在定长存储的基础上通过使用动态数组,避免了在操作串时可能因为申请存储空间的不足而丢失字符数据;和块链存储方式相比,结构相对简单,更容易操作。

三、串的操作

字符串可以有丰富的操作,在日常非数值运算大量的都是字符串的操作。归纳总结可以大致分为以下操作

(1)赋值 strassign()

(2)判空 strlen() strcmp()

(3)字符串比较 strcmp

(4)求字符串长度 strlen

(5)字符串拷贝 strcpy(数组名、字符串);

(6)字符串连接 strcat

(7)求字符串子串(截取字符串)

(1)串的初始化(以下基于顺序表):

#include
#include
#include
#define SIZE 20
typedef struct Str
{
    char elem[SIZE];//elem数组用来存放串的元素
    int length;//定义一个串长度
}Str;
//为串分配空间、赋值
int StrAssign(HString &T,char *chars){
	int i;
//	if(T.ch)free(T.ch);					//释放T原有空间 【1】 
	char *c=chars;
	for(i=0;*c!='\0';++i,++c);		//求chars的长度i
	if(!i){
		T.ch=NULL;
		T.length=0;
	}else{
		if(!(T.ch=(char*)malloc(i*sizeof(char))))return 0;	//分配空间的同时判断是否成功分配 
		for(int j=0;j<i;j++)
			T.ch[j]=chars[j];
		T.length=i;
	}
	return 1;
}

(2)串的操作(代码):

void StrAssign(Str *s, const char *chars);//初始化串
void StrCpy(Str*s, Str*t);//拷贝串
int Getlength(Str *s);//获取串的长度
void Clear(Str *s);//清空串s
bool Inset(Str *s,int pos,Str *t);//在串s的pos位置插入串t
void Show(Str *s) ;//打印串
int BF(Str *s,Str *sub,int pos);//在串s里找第一个串sub,并返回串sub的第一个元素的下标
bool DeletePos(Str* s,int pos,int len);//在串中的pos位置开始删除len长度个元素
bool Delete(Str *s,int pos,Str *t);//在串中的pos位置开始删除串t
bool Replace(Str *s,Str *t,Str *v,int pos);//用v替换从pos位置开始的第一个t
bool RepIaceAll(Str *s,Str *t,Str *v);//在串s中查找到串t并用串v替换

void StrAssign(Str *s, const char *chars)//初始化串
{
    assert(s != NULL);
    int len = strlen(chars);

    /*if(s->length
    s->length = len;
    for(int i = 0;i<len;i++)
    {
        s->elem[i] = chars[i];
    }
}

void StrCpy(Str*s, Str*t)//拷贝串
{
    for(int i = 0;i<t->length;i++)
    {
        s->elem[i] = t->elem[i];
    }
    s->length = t->length;
}

int Getlength(Str *s)//获取串的长度
{
    return s->length ;
}

void Clear(Str *s)//清空串s
{
    s->length = 0;
}

bool SubStr(Str *sub,Str *s,int pos,int len)//空白字符不拷贝
{
    if(pos < 0 || len < 1||pos >=s->length||pos+len>s->length-1)
    {
        return false;
    }
    for(int i = pos;i<pos+len;i++)
    {
        sub->elem[i-pos] = s->elem[i];
    }
    sub->length = len;
    return true;
}

bool Inset(Str *s,int pos,Str *t)//在串s的pos位置插入串t
{
    assert(s!=NULL);
    assert(t!=NULL);
    if(pos<0||pos>s->length||pos+t->length>SIZE)
    {
        return false;
    }
    for(int i = s->length-1;i>=pos;i--)
    {
        s->elem[i+t->length] = s->elem[i];  
    }
    for(int j = 0;j<t->length;j++)
        {
            s->elem[j+pos] = t->elem[j];
        }
    s->length +=t->length;
    return true;
}
int BF(Str *s,Str *sub,int pos)//在串s里找第一个串sub,并返回串sub的第一个元素的下标
{
    if(pos<0||pos>s->length)
    {
        return -1;
    }

    int i = pos;
    int j = 0;

    int lens = s->length;
    int lensub = sub->length;

    while (i < lens && j < lensub)
    {
        if (s->elem[i] == sub->elem[j])
        {
            i++;
            j++;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    if (j >= lensub)
        {
            return i - j;
        }
        else 
            return -1;
}

bool DeletePos(Str* s,int pos,int len)//从pos位置开始删除len个长度
{
    assert(s!=NULL);
    if(pos<0||pos+len>s->length||len<0)
    {
        return false;
    }
    //for(int i = pos;i
    for(int i = pos;i<s->length-len;i++)
    {
        s->elem[i] = s->elem[i+len];
    }
    s->length -= len;
    return true;
}

bool Delete(Str *s,int pos,Str *t)//在串中的pos位置开始删除串t
{
    assert(s!=NULL);
    assert(t!=NULL);
    if(pos<0||pos>s->length||t->length>s->length)
    {
        return false;
    }
    int index = BF(s,t,pos);
    if(index < 0)
    {
        return false;
    }
    return DeletePos(s,index,t->length);
}
bool Replace(Str *s,Str *t,Str *v,int pos)//用v替换从pos位置开始的第一个t
{
    assert(s!=NULL);
    assert(t!=NULL);
    assert(v!=NULL);

    int index = BF(s,t,pos);
    if(index < 0)
    {
        return false;
    }
    DeletePos(s,index,t->length);
    return Inset(s,index,v);
}
bool RepIaceAll(Str *s,Str *t,Str *v)//在串s中查找到串t并用串v替换
{
    assert(s!=NULL);
    assert(t!=NULL);
    assert(v!=NULL);

    while(Replace(s,t,v,0));
    return true;
}
void Show(Str *s) //打印  
{
    for(int i=0;i<Getlength(s);i++)
    {
        printf("%c",s->elem[i]); 
    }
    printf("\n");
}  


int main()
{
    Str s;
    char *s1 = "abcdecdfcd";
    StrAssign(&s, s1);
    Show(&s);

    Str t;
    char *t1 = "cd";
    StrAssign(&t, t1);
    Show(&t);
    /*
    Inset(&s, 2, &t);
    Show(&s);

    Inset(&s, 7, &t);
    Show(&s);*/

    /*int index = BF(&s,&t,0);
    printf("index = %d\n",index);*/

    /*DeletePos(&s,3,2);
    Show(&s);*/

    /*Str v;
    char *v1 = "zkr";
    StrAssign(&v, v1);
    Show(&v);
    Replace(&s,&t,&v,0);
    Show(&s);*/

    Str v;
    char *v1 = "zkr";
    StrAssign(&v, v1);
    Show(&v);
    RepIaceAll(&s,&t,&v);
    Show(&s);
    system("pause");
    return 0;
}

参考博客:

https://www.jianshu.com/p/65534d21e097

https://www.cnblogs.com/TimVerion/p/11194268.html

https://blog.csdn.net/weixin_42034217/article/details/84891312

你可能感兴趣的:(算法与数据结构,字符串,数据结构,指针,C语言)