串的链式存储及其基本操作实现

我们大部分时间都是在使用数组来存储串 , 然后进行操作的 , 对于链串我们不经常使用, 在这里我们为了练习的需要 , 实现一下链串的基本操作 , 其实也就是插入,删除, 替换 ,然后指针的交替罢了.

下面开始实操:

目录

● 串赋值 : StrAssign(s,cstr)    

● 串复制: StrCopy(s,t)

● 判断串相等 : StrEqual(s,t)  

● 求串长 : StrLength(s)

●串连接: Concat(s,t)

●求子串: SubStr(s,i,j)

●输出串 : DispStr(s)

●插入串 : InsStr(s,i,t)

● 删除串中字符 : DelStr(s,i,j)

 ● 替换子串 : RepStr(s , i, j, t)


我们通常的操作是 在链串里面 ,每个节点存储四个字节的数据, 就是一个字的数据 , 在这个我们方便对数据进行操作 , 每个 节点只放一个字节 , 那一个节点明明可以存储 4 个字节 , 我们只存一个字节 ,那就会空余 3 个字节的空间 , 那也没办法 , 继续操作即可

(c 语言有内存对齐机制 ,所以就算我们定义数据结构的时候, 定义 char 节点, 一样会占用 4 个字节的空间的)

串的链式存储及其基本操作实现_第1张图片

我们定义串的节点 

typedef struct snode
{
    char data;
    struct snode *next;
}LiString;

自定义结构体 , 数据和 本节点的后继节点

● 串赋值 : StrAssign(s,cstr)    

将一个字符串常量cstr赋值给串s
方法: 尾插法


//传入要插入的链表的指针地址, 字符串常量数组
void StrAssign(LiString *&s,char cstr[])
{
    //定义遍历计数器
    int i;
    //定义指向尾指针的变量 r ,创建的新节点p
    LiString *r , *p;
    s = (LiString *)malloc(sizeof(LiString));
    //刚开始尾指针指向 头结点
    r = s;
    //开始遍历赋值
    for(i= 0; cstr[i]='\0'; i++)
    {
        p = (LiString *)malloc(sizeof(LiString));  
        //数据赋值
        p->data = cstr[i];
        //尾指针操作
        //尾结点的后继指针指向新节点
        r->next = p;
        //指向之后,新节点成为尾结点,然后 r指向尾结点
        r = p;
    }
    //尾结点的后继节点置空
    r->next = NULL;
}

● 串复制: StrCopy(s,t)

   将串 t 复制给串 s 
   说白了,就是用一个指针指向 t 的元素,然后对 s 数据进行修改


//传入 要被赋值的串的指针地址 和 要复制的串的指针
//注意: 指针地址会修改原串, 指针并不会修改
void StrCopy(LiString *&s , LiString *t)
{
    //定义一个指针 p 指向t的元素,初始指向 t 的头结点后的第一个元素(头结点一般不保存数据,起标识作用)
    LiString *p = t->next;
    //创建s的头结点
    s = (LiString *)malloc(sizeof(LiString));
    // 定义指针 r 指向 s 的尾结点,初始时,串s里面就一个头结点
    r = s;    
    //下面将t除了头结点之外的元素全部复制给s
    while(p!=NULL)    //当p指向 t的节点不为空时,就循环跳转复制
    {
        //先为 s 的新节点分配空间
    q = (LiString *)malloc(sizeof(LiString));
    //为s的节点赋值
    q->data = p->data;
    //新节点已经创建,开始插入到 s 后面
    //利用尾插法
    //先把尾结点的后继指针指向新节点
    r->next = q;
    //然后把尾指针指向新节点 q
    r=q;
    //然后 指向我们复制的串的指针 向后移动,继续复制新元素
    p = p->next;            
    }
    // 当复制完后 , 串s的尾指针指向空 
    r->next = NULL;
}

● 判断串相等 : StrEqual(s,t)
  

 若两个串 s 与 t 相等返回真(1);否则返回假(0)
//传入两个比较的串
bool StrEqual(LiString *s,LiString *t)
{
    //我们要比较的不是头结点,而是后面的节点
    //头结点里面存储的是, 识别串的信息
    LiString *p = s->next;
    LiString *q = t->next;
    //当两者指向的指针指向空,跳出 ,或者两者所指向的数据区不一样跳出
    while( p != NULL && q! = NULL && p->data == q->data)
    {
        //开始比较,跳转指针
        p = p->next;
        q = q->next;
    }            
    //我们要判断跳出的是那两种情况
    //如果p 和 q 都指向空的时候 ,我们就说明已经遍历完才跳出,不然的话,就是数据不一致才跳出
    if( p == NULL && p == NULL)
    {
        return true;
    }
    else
    {
        return false;
    }
}

● 求串长 : StrLength(s)


// 返回串的串长
//说白了就是遍历一下串而已,加一个计数器

//传入要计算的串的长度
int StringLength(LiString *s)
{
    //计数器初始为0
    int i = 0;
    //用一个指针来遍历,初始指向头结点的后继节点
    LiStirng *p = s->next;
    //不为空时,继续遍历
    while(p!=NULL)
    {
    // 不为空,代表没有遍历到最后,加一即可
        i++;
    //接着遍历下一个
        p = p->next;
    }
    //为空则跳出
    //返回i的值即可
    return i;
}

●串连接: Concat(s,t)

将两个串 s 和 t 连接形成新串,返回这个新串


代码如下:

//我们遍历两个串,然后把串的数据赋值给一个新串
//传入两个串的指针
LiString  *Concat(LiString *s,LiString *t)
{
    //既然我们要遍历这两个串,就要用指针指向串元素
    //定义指针 *p 指向要被串连的串的数据
    LiString *p;
    //插入新串,我们就要创建一个新节点 *q
    LiStirng *q;
    //我们要插入到新串的结尾,需要用到尾插法,所以需要定义指针r指向尾指针
    LiStirng *r;
    //下面我们开始创建新串节点
    str = (LiString *)malloc(sizeof(LiString));
    //尾指针 r 指向新串 str头结点
    r = str;
    //两个串按次序插入到新串
    //先将 s 的所有节点复制到 str, p初始指向s头结点的后继节点    
    p = s->next
    // P所指向的节点为空时,跳出赋值循环
    while(p!=NULL)    
    {
    //构造新串节点
    q = (LiString *)malloc(sizeof(LiString));
    //数据区赋值
    q->data = p -> data;
    //每创建一个新节点,其都是str的尾结点,所以后继指针置空
    q->next = NULL;
    //开始把新节点,利用尾插法,插入到新串 str
    //新串尾结点指向新节点
    r->next = q;
    //尾指针指向新节点
    r = q;
    //指向 串 s 的指针后移
    p = p->next;
    }
    //当p下一个节点为空时跳出
    //下面将t的所有节点复制到str
    //赋值指针p 指向 t头结点的后继节点
    p = t->next;
    //当p指向的节点不为空时候,一直循环插入
    while(p!=NULL)
    {
        //同理
        q = (LiString *)malloc(sizeof(LiString));
        q->data = p->data;
        r->next = q;
        r = q;
        p = p->next;
    }
    //最后将新串的尾结点的后继指针指向空
    r->next = NULL;
    //返回新串
    return str;
}

●求子串: SubStr(s,i,j)

返回串 s 中从第i个字符开始的,由连续 j 个字符组成的子串
 参数不正确时,返回一个空串
 (1 <= i <= StrLength(s))
 (我们所说的第i个字符是逻辑上所说的位序,从1开始,
  所以子串开始的字符那就是从1到字符串长度)

我们就是创建一个新串,然后从第i个字符开始复制,然后加一个计数器,然后赋值 j 个即可
我们需要检查输入字符的合法性


//传入要求子串的字符串, 逻辑位序,子串个数
LiString *SubStr(LiString *s,int i,int j)
{
    //先创建一个空子串
    //然后非法字符的话才能返回空串
    LiString *str;
    //为新子串分配空间
    str = (LiString*)malloc(sizeof(LiString));
    //我们已经创建了一个头结点,头结点的后继指针置空
    str->next = NULL;
    //我们既然要往子串后面插入节点,就要定义一个指向串尾的指针 *r
    LiString r = str;    //初始指向头结点
    // 我们新建节点 , 需要为新节点分配空间 ,用一个指针指向新节点
    LiString *q;
    //为新节点复制数据的是 串 s ,再来一个指针 p指向 s的节点
    LiString *p = s->next;    //初始指向s头结点的后继节点,即第一个元素, 头结点不算数据元素
    
    //下面判断非法输入
    //如果 i 不在0 到字符串长度之间,或者 从第 i 个元素开始,索要的元素超过串s的长度的话,也返回空串
    // j为索要的元素长度 , i-1表示开始索要元素前,前面已经略过几个元素,再加j 个的话,不能超过s的长度
    if(i<=0 || i>StrLength(s) || j<0 || i-1+j > StrLength(s))
    {
        return str;    //参数非法时, 返回空串
    }
    //通过合法验证,则进行串的复制
    //要从哪里开始复制呢? 从第i个元素
    //进入循环前 ,p= s->next , p指向头结点的后继节点,即第1个数据元素
    // 进入循环后
    //  当k = 0时, p指向第2个数据元素
    // 当 k= 1时 ,p指向第 3 个数据元素
    //当k = 2时 ,p指向第 4个数据元素
    //当 k = i-2 时,跳出,此时 p 指向第 i 个数据元素 

    for(k = 0; knext;        
    }
    //元素已经找到,下面开始复制 j 个元素到新子串
    for( k = 1; k<=j; k++)
    {
        //为新节点创建空间
        q = (LiString *)malloc(sizeof(LiString));
        //数据赋值
        q->data = p->data;
        //尾插法插入元素
        //新元素插入到尾部
        r->next = q;
        //尾指针指向新节点
        r = q;
        // 继续复制元素,我们的p 继续向后移动即可
        p = p->next;        
    }
    //子串尾指针置空
    r->next = NULL;
    //返回子串
    return str;
}

