🌳结构声明
typedef struct snode {
char data;
struct snode* next;
}LinkStrNode;
- 也是需要一个数据域,然后既然是链式结构,那一定有指针
🌳分步算法实现分析【⭐⭐⭐】
🍃生成串
- 首先我们来看如何使用链式思维去实现一个串
- 参数的话还是一样,一个链串类型,一个字符数组,上面说了,形成一个链串需要链式思维,那对于链表我们是怎么构建的呢?大家还记得吗,使用尾插法会来的更加好一些,因此我们下面的算法操作都是基于尾插法的来实现的,我来一步步给大家说一下
- 首先你需要定义一个头结点指针,用于头结点的存储,然后在定义一个新生结点的指针,用于存放后续结点。接着就是尾插法的基本思路,使用【malloc】开辟空间,一开始只有一个头结点,因此它就是尾结点
- 接着取遍历这个字符数组,取出里面的每一个字符,为新的结点开辟空间,然后将字符一一放入data域,将此结点链在尾结点后,然后这个结点成为新的尾结点
- 最后不要忘了将尾结点的next域置为空哦,这很重要❗
void StrAssign(LinkStrNode*& s, char cstr[])
{
LinkStrNode* r, * p;
//为头结点开辟空间
s = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = s;
for (int i = 0; cstr[i] != '\0'; ++i)
{
//为串s的后继结点开辟空间
p = (LinkStrNode*)malloc(sizeof(LinkStrNode));
p->data = cstr[i];
//尾插法
r->next = p; r = p;
}
r->next = NULL;
}
🍃销毁串
- 然后来说说如何销毁一个串,这和销毁链表其实是一个道理。
- 你要销毁一个结点,那就要判断它的下一个结点是否为空,若是不为空,就进入循环,下一个结点不为空,就free掉这个结点,然后进行一个交替更换,直至扫描完所有的结点位置
- 最后别忘了当扫描到最后一个尾结点的时候,跳出循环要单独再做一个free
void DestroyStr(LinkStrNode*& s)
{
LinkStrNode* pre = s, * p = s->next;
while (p != NULL)
{
free(pre);
pre = p;
p = pre->next;
}
free(pre);
}
🍃串的复制
- 首先一样,是基于尾插法的整体思路,可以看到,这里是给目标串s做了一个引用,所以是要将串t复制到串s中去。
- 接着取思考一下我们的需求,我们需要一个头结点现将串s放进去,然后需要一个结点指针去扫描复制串t的,然后对于新增加的结点,还需要一个新增结点指针
- 接下去就是算法思路,具体不做讲解,与生成串一致。就是这个【q->data = p->data】,就是将p中即扫描的串t的内容放到新生结点中
void StrCopy(LinkStrNode*& s, LinkStrNode* t)
{
LinkStrNode* r, * p = t->next, * q;
//为头结点开辟空间
s = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = s;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; //将串t中的结点放入q中暂存
r->next = q;
r = q; //尾插法
p = p->next;
}
r->next = NULL;
}
🍃判断两个串是否相等
- 然后去比较两个串是否相等,思路很简单,我们来看看
- 首先你需要两个指针,一个指向串s的内容,另一个指向串t的内容。接着将它们放到一个循环里分别去扫描即可,若是相等的话继续向下扫描,直至尾结点位置,其中若是发现 有不相同的元素,则跳出循环
- 在循环外面,可以看到我是有两个判断,一个是判断是否两个指针都扫描到了两个串的尾部,若是,则表明两个串是相等匹配的,返回true;若不是,则表明上面的循环是中途结束的,指针并没有扫描到结尾,返回false
bool StrEqual(LinkStrNode* s, LinkStrNode* t)
{
LinkStrNode* p = s->next, * q = t->next;
while (p != NULL && q != NULL && p->data == q->data)
{
p = p->next;
q = q->next;
}
if (p == NULL || q == NULL) //表明此时已经扫描到串尾
return true;
else
return false;
}
🍃求串的长度
- 求解串的长度一样是和链表一样,从首结点开始扫描,使用循环控制,直至尾结点即可,使用一个变量记录循环次数,最后返回
int StrLength(LinkStrNode* s)
{
int i = 0;
LinkStrNode* p = s->next;
while (p != NULL)
{
i++;
p = p->next;
}
return i;
}
🍃连接两个串
- 连接串的思路其实和顺序串一致,也是先定一个存储串,然后先将串s放入,接着再将串t做一个链接
- 一样分析一下需求:存放结果串的指针、存放串s的指针、新建的结点指针以及尾指针
- 内部思路就是尾插法的思路,便不做过多叙述。这里的话就是将这个一开始存放串s的指针p做了一个再次利用
LinkStrNode* ConCat(LinkStrNode* s, LinkStrNode* t)
{
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = str;
//先将s存放到str中
p = s->next;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//再循环利用变量将t也存入str中,即接在s之后
p = t->next;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
🍃取子串
- 然后是取子串的部分,一样需要先去判断要取出的子串是否合法,若不合法,直接返回定义出来的空串即可
- 这里也是一样先使用指针p存放串s的内容,接着使用循环去遍历,但这还是用到了一个技巧,因为要取的子串是从位置i开始,那么直接使用循环遍历到了这个地方,然后的话其实就又是一个尾插法的操作,从这个位置开始,将s串中的部分一一存放到串str中,最后将尾指针置为空,返回即可
LinkStrNode* SubStr(LinkStrNode* s, int i, int j)
{
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL; //若不将str置为空串,则输入放入会出现异常
r = str;
if (i <= 0 || i > StrLength(s) || j < 0 || i - 1 + j > StrLength(s))
return str;
//利用指针p先行遍历到位置i
p = s->next;
for (int k = 1; k < i; ++k)
p = p->next;
//在从位置i开始遍历到位置j取出子串
for (int k = 1; k <= j; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
🍃在串1指定位置插入串2
- 一样,也是顺序串的思路,先将前半部分放到目标串中,然后在后插入串,接着将后半部分继续放入即可
- 指针数量还是一样,只是多了一个指针去遍历插入串t,细心的同学在看了上篇文章后,一定也发现了这里也使用了边界条件的判断,上次我们说过,这个插入串的话是可以插入到一个串的末尾的,所以是其长度length + 1,但是超出就不行了
- 接着三步走,完成插入操作
- 第一步,将串s的前半部分,也就是从1遍历到i - 1的位置,一样使用尾插法插入
- 第二步,串s的前半部分插入完成后,使用指针p1继续去遍历串t,一样的思路放入串str中
- 第三步,放入串s的后半部分,被忘了,此时这个指针p还在i的位置,只需要继续使用这个指针p,利用尾插法插入即可
LinkStrNode* InsStr(LinkStrNode* s, int i, LinkStrNode* t)
{
int k = 1;
LinkStrNode* str, * p, * p1, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
//判断下标i是否越界
if (i <= 0 || i > StrLength(s) + 1)
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q= (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//利用指针p1,将串t中的结点顺势放入str中
p1 = t->next;
while (p1 != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p1->data;
r->next = q; r = q;
p1 = p1->next;
}
//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
🍃删除一个串中指定长度的串
- 不要看代码越来越长,其实这就是【纸老虎】,实现的思路还是很简单,内部的原理都是使用尾插法,只需要在外层控制一下指针的移动即可
- 首先一样先来看一下这个边界的判断,顺序串中说过,插入串不需要判断长度,但是删除需要,还有就是这个边界条件,目标串的最后部分是不可以被删除的,因为根本没有内容可以给你删
- 删除操作,一样是现将前i个字符放入目标串中,接着我们再度利用指针p,通过一个循环,将这j个位置直接遍历过。循环结束时,p就处于j的位置,然后利用这个指针将剩余的内容放入目标串中即可
LinkStrNode* DelStr(LinkStrNode* s, int i, int j)
{
int k = 1;
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || i - 1 + j > StrLength(s))
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//再利用指针p,直接遍历过j个位置
for (k = 0; k < j; ++k)
p = p->next;
//此时的指针p,已然移动到j的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
🍃将一个串中从某一位置开始的指定长度替换为另一个串
- 一眼望去代码很长,你会怕吗?要记住这只是【纸老虎】罢了
- 顺序串里也说到过,替换串的操作和插入串很像,你也可以再拉上去看看,是不是真的很像
- 你可以进行一个对比,然后就可以发现,又是插入和删除两个算法操作的结合
- 对于替换串来说,和删除串一样,超过length + 1的位置是不可以替换的,替换串的话不想插入串那样,不要考虑长度,替换的话肯定是要考虑你所使用的替换串的长度
- 替换操作的整体思路也会差不多,利用指针做一个后移,内部实现逻辑还是尾插法,如果你认真看了上面的内容,相信你对这一个算法不会陌生,自己分析着试试吧😀
LinkStrNode* RepStr(LinkStrNode* s, int i, int j, LinkStrNode* t)
{
int k = 1;
LinkStrNode* str, * p, * p1, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
//判断下标i是否越界
if (i <= 0 || i > StrLength(s) || j<0 || i - 1 + j>StrLength(s))
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; q->next = NULL;
r->next = q; r = q;
p = p->next;
}
for (k = 0; k < j; ++k)
p = p->next; //直接删除原本串s中从第i位开始长度为j的那一子串
//利用指针p1,将串t中的结点顺势放入str中
p1 = t->next;
while (p1 != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p1->data; q->next = NULL;
r->next = q; r = q;
p1 = p1->next;
}
//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; q->next = NULL;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
🍃输出串
void DispStr(LinkStrNode* s)
{
LinkStrNode* p = s->next;
while (p != NULL)
{
printf("%c", p->data);
p = p->next;
}
printf("\n");
}
🌳测试运行结果
🐚测试1
void test1()
{
printf("*测试串的生成*\n");
LinkStrNode* s1;
char ch1[12] = "Hello World";
StrAssign(s1, ch1);
printf("串s初始化为:");
DispStr(s1);
printf("\n*测试串的复制*\n");
LinkStrNode* s2;
char ch2[6] = "Frank";
StrAssign(s2, ch2);
StrCopy(s1, s2);
printf("复制后的串s1为:");
DispStr(s1);
printf("\n*测试串是否相等*\n");
LinkStrNode* s3, * s4, * s5;
char ch3[6] = "apple";
char ch4[6] = "apple";
char ch5[7] = "banana";
StrAssign(s3, ch3);
StrAssign(s4, ch4);
StrAssign(s5, ch5);
if (StrEqual(s3, s4))
printf("s3与s4两串相等\n");
else
printf("s3与s4两串不相等\n");
if (StrEqual(s3, s5))
printf("s3与s5两串相等\n");
else
printf("s3与s5两串不相等\n");
printf("\n*测试串的长度*\n");
int len = StrLength(s5);
printf("显示一下串s5:");
DispStr(s5);
printf("串s5的长度为:%d\n", len);
printf("\n*测试串的连接*\n");
LinkStrNode* s6;
LinkStrNode* s7;
LinkStrNode* s8;
char ch6[3] = "so";
char ch7[6] = " good";
StrAssign(s6, ch6);
StrAssign(s7, ch7);
s8 = ConCat(s6, s7);
printf("串s6与串s7连接之后为:");
DispStr(s8);
}
🐚测试2
void test2()
{
printf("*测试求子串*\n");
LinkStrNode* s1, * s2;
char ch1[10]= "wonderful";
StrAssign(s1, ch1);
printf("初始化的串s1为:");
DispStr(s1);
printf("串s1从第2个字符开始的连续4个字符的子串为:");
s2 = SubStr(s1, 2, 4);
DispStr(s2);
printf("\n*测试子串的插入*\n");
LinkStrNode* s3,* s4;
char ch3[10] = "ok";
StrAssign(s3, ch3);
printf("在串s1的第3个位置插入串s3之后为:");
s4 = InsStr(s1, 3, s3);
DispStr(s4);
printf("\n*测试子串的删除*\n");
LinkStrNode* s5, * s6;
char ch5[10] = "fantistic";
StrAssign(s5, ch5);
printf("将串s5的第5个字符开始的长度为2的子串删除后为:");
s6 = DelStr(s5, 5, 2);
DispStr(s6);
printf("\n*测试子串的替换*\n");
LinkStrNode* s7;
LinkStrNode* s8;
LinkStrNode* s9;
char ch7[15] = "accountability";
char ch8[3] = "le";
StrAssign(s7, ch7);
StrAssign(s8, ch8);
printf("将串s7从第10个字符开始的长度为2的子串替换成s8后为:");
s9 = RepStr(s7, 10, 5, s8);
DispStr(s9);
}
🌳整体代码展示
#include <stdio.h>
#include <malloc.h>
typedef struct snode {
char data;
struct snode* next;
}LinkStrNode;
/*生成串*/
void StrAssign(LinkStrNode*& s, char cstr[])
{
LinkStrNode* r, * p;
//为头结点开辟空间
s = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = s;
for (int i = 0; cstr[i] != '\0'; ++i)
{
//为串s的后继结点开辟空间
p = (LinkStrNode*)malloc(sizeof(LinkStrNode));
p->data = cstr[i];
//尾插法
r->next = p; r = p;
}
r->next = NULL;
}
/*摧毁串*/
void DestroyStr(LinkStrNode*& s)
{
LinkStrNode* pre = s, * p = s->next;
while (p != NULL)
{
free(pre);
pre = p;
p = pre->next;
}
free(pre);
}
/*串的复制*/
void StrCopy(LinkStrNode*& s, LinkStrNode* t)
{
LinkStrNode* r, * p = t->next, * q;
//为头结点开辟空间
s = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = s;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; //将串t中的结点放入q中暂存
r->next = q;
r = q; //尾插法
p = p->next;
}
r->next = NULL;
}
/*判断串相等*/
bool StrEqual(LinkStrNode* s, LinkStrNode* t)
{
LinkStrNode* p = s->next, * q = t->next;
while (p != NULL && q != NULL && p->data == q->data)
{
p = p->next;
q = q->next;
}
if (p == NULL || q == NULL) //表明此时已经扫描到串尾
return true;
else
return false;
}
/*求串长*/
int StrLength(LinkStrNode* s)
{
int i = 0;
LinkStrNode* p = s->next;
while (p != NULL)
{
i++;
p = p->next;
}
return i;
}
/*串的连接*/
LinkStrNode* ConCat(LinkStrNode* s, LinkStrNode* t)
{
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
r = str;
//先将s存放到str中
p = s->next;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//再循环利用变量将t也存入str中,即接在s之后
p = t->next;
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
/*求子串*/
LinkStrNode* SubStr(LinkStrNode* s, int i, int j)
{
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL; //若不将str置为空串,则输入放入会出现异常
r = str;
if (i <= 0 || i > StrLength(s) || j<0 || i - 1 + j>StrLength(s))
return str;
//利用指向p先行遍历到位置i
p = s->next;
for (int k = 1; k < i; ++k)
p = p->next;
//在从位置i开始遍历到位置j取出子串
for (int k = 1; k <= j; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
/*子串的插入*/
LinkStrNode* InsStr(LinkStrNode* s, int i, LinkStrNode* t)
{
int k = 1;
LinkStrNode* str, * p, * p1, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
//判断下标i是否越界
if (i <= 0 || i > StrLength(s) + 1)
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q= (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//利用指针p1,将串t中的结点顺势放入str中
p1 = t->next;
while (p1 != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p1->data;
r->next = q; r = q;
p1 = p1->next;
}
//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
/*子串的删除*/
LinkStrNode* DelStr(LinkStrNode* s, int i, int j)
{
int k = 1;
LinkStrNode* str, * p, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
if (i <= 0 || i > StrLength(s) || i - 1 + j > StrLength(s))
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
//再利用指针p,直接遍历过j个位置
for (k = 0; k < j; ++k)
p = p->next;
//此时的指针p,已然移动到j+1的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
/*子串的替换*/
LinkStrNode* RepStr(LinkStrNode* s, int i, int j, LinkStrNode* t)
{
int k = 1;
LinkStrNode* str, * p, * p1, * q, * r;
str = (LinkStrNode*)malloc(sizeof(LinkStrNode));
str->next = NULL;
r = str;
//判断下标i是否越界
if (i <= 0 || i > StrLength(s) + 1 || j<0 || i - 1 + j>StrLength(s))
return str;
//利用指针p,先将前0~i-1个字符存入str
p = s->next;
for (int k = 1; k < i; ++k)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; q->next = NULL;
r->next = q; r = q;
p = p->next;
}
for (k = 0; k < j; ++k)
p = p->next; //直接删除原本串s中从第i位开始长度为j的那一子串
//利用指针p1,将串t中的结点顺势放入str中
p1 = t->next;
while (p1 != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p1->data; q->next = NULL;
r->next = q; r = q;
p1 = p1->next;
}
//此时的指针p,已然移动到i的位置,开始将后面的剩余结点继续存入串str中
while (p != NULL)
{
q = (LinkStrNode*)malloc(sizeof(LinkStrNode));
q->data = p->data; q->next = NULL;
r->next = q; r = q;
p = p->next;
}
r->next = NULL;
return str;
}
/*输出串*/
void DispStr(LinkStrNode* s)
{
LinkStrNode* p = s->next;
while (p != NULL)
{
printf("%c", p->data);
p = p->next;
}
printf("\n");
}
🌳总结与提炼