數據結構(12.2)串之定長順序串
前言
終於考完試放假回家了,進度被拖慢了不少,只能假期慢慢補了。
定長順序串是串順序存儲結構的一種,順序存儲結構意味着它所佔的內存空間是一塊連續的,而定長意味着空間大小在串創建之初就確定了,無法動態分配內存,如果有超過的部分只能捨去。在這裏我們使用尾部加“ \0 ”的方法來表示串結束,因此實際要開闢的空間是 MAXSTRLEN + 1。
定長順序串的初始化
我們使用數組來保存串,因此直接定義一個字符數組作爲串。
#define u_char unsigned char
typedef u_char SString[MAXSTRLEN+1];
串的初始化就是將這個字符數組初始化爲空串,在數組首位添加" \0 "即可
void InitString(SString s){
s[0] = '\0';
}
定長順序串的賦值
在這裏賦值的意思是,我們有一個char *類型的字符串,將它轉化爲我們自定義的SString類型,就是給SString進行賦值。
因此賦值包括三個步驟:
1.計算出char *類型的字符串的長度
2.判斷長度是否超出定義的最大長度,如果超出則需要截斷超出的部分
3.依次取出char *中的每一個字符,然後保存到SString數組中
void StrAssign(SString s,char *str){
//1.求要賦值的字符串的長度
int len = (int)strlen(str);
//2.比較是否超出最大長度
if (len > MAXSTRLEN) {
//超出,只能賦值到最大長度的位置
len = MAXSTRLEN;
}
//3.依次賦值
for (int i = 0; i < len; i ++) {
s[i] = str[i];
}
//標記字符串結束
s[len] = '\0';
}
在最後不要忘記添加“ \0 ”表示字符串結束
定長順序串的比較
比較的意思是,將兩個字符串對應位置的字符的ASCII碼依次進行比較,假如一致則繼續比較,不一致則直接輸出結果。例如字符串abc和字符串acb,先將a與a比,一樣則b與c比,因爲c的ASCII碼比b大,認爲acb大於abc,輸出結果。只有每個字符都相等時,才意味着兩個字符串相等。
因爲是直接比較ASCII碼,我們也可以這樣設計:定義result等於0,然後依次取出字符直接相減,假如a字符大於b字符,則結果大於0;若小於則小於0,一致則等於0。在大於或小於0時直接退出循環,等於0時則繼續取下一個字符,直到兩個字符串都結束爲止。
int StrCompare(SString s,SString t){
int result = 0;
//判斷字符串是否結束
while (*s != '\0' || *t != '\0') {
result = *s - *t;
if (result != 0) {
//如果有結果則直接退出循環
break;
}
s ++;
t ++;
}
return result;
}
定長順序串的連接
連接是指將串s2接到串s1的尾部,用串t來保存。由於是定長順序串,需要考慮到串t能否裝得下的問題。
一共有三種情況:
1.s1和s2都裝得下,直接裝即可
2.只裝得下s1和s2的一部分,s2需要截斷
3.只裝得下s1(s1的長度等於MAXSTRLEN),只存s1即可
需要針對不同的情況進行不同處理。
void StrConcar(SString t,SString s1,SString s2){
int len1 = StrLength(s1);
int len2 = StrLength(s2);
if (len1 + len2 <= MAXSTRLEN) {
//裝得下兩個子串->直接裝入
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
for (int j = 0; j < len2; j ++) {
t[len1+j] = s2[j];
}
t[len1+len2] = '\0';
}else if(len1 < MAXSTRLEN){
//只裝得下s1和部分s2->s2需要截斷
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
for (int j = 0; j < MAXSTRLEN - len1; j ++) {
t[len1+j] = s2[j];
}
//最後一個位置設爲'\0'表示結束
t[MAXSTRLEN] = '\0';
}else{
//只裝得下s1
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
t[MAXSTRLEN] = '\0';
}
}
當然還有另一種寫法:先將s1存入t中,然後判斷t剩餘空間的情況,如果能存下s2則直接存入,否則將s2截斷後存入。
定長順序串的插入與刪除
定長順序串的插入指的是,將串t從pos的位置插入到串s中。這同樣要考慮s串能否裝得下的問題,假如裝不下則需要截斷串t,然後從pos位置起的字符往後挪t_len(實際能插入的長度)位,再將t插入。
刪除指的是,在串s中從pos的位置開始刪除長度爲len的子串。首先需要判斷是否越界的問題,然後直接從pos位置起將len長度後的字符直接往前挪即可。
定長順序串的定位
定位指的是,在串s中從pos位置起尋找子串t,如果找到則返回其第一個字符在s串的位置。這個方法返回的是第一個子串t的位置,假如後續還有其他子串t是無法找到的。
這其中涉及到字符串的匹配算法,比較複雜,爲了實現的方便我們直接使用BF算法:從pos位置起遍歷串s,與串t的首個字符開始比較,假如一致則串s和串t都移到下一位,繼續比較下一個字符。當有不一致時,串t需要回溯到起始位置,而串s要回溯到與串t開始相等的位置,並且移到下一位,繼續和串t的首個字符比較。
int StrIndex(SString s,SString t,int pos){
//標記s串的位置
int i = pos;
//標記t串的位置
int j = 0;
while (s[i]!='\0' && t[j]!='\0') {
//判斷兩個字符是否相等
if (s[i] == t[j]) {
//相等,找到下一個位置
i ++;
j ++;
}else{
//不相等,s串回溯到與t串開始相等的位置,並且挪到下一位
i = i-j+1;
//t串回溯到起始位置
j = 0;
}
}
if (t[j] == '\0') {
return i-j;
}
return -1;
}
定長順序串的替換
替換指的是使用串v替換在串s中出現的所有子串t。
因爲在之前已經實現過定位、插入和刪除的方法,在這裏我們直接調用即可。思路是:首先調用定位方法,找到子串t在串s中的位置pos,然後調用刪除方法刪除從pos位置起長度爲t_len的字符串,最後調用插入方法從pos位置起插入串v。
需要注意的是,因爲子串可能不止出現一次,因此需要循環調用定位的方法,當定位失敗(不存在子串)或者位置已經超出串s的長度時才能退出循環。
void StrReplace(SString s,SString t,SString v){
int s_len = StrLength(s);
int t_len = StrLength(t);
int v_len = StrLength(v);
int index = -1;
int pos = 0;
while (pos < s_len) {
//開始匹配
index = StrIndex(s, t, pos);
if (index == -1) {
//不存在子串
return;
}
//存在
//1.先刪除t串
StrDelete(s, index, t_len);
//2.插入v串
StrInsert(s, index, v);
pos = index + v_len;
}
}
全部代碼
SString.h
#ifndef SString_h
#define SString_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSTRLEN 20
#define u_char unsigned char
typedef u_char SString[MAXSTRLEN+1];
//初始化
void InitString(SString s);
//賦值
void StrAssign(SString s,char *str);
//展示
void PrintString(SString s);
//拷貝(將t串拷貝到s串來
void StrCopy(SString s,SString t);
//判空:0->非空 !0->空
int StrEmpty(SString s);
//比較(若s>t 返回值>0 s==t 返回值==0 s<t 返回值<0
//依次取出對應位置的字符,以ASCII碼值進行比較
int StrCompare(SString s,SString t);
//求長度
int StrLength(SString s);
//鏈接
void StrConcar(SString t,SString s1,SString s2);
void StrConcar2(SString t,SString s1,SString s2);
//求子串(在s串中從pos位置開始求長度爲len的子串,並保存到sub串中
void SubString(SString s,SString sub,int pos,int len);
//插入(將t串從pos位置起插入到s串中
void StrInsert(SString s,int pos,SString t);
//刪除(在s串中從pos位置開始刪除長度爲len的子串
void StrDelete(SString s,int pos,int len);
//清除
void StrClear(SString s);
//定位(在s串中尋找子串t,如果找到則返回第一個字符在s串的位置,用pos返回
int StrIndex(SString s,SString t,int pos);
//替換(用v替換在s中出現的所有子串t
void StrReplace(SString s,SString t,SString v);
#endif /* SString_h */
SString.c
#include "SString.h"
//初始化->初始化爲空串
void InitString(SString s){
s[0] = '\0';
}
//賦值
void StrAssign(SString s,char *str){
//求要賦值的字符串的長度
int len = (int)strlen(str);
//比較是否超出最大長度
if (len > MAXSTRLEN) {
//超出,只能賦值到最大長度的位置
len = MAXSTRLEN;
}
//依次賦值
for (int i = 0; i < len; i ++) {
s[i] = str[i];
}
//標記字符串結束
s[len] = '\0';
}
//展示
void PrintString(SString s){
printf("%s\n",s);
}
//拷貝(將t串拷貝到s串來
void StrCopy(SString s,SString t){
//獲取t串長度
int len = StrLength(t);
for (int i = 0; i < len; i ++) {
s[i] = t[i];
}
s[len] = '\0';
}
//判空:0->非空 !0->空
int StrEmpty(SString s){
return s[0] == '\0';
}
//比較(若s>t 返回值>0 s==t 返回值==0 s<t 返回值<0 以ASCII碼值進行比較
int StrCompare(SString s,SString t){
int result = 0;
while (*s != '\0' || *t != '\0') {
result = *s - *t;
if (result != 0) {
break;
}
s ++;
t ++;
}
return result;
}
//求長度
int StrLength(SString s){
int len = 0;
while (*s != '\0') {
len ++;
s ++;
}
return len;
}
//鏈接
void StrConcar(SString t,SString s1,SString s2){
int len1 = StrLength(s1);
int len2 = StrLength(s2);
if (len1 + len2 <= MAXSTRLEN) {
//裝得下兩個子串->直接裝入
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
for (int j = 0; j < len2; j ++) {
t[len1+j] = s2[j];
}
t[len1+len2] = '\0';
}else if(len1 < MAXSTRLEN){
//只裝得下s1和部分s2->s2需要截斷
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
for (int j = 0; j < MAXSTRLEN - len1; j ++) {
t[len1+j] = s2[j];
}
//最後一個位置設爲'\0'表示結束
t[MAXSTRLEN] = '\0';
}else{
//只裝得下s1
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
t[MAXSTRLEN] = '\0';
}
}
void StrConcar2(SString t,SString s1,SString s2){
int len1 = StrLength(s1);
int len2 = StrLength(s2);
//先將s1裝入
for (int i = 0; i < len1; i ++) {
t[i] = s1[i];
}
//判斷t剩餘的空間情況
if (MAXSTRLEN-len1 > 0) {
//還未滿
//判斷是否能把整個s2裝下
if (len1+len2 <= MAXSTRLEN) {
//是
for (int j = 0; j < len2; j ++) {
t[len1+j] = s2[j];
}
t[len1+len2] = '\0';
}else{
//否,s2需要截斷
for (int j = 0; j < MAXSTRLEN - len1; j ++) {
t[len1+j] = s2[j];
}
//最後一個位置設爲'\0'表示結束
t[MAXSTRLEN] = '\0';
}
}
}
//求子串(在s串中從pos位置開始求長度爲len的子串,並保存到sub串中
void SubString(SString s,SString sub,int pos,int len){
//判斷位置是否合法
int s_len = StrLength(s);
if (pos<0 || pos>= s_len || len<0 || len>s_len || pos+len>s_len) {
printf("要截取的位置或長度不合法\n");
return;
}
for (int i = 0; i < len; i ++) {
sub[i] = s[pos+i];
}
sub[len] = '\0';
}
//插入(將t串從pos位置起插入到s串中
void StrInsert(SString s,int pos,SString t){
int s_len = StrLength(s);
int t_len = StrLength(t);
//長度不足->只能插入t串的一部分
if (s_len+t_len > MAXSTRLEN) {
t_len = MAXSTRLEN-s_len;
}
//將原來pos位置的字符挪到pos+t_len上
for (int i = s_len-1; i>=pos; i --) {
s[i+t_len] = s[i];
}
//將t串插入到pos位置上
int j = pos;
for (int i = 0; i < t_len; i++) {
s[j+i] = t[i];
}
s[s_len+t_len] = '\0';
}
//刪除(在s串中從pos位置開始刪除長度爲len的子串
void StrDelete(SString s,int pos,int len){
int s_len = StrLength(s);
if (pos < 0 || pos > s_len || len < 0 || len > s_len) {
printf("要刪除的位置或長度非法\n");
return;
}
for (int i = pos; i < s_len; i ++) {
s[i] = s[i+len];
}
s[s_len - len] = '\0';
}
//清除
void StrClear(SString s){
s[0] = '\0';
}
//定位(在s串中從pos位置起尋找子串t,如果找到則返回第一個字符在s串的位置
int StrIndex(SString s,SString t,int pos){
//標記s串的位置
int i = pos;
//標記t串的位置
int j = 0;
while (s[i]!='\0' && t[j]!='\0') {
//判斷兩個字符是否相等
if (s[i] == t[j]) {
//相等,找到下一個位置
i ++;
j ++;
}else{
//不相等,s串回溯到與t串開始相等的位置,並且挪到下一位
i = i-j+1;
//t串回溯到起始位置
j = 0;
}
}
if (t[j] == '\0') {
return i-j;
}
return -1;
}
//替換(用v替換在s中出現的所有子串t
void StrReplace(SString s,SString t,SString v){
int s_len = StrLength(s);
int t_len = StrLength(t);
int v_len = StrLength(v);
int index = -1;
int pos = 0;
while (pos < s_len) {
//開始匹配
index = StrIndex(s, t, pos);
if (index == -1) {
//不存在子串
return;
}
//存在
//1.先刪除t串
StrDelete(s, index, t_len);
//2.插入v串
StrInsert(s, index, v);
pos = index + v_len;
}
}
Main.c
#include "SString.h"
int main(int argc, const char * argv[]) {
SString s;
InitString(s);
SString t;
InitString(t);
//賦值
char *str = "abcd";
StrAssign(s, str);
char *str2 = "xyz";
StrAssign(t, str2);
//比較
int result = StrCompare(s,t);
printf("compare:%d\n",result);
//拷貝
//StrCopy(t,s);
//PrintString(t);
//連接
SString y;
InitString(y);
StrConcar2(y,s,t);
PrintString(y);
//求子串
SString sub;
InitString(sub);
SubString(y, sub, 2, 3);
PrintString(sub);
//插入
StrInsert(s, 1, t);
PrintString(s);
//刪除
StrDelete(s, 1, 2);
PrintString(s);
//定位
SString s1;
InitString(s1);
StrAssign(s1, "ababcababcab");
SString s2;
InitString(s2);
StrAssign(s2, "abc");
result = StrIndex(s1, s2, 0);
printf("index:%d\n",result);
//替換
SString s3;
InitString(s3);
StrAssign(s3, "xy");
StrReplace(s1, s2, s3);
PrintString(s1);
return 0;
}