●输出串 : DispStr(s)

所谓输出串 ,就是把链串里面的数据逐个输出而已


//传入要输出的串
void DispStr(LiString *s)
{
    //第一个节点是串s头结点的后继节点
    LiString *p = s->next;
    //开始循环遍历串,指导p指向为空
    while(p!=NULL)
    {
        //开始输出
        printf("%c",p->data);
        //输出之后,指针向后移动
        p = p->next;
    }
    //为了优美,换行
    printf("\n");
}

●插入串 : InsStr(s,i,t)

将串 t 插入到s的第i个字符中,即将t的第一个字符作为s的第i个字符, 并返回新串
参数不正确时,返回一个空串
如下图所示:

//我们要把t的数据插入到 s 的第i个字符中,就是把t的数据节点,从 s第i个元素 前 逐个插入,然后,再把 s 后面从 第i个被断开 的节点,再插入到后面即可


串的链式存储及其基本操作实现_第2张图片


 下面开始代码实操:

//我们先传入要插入和被插入的链串,和插入的位置
LiString *InsStr(LiString *s,int i,LiString *t)
{
	//我们的建议还是不在原串上修改,而是再新建一个新串,然后再逐个复制
	//新建一个str串,并分配空间
	LiString *str;
	str = (LiString *)malloc(sizeof(LiString));
	//后继指针置空
	str->next = NULL;
	//下面开始验证非法字符
	//我们每次都是插入到字符前的,理论上我们可以把字符插入到字符串的任意字符串的间隔,
	//所以我们也能插入到尾部,那尾部的前面是第几个元素呢? 是第i=StrLength(s)+1 个,所以这时也合法
	if(i<1 || i>StrLength(s)+1)
	{
		//非法,返归空串
		return str;
	}
	//接下来,我们需要往新串里面,分三段插入节点
	// ①将s的前 i-1 个节点复制到str
	// ②将t的所有节点复制到str
	// ③将s从第i个节点往后,全部复制到str
	//下面开始我们的实操
	//我们插入的方法是尾插法, 所以需要一个指针r指向 str尾部,初始指向头结点
	LiString *r;
	r = str;
	//然后我们创建新节点,需要指针指向 ,定义 q 为新节点指针
	LiString *q;
	//定义 p 为指向需要复制的节点
	LiString *p;
	//开始第一段复制,p指向s的前 i-1 个节点中第一个节点
	p = s->next;
	for(k=1;k<=i-1;k++)
	{
		//创建新节点,然后赋值
		q = (LiString *)malloc(sizeof(LiString));
		q->data = p->data;
		//开始尾插法,插入到str尾部
		//str尾指针r指向 新节点
		r->next = q;
		//尾指针后移
		r = q;
		// 幅值节点,继续向后移动
		p = p->next;
	}
	//当跳出循环时,p又向后移动了,指向了s的第 i 个元素
	//我们下面,能直接利用p 来进行 t的元素赋值吗? 
	// 答案是:不能,我们此时是利用链表来进行指针赋值,p里面还存放着后续我们所需要的信息
	//所以,另定义一个 p1 来指向 t的元素

	//开始第二段复制,p指向t的第一个节点,复制全部节点
	LiString *p1;
	p1 = t->next;
	//当p不为空,则一直复制
	while(p1!=NULL)
	{
		//尾插法同理
		q = (LiString *)malloc(sizeof(LiString));
		q->data = p1->data;
		r->next = q;
		r = q;
		p1 = p1->next;
	}
	//开始第三段复制,p指向 s的第i个节点,直到最后
	//我们此时p里面存放的是 s第 i个节点的信息
	//下面我们直接开始复制,把元素利用尾插法插入到新串后面
	//当p不为空时继续
	while(p!=NULL)
	{
		//为新节点 q 分配空间
		q = (LiString *)malloc(sizeof(LiString));
		//数据赋值
		q->data = p->data;
		//尾插法开始
		r->next = q;
		r = q;
		//p 继续向后移动
		p = p->next;
	}
	//最后新串尾结点置空即可
	r->next = NULL;
	//返回新串
	return str;
}

