串的結構類似與線性表,只不過串的數據元素是一個字符,即是由零個或多個字符組成的有限序列。
一、串的順序存儲
串的順序存儲結構也就是順序存儲,即串中的字符被依次的存在一組連續的存儲單元中,可以類比線性表的順序存儲,可以寫出其數據結構如下:
typedef struct st { char *ch; //串存放的起始地址,串中第i個字符存儲在ch[i-1]中 int length; //串的長度 int strsize; //分配的存儲空間的大小,如果不足,在通過realloc()分配增加空間 }string;
假設現在的字符串是“friend”,其結構可以用如圖所示:
現在我們拿到這個數據結構第一步是要初始化一個串,即首先在內存中開辟一段連續的存儲空間(大小可以假先預定MAXSIZE個存儲空間),初始化其長度length為0,同時制定其串的最大容量是MAXSZIE,如下:
//串的初始化操作 string CreateNullString() { string s; s.length=0; s.ch=(char*)malloc(MAXSIZE *sizeof(char)); s.strsize=MAXSIZE; return s; }
初始化完成之后,我們可以類比順序表,對串完成一系列操作,如串拷貝、取子串、串賦值等,
程序示例如下:
#include<stdio.h> #include<stdlib.h> #define MAXSIZE 100 typedef struct st { char *ch; //串存放的起始地址,串中第i個字符存儲在ch[i-1]中 int length; //串的長度 int strsize; //分配的存儲空間的大小,如果不足,在通過realloc()分配增加空間 }string; //串的初始化操作 string CreateNullString() { string s; s.length=0; s.ch=(char*)malloc(MAXSIZE *sizeof(char)); s.strsize=MAXSIZE; return s; } //判斷空串 int isEmpty(string s) { if(s.length==0) return 1; else return 0; } //賦值操作 void StringAssign(string *s1,char s2[]) { int i=0; while(s2[i]!='\0') // '\0' 是字符串的結束符,任何字符串之后都會自動加上'\0' i++; //計算s2的長度 if(i>s1->strsize) { //所賦值的字符數組超過字符串的默認容量,則增加存儲空間 s1->ch=(char*)malloc(i*sizeof(char)); s1->strsize=i; } s1->length=i; for(i=0;i<s1->length;i++) s1->ch[i]=s2[i]; //從第一個字符開始逐個字符賦值 } //串拷貝操作 void StringCopy(string *s1,string s2) { if(s1->strsize<s2.length) { s1->ch=(char*)realloc(s1->ch,s2.length*sizeof(char)); s1->strsize=s2.length; } s1->length=s2.length; int i; for(i=0;i<s1->length;i++) s1->ch[i]=s2.ch[i]; } //求串的長度 int StringLength(string s) { return s.length; } //串的連接操作 void concat(string *s,string s1,string s2) { if(s->strsize<s1.length+s2.length) { s->ch=(char*)realloc(s->ch,(s1.length+s2.length)*sizeof(char)); s->strsize=s1.length+s2.length; } s->length=s1.length+s2.length; //兩串連接 int i; for(i=0;i<s1.length;i++) //將s1復制到s中 s->ch[i]=s1.ch[i]; for(;i<s->length;i++) s->ch[i]=s2.ch[i-s1.length]; //將s2復制到s中去 } //取子串操作 int substr(string s,int i,int len,string *t) { /* i表示從字符串s的第i個位置開始截取(索引從1開始) len表示截取字符串的長度 */ if(i<=0 || i>s.length || len<0 || len>s.length-i+1) //參數不合法 return 0; if(t->length<len) //存儲空間不夠,繼續分配存儲空間 { t->ch=(char*)realloc(t->ch,len*sizeof(char)); t->strsize=len; } t->length=len; int k; for(k=0;k<t->length;k++) t->ch[k]=s.ch[i-1+k]; return 1; } //插入操作 int insertString(string *s,int i,string t) { //在字符串s的第i個位置插入字符串t if(i<=0 || i>s->length+1) return 0; if(s->strsize<s->length+t.length) //空間不足 { s->ch=(char*)realloc(s->ch,(s->length+t.length)*sizeof(char)); s->strsize=s->length+t.length; } int k; for(k=s->length-1;k>=i-1;k--) //將s中的后i個字符后移到后面 s->ch[k+t.length]=s->ch[k]; s->length=s->length+t.length; for(k=0;k<t.length;k++) //將t的值賦值給s s->ch[k+i-1]=t.ch[k]; return 1; } //刪除操作 int deleteString(string *s,int i,int len) { //從s的第i個字符開始刪除len個字符 if(i<=0 || i>s->length || len<0 || len>s->length-i+1) //參數不合法 return 0; int k; for(k=i+len-1;k<s->length;k++) //從s的i+len-1個位置開始將其后的所有字符前移 s->ch[k-len]=s->ch[k]; s->length-=len; return 1; } //輸出操作 void print(string s) { int i; for(i=0;i<s.length;i++) printf("%c",s.ch[i]); printf("\n"); } void main() { string s1=CreateNullString(); string s2=CreateNullString(); string s3=CreateNullString(); char ch[MAXSIZE]; printf("請輸入主串:\n"); gets(ch); StringAssign(&s1,ch); printf("主串 s1 為:"); print(s1); StringCopy(&s2,s1); printf("拷貝串操作結果如下,結果如下 s2 :"); print(s2); printf("刪除操作(1——s1.length-3 全刪):"); deleteString(&s2,1,s1.length-3); print(s2); printf("插入操作,插入到s2的第2個位置上,請輸入插入的字符串:"); gets(ch); StringAssign(&s3,ch); insertString(&s2,2,s3); print(s2); printf("取子串操作(取s1的子串【2-4】):"); substr(s1,2,3,&s3); print(s3); printf("串連接操作【將s1與s3合並】:"); concat(&s1,s1,s2); print(s1); }
結果如下:
二、串的鏈式存儲
串的鏈式存儲結構稱為串的鏈式存儲,即串中的每一個節點包括兩個部分:數據域和指針域,其中數據域用來存放字符,指針域用來存放指向下一個節點的指針。
由此可得串的鏈式數據結構如下:
typedef struct node { char ch; //字符域 struct node *next; //指針域,存放下一個結點的地址 }node,*linkstr;
如下所示:
其初始化操作主要是開辟頭結點,同時讓它的下一個指針指向為NULL:
//初始化操作 linkstr CreateNullString() { linkstr s; s=(linkstr)malloc(sizeof(node)); if(s!=NULL) s->next=NULL; return s; }
由此,完整的案例如下:
#include<stdio.h> #include<stdlib.h> //鏈式串 typedef struct node { char ch; //字符域 struct node *next; //指針域,存放下一個結點的地址 }node,*linkstr; //初始化操作 linkstr CreateNullString() { linkstr s; s=(linkstr)malloc(sizeof(node)); if(s!=NULL) s->next=NULL; return s; } //判斷空串 int iEmpty(linkstr s) { if(s->next==NULL) return 1; else return 0; } //賦值操作 void Stringassign(linkstr s,char t[]) { linkstr p,q,r; r=s; //r始終表示的是尾結點(最后一個非空節點,而不是最后一個NULL節點)。 q=s->next; int i; for(i=0;t[i]!='\0';i++) if(q!=NULL) { q->ch=t[i]; r=q; q=q->next; } else { //(初始化時只給頭結點分配了存儲空間或者其他情況),如果需要繼續添加數據(其他節點沒分配空間)需要繼續分配 p=(linkstr)malloc(sizeof(node)); //添加節點 p->ch=t[i]; r->next=p; r=p; } r->next=NULL; //將s中原來多余的空間釋放掉 while(q!=NULL) { p=p->next; free(q); q=p; } } //串拷貝操作 void assign(linkstr s,linkstr t) { //將t串的值賦值給s串 linkstr p,q,r,u; p=t->next; q=s->next; r=s; while(p!=NULL) { //串s原先分配了空間 if(q!=NULL) { q->ch=p->ch; r=q; q=q->next; } //若串s原先的空間不夠用 else { u=(linkstr)malloc(sizeof(node)); u->ch=p->ch; r->next=u; r=u; } //p節點后移 p=p->next; } //同理,若q的長度過長,可以釋放多余的空間 while(q!=NULL) { p=p->next; free(q); q=p; } r->next=NULL; } //求串的長度 int length(linkstr s) { linkstr p; int n=0; p=s->next; while(p!=NULL) { n++; p=p->next; } return n; } //串的連接操作 void contact(linkstr s,linkstr s1,linkstr s2) { linkstr p,q,r,t; r=s; p=s1->next; q=s->next; while(p!=NULL) { if(q!=NULL) { q->ch=p->ch; q=q->next; r=q; } else { //串s原來沒有分配存儲空間,需要申請空間 t=(linkstr)malloc(sizeof(node)); t->ch=p->ch; r->next=t; r=t; } p=p->next; } p=s2->next; while(p!=NULL) { if(q!=NULL) { q->ch=p->ch; q=q->next; r=q; } else { //串s原來沒有分配存儲空間,需要申請空間 t=(linkstr)malloc(sizeof(node)); t->ch=p->ch; r->next=t; r=t; } p=p->next; } //將串s的多余的空間清除掉(這個情況只可能發生在while的if循環中) while(q!=NULL) { p=q->next; free(q); q=p; } r->next=NULL; } //截取子串 int substr(linkstr s,int i,int len,linkstr t) { linkstr p,q,r,u; if(i<=0 || i>length(s) || i+len-1>length(s) ) return 0; //指針指向s的第i-1個位置 int j,k; for(j=0,p=s;j<i;j++) p=p->next; for(k=0,r=t,q=t->next;k<len;k++) { if(q!=NULL) { q->ch=p->ch; r=q; q=q->next; } else { u=(linkstr)malloc(sizeof(node)); u->ch=p->ch; r->next=u; r=u; } p=p->next; } while(q!=NULL) { p=q->next; free(q); q=p; } r->next=NULL; return 1; } //插入操作 int insert(linkstr s,int i,linkstr t) { linkstr p,q,r; if(i<=0 || i>length(s)+1) return 0; //指向第i-1個位置 int j; for(j=0,p=s;j<i-1;j++) p=p->next; q=t->next; while(q!=NULL) { r=(linkstr)malloc(sizeof(node)); r->ch=q->ch; r->next=p->next; p->next=r; q=q->next; p=r; } return 1; } //刪除操作 int deleteString(linkstr s,int i,int len){ linkstr p,q,r; if(i<=0 || i>length(s) || i+len-1>length(s) ) return 0; int j; for(j=0,p=s;j<i-1;j++) p=p->next; for(j=0;j<len;j++) { q=p->next; p->next=q->next; free(q); } return 1; } //打印輸出 void print(linkstr s) { linkstr p=s->next; while(p!=NULL) { printf("%c",p->ch); p=p->next; } printf("\n"); } void main() { linkstr s1; linkstr s2; linkstr s3; s1=CreateNullString(); s2=CreateNullString(); s3=CreateNullString(); char str[100]; printf("請輸入字符串:\n"); gets(str); Stringassign(s1,str); printf("串s1:"); print(s1); printf("串s1的長度為:%d\n",length(s1)); assign(s2,s1); printf("串s2:"); print(s2); printf("串s2刪除操作(第三個位置的三個字符刪除 :"); deleteString(s2,3,3); print(s2); printf("串連接操作(s3=s1+s2 ):"); contact(s3,s1,s2); print(s3); printf("插入操作(從s1的第6個位置插入s3):"); insert(s1,6,s3); print(s1); printf("測試截取子串的功能s2(截取s3的第四個位置長度為4的字符串:"); substr(s3,4,4,s2); print(s2); printf("測試字Contact的清除過多存儲空間的功能:(將兩個較短的字符[兩個s2]合並寫到s1上去:"); contact(s1,s2,s2); print(s1); }
結果如圖: