链串,就是用链表存储字符串,一些基本操作和顺序串一样,但是写法不同,单链表类似。
存储结构部分:
#include<iostream>
#include<stdio.h>
#define maxn 100
using namespace std;
typedef struct Lnode
{
char data;//链串的数据域是char类型的
struct Lnode *next;//next域是结构体 Lnode,递归定义
}Lnode,*Linkstring;//起两个其他名字的,一个是Lnode普通结构体类型,另一个是指针类型的。
先讲输入,这个输入就是链表的尾插法创建。输入以#结尾的字符串,来创建串。
void CreateList(Linkstring &L)//这里要传引用型的参数
{
char temp;
Lnode *r,*p;//定义两个结点指针
L=(Linkstring)malloc(sizeof(Lnode));
L->next=NULL;//这两行相当于给串的头结点开辟空间,初始化。
r=L;//这个一定要有。我们每次移动的相当于头结点地址的副本。
//这样我们就能保留头结点地址,因此才能保证找到串。
cin>>temp;//输入第一个字符,这里的逻辑和顺序串一样。
while(temp!='#')
{
p=(Linkstring)malloc(sizeof(Lnode));
p->data=temp;//一定要写这两句话,开辟新节点。
//新节点存放新元素
r->next=p;//新结点插入到尾部
r=p; //尾指针指向新节点,指到最后
cin>>temp;//再次输入
}
r->next=NULL;//加上这句话好知道链表的结束标志,输出时能用到。
}
这个输入就相当于插入的特殊情况,搞清楚while里面的几句话就明白链串的核心操作了。想插入新元素的时候,一定要先new 新结点,这是重重之中。
再讲一下输出,输出比较简单。
void print_Mystring(Linkstring L)//不需要引用型变量
{
Lnode *p;
p=L->next;//p指向头结点的下一个结点,即首元结点
while(p)//当p非空指针时
{
cout<<p->data;//输出数据域
p=p->next;//向后移动
}
cout<<endl;//输出换行
}
获取长度,和输出差不多,就是遍历,只不过是吧=把输出换成计数,i++,然后return
int string_leng(Linkstring s)
{
int i=0;
Lnode *p;
p=s->next;
while(p!=NULL)
{
i++;
p=p->next;
}
return i;
}
插入算是几个操作中最难的了,这里设计的几个操作都是不改变传入的,母串,生成新的子串。所以我们用结构体指针 返回类型的函数。
Linkstring string_insert(Linkstring s,int index,Linkstring temp)//被插入的串,开始插入的位置,插入的串。
{
int i=1,j=0;
int temp_len=string_leng(temp),s_len=string_leng(s);//获取长度
Lnode *p,*q,*result,*re,*tmp;//定义许多临时指针结点,并开辟空间。
p=(Linkstring)malloc(sizeof(Lnode));
q=(Linkstring)malloc(sizeof(Lnode));
result=(Linkstring)malloc(sizeof(Lnode));//存放结果,需要return 的结点。
re=(Linkstring)malloc(sizeof(Lnode));
p=s->next;//母串首元结点的副本
q=temp->next;//同上
re=result;//同上
while(i<index)//当小于开始位置时,把母串结点一次赋给结果结点。都是通过副本进行操作。
{
tmp=(Linkstring)malloc(sizeof(Lnode));//这个temp,叫临时结点,和输入操作的p结点一样,new一个新节点。新结点存数据,然后接到单链表的后面
tmp->data=p->data;//赋值
re->next=tmp;//接到结果结点副本后面
re=tmp;//re往后移动,总是代表位结点。
p=p->next;//母串也向后移
i++;//计数
}
while(j<temp_len)//同样的道理,从start以后,把插入的串,拼到结果串的后面。
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=q->data;
re->next=tmp;
re=tmp;
q=q->next;
j++;
}//拼完插入串,我们接着拼母串,这就相当于在母串中间插入一个其他的串,从而生成新串。
while(i<=s_len)
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
re->next=NULL;//尾结点的next指向NULL
return result;//返回result
}
搞懂了插入串之后,其他和他类似,创建副本,对副本进行操作,然后return。
删除操作和插入类似,相同的语句完全可以复制上面的,稍微改一下即可。不然每次都打这么多,效率比较低
Linkstring string_delete(Linkstring s, int start,int num)
{
int i=1,j=0;
int s_len=string_leng(s);
Lnode *result,*re,*p,*tmp;
result=(Linkstring)malloc(sizeof(Lnode));
re=(Linkstring)malloc(sizeof(Lnode));
p=(Linkstring)malloc(sizeof(Lnode));
p=s->next;
re=result;
while(i<start)//这部分和插入第一部分一样
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
if(start+num>s_len)//从起始位置没有num个字符,即不够删的就全删
{
re->next=NULL;
return result;
}
else
{
while(i<start+num)//这步就相当于删除,我们把往后移num,但是没有赋值给result的副本
{
i++;
p=p->next;
}
while(i<=s_len)//这和插入的第三部分一样
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
re->next=NULL;
return result;
}
}
这样看来,是不是插入和删除非常像啊。其他几个操作也类似。
替换也类似。
Linkstring string_replace(Linkstring s,Linkstring s1,int start)//开始位置,和替换的字符串
{
int i=1,j=0;
int s_len=string_leng(s),s1_len=string_leng(s1);
Lnode *result,*re,*p,*tmp,*q;
result=(Linkstring)malloc(sizeof(Lnode));
re=(Linkstring)malloc(sizeof(Lnode));
p=(Linkstring)malloc(sizeof(Lnode));
q=(Linkstring)malloc(sizeof(Lnode));
p=s->next;
q=s1->next;
re=result;
while(i<start)//和插入第一部分一样
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
while(i<=s_len&j<s1_len)//设计是是不改变原来字符串的长度去替换
//啥意思呢,比如:ABCDEF 从第5的个位置替换成 XYZ 得 ABCDXY
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=q->data;
re->next=tmp;
re=tmp;
q=q->next;
p=p->next;//s也要向前走!!!!!!!!,母串也向前走
i++;j++;
}
while(i<=s_len)//和插入第三部分一样
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
re->next=NULL;
return result;
}
到这是不是和插入操作都差不多啊,下面就剩下两个操作了。
先看提取
Linkstring string_sub(Linkstring s,int start,int sub_len)
{
int i=1,j=0;
int s_len=string_leng(s);
Lnode *result,*re,*p,*tmp;
result=(Linkstring)malloc(sizeof(Lnode));
re=(Linkstring)malloc(sizeof(Lnode));
p=(Linkstring)malloc(sizeof(Lnode));
p=s->next;
re=result;
while(i<start)//这个稍微不太一样,我们一直往后移,移动的开始位置,而不赋值
{
p=p->next;
i++;
}
while(i<=s_len&&j<sub_len)//这个和插入第二部分是不是一样。我们加了一个i<=s_len条件,就是想不够提取的时候停止提取,例如:ABCDEF 从第五个提取4个得 EF
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;j++;
}
re->next=NULL;
return result;
}
剩下一个连接也很简单。可以看成插入到最后位置。所以就少了,插入操作的第三while部分。
Linkstring string_connect(Linkstring s,Linkstring s2)
{
int i=0,j=0;
int s2_len=string_leng(s2),s_len=string_leng(s);
Lnode *p,*q,*result,*re,*tmp;
p=(Linkstring)malloc(sizeof(Lnode));
q=(Linkstring)malloc(sizeof(Lnode));
result=(Linkstring)malloc(sizeof(Lnode));
re=(Linkstring)malloc(sizeof(Lnode));
p=s->next;
q=s2->next;
re=result;
while(i<s_len)//把第一个串一直赋值给结果串
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=p->data;
re->next=tmp;
re=tmp;
p=p->next;
i++;
}
while(j<s2_len)//把第二串一直赋值给结果串
{
tmp=(Linkstring)malloc(sizeof(Lnode));
tmp->data=q->data;
re->next=tmp;
re=tmp;
q=q->next;
j++;
}
re->next=NULL;
return result;
}
这样就写完了几个重要操作,起始都是大同小异,无非就是,遍历找起始位置,然后赋值。理解一个了,其他自然理解。
下面简单写一下主函数如何调用的。
int main()
{
int length,index;
//定义
Linkstring s,s1,s2,s3,s4;
Linkstring &temp=s;
Linkstring &temp1=s1;
//创建,输出
CreateList_R(temp);
CreateList_R(temp1);
print_Mystring(s);
//串长
length=string_leng(s);
cout<<"s的串长为:"<<length<<endl;
//插入
cin>>index;
s2=string_insert(s,index,s1);
//删除
int start,del_len;
cin>>start>>del_len;
s2=string_delete(s,start,del_len);
//替换
cin>>start;
s2=string_replace(s,s1,start);
//提取
int sub_len;
cin>>start>>sub_len;
s3=string_sub(s,start,sub_len);
//连接
s4=string_connect(s,s2);
print_Mystring(s4);
return 0;
}