● 删除串中字符 : DelStr(s,i,j)

从串s 中删去第 i 个字符开始的长度为 j 的子串 , 并返回剩下的新串,参数不正确时, 返回一个空串 , ( 1 <= i <= StrLength(s))


串的链式存储及其基本操作实现_第3张图片

我们要删除这其中一部分子串, 第 i 个的话 ,  需要一个计数器 , 遍历到 第 i-1 个节点, 然后标记,  接着再 用另一个计数器, 查 j 个元素 , 再把 j 个元素之后的元素, 插入到 第 i-1 个元素的后面即可 . 

我们此时的操作 , 目的是构造一个新串 , 然后去除我们要删除的子串 ,

思路差不多 , 只不过是有选择的复制罢了. 


//传入要删除子串的串指针,删除的元素位序, 删除的元素个数
LiString *DelStr(LiString *s,int i, int j)
{
	//首先第一步,要判断非法字符, 非法返回空串
	//先定义新串吧
	LiString *str;
	str = (LiString *)malloc(sizeof(LiString));
    //后继指针指控
    str->next = NULL;
	//我们要删除的元素,位序在 1到 串长度之间,然后我们从删除的元素开始, 删除的长度不能超过串长度
	//从第i个元素开始 , 说明我们前面需要略过 i-1 个元素 ,然后从第i个元素开始删除
	// 我们需要删除j 个元素 , 但是这不能超过串长度 , 所以
	//  i-1 + j < StrLength(s)
	if(i<1 || i> StrLength(s) || i-1 + j > StrLength(s))
	{
		//非法返回空串
		return str;
	}
	// 如果字符合法 ,则开始有选择复制
	// 我们当然是利用尾插法, 插入到新串str 后面
	// 定义一个尾指针 r , 指向串s的指针 p ,  新节点 q 
	LiString *r,*p,*q;
	//先将s 的前 i-1 个节点复制到 str
	//p 先指向 s 头结点的后继节点,即 第一个数据节点
	p = s->next;
	//新串尾指针先指向头节点 
	r = str;
	int k;
	for(k=1;k <= i-1 ; k++)		
	{
		//构造新节点
		q = (LiString *)malloc(sizeof(LiString));
		//新节点赋值
		q->data = p->data;
		//尾插法实施
		r->next = q;
		r = q;
		//p向后移动指向下一个节点
		p = p->next;
	}
	//当p出来的时候 , p指向的是 第 i个节点
	//而我们要删除从第i 个节点到 第 i-1+j 个节点
	//所以我们需要把 从第(i-1+j + 1 )个节点到最后的节点,复制插入到新串
	//先把p指向第 (i-1+j + 1)个节点,即 第 (i+j)个节点
	for(k=1; k<=j; k++)
	{		
		p= p->next;
		//k=1 时, p指向 第(i+1)个
		//k=2 时, p指向 第(i+2)个
		//k=j 时, p指向 第(i+j)个
	}	
	//跳出的时候, p 指向串s第(i+j)个元素
	//下面就开始,把剩余的元素插入到新串尾部
	//不为空时,循环插入
	while(p!=NULL)
	{
		//构建新节点
		q = (LiString *)malloc(sizeof(LiString));
		//数据复制
		q->data = p->data;
		//尾插法
		r->next = q;
		r = q;
		//指向下一个节点
		p = p->next;
	}
	//直到 p 指向空,则跳出,我们把新串尾结点置空即可
	r->next = NULL;
	//返回新串,就是我们所需要的结果
	return str;
}

 ● 替换子串 : RepStr(s , i, j, t)

在串 s 中,将第 i 个字符开始的 j 个字符构成的子串用串 t 替换 , 并返回产生的新串 , 参数不正确返回一个空串

(1 <= i < =StrLength(s))


串的链式存储及其基本操作实现_第4张图片


 我们从第 i 个开始替换 ,就把串 s 的第 i-1个元素指向 t 的第一个元素 ,然后 t 的尾结点的后继指针指向 串 s 的第 i+j 个元素即可


我们 为了不破坏原串 ,所以选择创建新串 ,来进行有选择复制即可

我们要时刻注意,我们覆盖的指针 , 里面存放的信息 ,后续是否需要使用

 先复制 串 s 的前 i-1 个元素 ,然后复制 串 t 的元素 ,接着 让指向串s 的指针跳转过 j 个元素, 指向第 i+j 个元素 ,接着复制 , 构造一个符合结果的新串即可. 


下面开始代码实操:

//传入要被替换的串,替换的位置 ,替换的个数 ,替换的串
LiString *RepStr(LiString *s,int i, int j, LiString *t)
{
	//首先判断输入是否合法
	//不合法返回新串
	//返回新串 ,就要先定义新串
	LiString *str;
	str = (LiString *)malloc(sizeof(LiString));
	//后继指针置空
	str->next = NULL;
	//参数非法处理
	// 要被替换的位置,是 从 1 到 串的长度 , 个数的话,被替换位置前,已经有 i-1 个元素,
	//长度 i-1 + j < 串元素长度	
	if(i<1 || i>StrLength(s) || i-1+j > StrLength(s))
	{
		//如果是非法字符,返回空串
		return str;
	}
	//通过非法检验, 就开始往复制插入字符
	//我们要通过尾插法,把要选择复制的元素赋值给新串
	//先定义一个尾指针指向新串str,初始指向头结点
	LiString *r;
	r = str;
	//下面进行四段操作
	//① 将s的前 i-1 个节点复制到str
	//② 让p沿next 跳j个节点
	//③ 将 t所有节点复制到str
	//④ 将p及其后的节点复制到str
	//我们现在需要定义指向s的指针 *p , 构造新节点*q 
	// 指向 t 的节点 *p1 
	LiString *p;
	LiString *q;
	LiString *p1;
	//注意: 我们不能让 s 和 t 公用一个指针,因为我们需要记录s的指针,以备后用,把s后续节点插入到串str	
	//第一段
	//p先指向s的第一个节点,即 头结点的后继节点
	p = s->next;
	for(k = 1; k <= i-1; k++)
	{
		//为新节点分配空间
		q = (LiString *)malloc(sizeof(LiString));
		//数据赋值
		q->data = p->data;
		//每创建一个节点,其都是str的尾结点,所以后继指针置空
		q->next = NULL;
		//str的尾指针指向新节点q
		r->next = q;
		//尾指针后移
		r = q;
		//接着赋值节点后移
		p = p->next;
	}
	//跳出循环时, p指向第i个节点
	//为了后续接着把串s被替换后的节点插入到str,先将p跳过被替换的节点
	//我们从第i个节点, 跳过j个节点, 最后一个被替换的节点是 第(i+j-1)个
	//那下一个节点第(i+j)个就是需要复制的
	for(k=1; k<=j; k++)
	{
		p=p->next;
		//k=1时,p指向s的第i+1个节点
		//k=2时,p指向s的第i+2个节点
		//k=j时,p指向s的第i+j个节点
	}
	//p的指针已经保存好,接下来进行第二段

	//接着进行第二段,把要替换的串,复制给新串
	//p1指向新串的第一个数据节点
	p1 = t->next;
	//不为空时,继续
	while(p1!=NULL)
	{
		//创建新节点
		q = (LiString*)malloc(sizeof(LiString));
		//数据复制
		q->data = p1->data;
		//新节点后继指针置空
		q->next = NULL;
		//把新节点利用尾插法,插入到str结尾
		r->next = q;
		r = q;
		//赋值指针 p1继续向后移动
		p1 = p1->next;
	}
	//开始第三段赋值
	//把 p里面存放的第 i+j个元素开始,到串s结束,插入到str
	//p此时指向第(i+j)个元素
	while(p!=NULL)
	{
		//构造新节点
		q = (LiString*)malloc(sizeof(LiString));
		//赋值
		q->data = p->data;
		//新节点后继指针置空
		q->next = NULL;
		//利用尾插法,插入到str结尾
		r->next = q;
		r = q;
		//赋值节点后移
		p = p->next;
	}
	//为了保险起见,str后继节点置空
	r->next = NULL;
	//返回新串 str
	return str;
}

你可能感兴趣的:(笔记,1024程序员节,数据结构,算法)