目录
一.课本知识点
1.串类型的定义
- 串:即字符串,是由零个或多个字符组成的有限序列, 是数据元素为单个字符的特殊线性表。
- 串长:串中字符个数(n≥0). n=0 时称为空串
。
- 空白串:由一个或多个空格符组成的串。
- 字符位置:字符在串中的序号。
- 串相等:串长度相等,且对应位置上字符相等。
- 子串:串中任意个连续的字符组成的子序列。
- 主串:包含子串的串。
- 子串的位置:子串的第一个字符在主串中的序号。
- 串的基本操作:
StrInsert (&S, pos, T) (插入) 初始条件:串 S 和 T 均存在,1≤pos≤StrLength(S)+1。 操作结果:在串 S 的第 pos 个字符之前插入串T。 例如:S = "chater",T = "rac",则执行 StrInsert (S, 4, T) 得到 S = "character" StrDelete (&S, pos, len) (删除) 初始条件:串 S 存在,且1≤pos≤StrLength(S)-len+1。 操作结果:从串 S 中删除第 pos 个字符起长度为len的子串。 例如:S="character",执行StrDelete (S, 3, 4) ,得到S= "chter"。 StrAssign (&T, chars) (串赋值) 初始条件:chars 是字符串常量。 操作结果:把 chars 赋为 T 的值。 StrCopy (&T, S) (串复制) 初始条件:串 S 存在。 操作结果:由串 S 复制得串 T。 Concat (&T, S1, S2) (串联接) 初始条件:串 S1 和 S2 存在。 操作结果: T 为由串 S1 和串 S2 联接所得的串。 例如: Concat( T, "man", "kind") 求得 T ="mankind" Replace ( S, T, V) (串置换) 初始条件:串 S, T 和 V 均已存在,且 T 是非空串。 操作结果:用 V 替换主串 S 中出现的所有与(模式串)T 相等的不重叠的子串。 SubString (&Sub, S, pos, len) (求子串) 初始条件:串 S 存在,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1。 操作结果: 以 Sub 返回串 S 中第 pos 个字符起长度为 len 的子串。 Index ( S, T, pos) (定位函数) 初始条件:串 S 和 T 存在,且 T 是非空串, 1≤pos≤StrLength(S)。 操作结果:若主串 S 中存在和串 T 值相同的子串,则返回它在主串 S 中第 pos个字符之后第一次出现的位置; 否则函数值为0。
- 串和线性表的比较:
2.串的表示和实现
-
串有三种机内表示方法:
a.定长顺序存储:![](https://img-blog.csdnimg.cn/a898c58dd98545fa939211fec515f33e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAaGVudV9OZXd4YzAz,size_20,color_FFFFFF,t_70,g_se,x_16)
-
定长顺序存储:串连接
Status Concat ( Sstring &T, Sstring S1 ,Sstring S2) { if ( S1[0] +S2[0] <= MAXSTRLEN) //未截断 { T[1..S1[0]] = S1[ 1..S1[0]]; T[ S1[0]+1 .. S1[0]+S2[0] ] = S2[ 1 .. S2[0] ]; T[0] = S1[0] + S2[0]; uncut = TRUE; } else if ( S1[0] < MAXSTRSIZE ) // 截断 { T[ 1..S1[0] ] = S1[ 1..S1[0] ]; T[ S1[0] +1 .. MAXSTRLEN ] = S2[ 1.. MAXSTRLEN- S1[0]]; T[0] = MAXSTRLEN; uncut = FALSE; } else { T[ 0.. MAXSTRLEN ] = S1[0.. MAXSTRLEN ]; // 截取(仅取S1) uncut = FALSE; } return uncut; } // Concat
-
定长顺序存储:求子串
//将串S中从第pos个字符开始长度为len的字符序列复制到串Sub中(注:串Sub的预留长度与S一样) Status SubString (SString &sub, SString S, int pos, int len ) { if (pos<1 || pos>S[0] || len<0 || len>S[0]-pos+1) return ERROR; //pos不合法则警告 Sub[1……len] = S [pos……pos+len-1]; Sub[0]=len; return OK; }
b.堆分配存储:![](https://img-blog.csdnimg.cn/f6847afafc714a8aa4abf98c95e0f435.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAaGVudV9OZXd4YzAz,size_20,color_FFFFFF,t_70,g_se,x_16)
- 堆分配存储:插入操作
Status StrInsert ( HString &S, int pos, HString T ) { if (pos<1||pos>S.length+1) return ERROR; //pos不合法则告警 if(T.length){ //只要串T不空,就需要重新分配S空间,以便插入T if (!(S.ch=(char*)realloc (S.ch, (S.length+T.length)* sizeof(char)) )) exit(OVERFLOW); for ( i=S.length-1; i>=pos-1; --i ) //为插入T而腾出pos之后的位置 S.ch [i+T.length] = S.ch [i]; //从S的pos位置起全部字符均后移 S.ch[pos-1…pos+T.length-2] = T.ch[0…T.length-1]; //插入T S.length + = T.length; //刷新S串长度 } return OK; }//StrInsert
- 堆分配存储:赋值操作
Status StrAssign( HString &T, char *chars ) { if (T.ch) free(T.ch); for (i=0, c=chars;* c; ++i, ++c); //求串长度 if (!i) {T.ch = NULL; T.length = 0;} else{ if (!(T.ch = (char*)malloc (i*sizeof(char)))) exit(OVERFLOW); T.ch[0..i-1] = chars[0..i-1]; T.length =i; } Return OK; }//StrAssign
- 堆分配存储:比较操作
比较操作: Int Strcompare ( Hstring S, Hstring T ) { for ( i = 0; i < S.length && i <T.length; ++i ) if ( S.ch[i] != T.ch[i] ) return S.ch [i] – T.ch[i]; return S.length- T.length; } // StrCompare
- 堆分配存储:清空操作
清空操作: Status ClearString ( Hstring &S) { if ( S.ch ) { free(S.ch); S.ch = NULL; } S.length = 0; return OK; } // ClearString
- 堆分配存储:连接操作
Status Concat ( HString &T, Hstring S1, Hstring S2 ) { //用T返回由S1和S2联接而成的新串。 if (T.ch) free(T.ch); // 释放旧空间 if ( !(T.ch = (char *) malloc ((S1.length+S2.length) * sizeof (char) ) ) ) exit ( OVERFLOW); T.ch[0 .. S1.length-1] = S1.ch[0 .. S1.length-1]; T.length = S1.length + S2.length ; T.ch [S1.length .. T.length-1] = S2.ch [0 .. S2.length-1]; return OK; } // Concat
- 2.堆分配存储:求子串操作
Status SubString ( Hstring &Sub, Hstring S,int pos,int len ) { //用Sub返回串S的第pos个字符起长度为len的子串。 // 其中,1<=pos<= StrLength (S) 且 0<=len<=StrLength(S)-pos+1。 if ( pos < 1 || pos>S.length || len<0 || len>S.length-pos+1) return ERROR; // 参数不合法 if ( Sub.ch) free ( Sub.ch); // 释放旧空间 if (!len) { Sub.ch = NULL; Sub.length = 0; } // 空子串 else { // 完整子串 Sub.ch = ( char *) malloc ( len *sizeof ( char )); Sub.ch[0..len-1] = S.ch [ pos-1.. Pos+len-2] ; Sub.length = len; } return OK; }
c.链式存储![](https://img-blog.csdnimg.cn/8999210ced9d4f20ad7e137b6a85bf75.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAaGVudV9OZXd4YzAz,size_20,color_FFFFFF,t_70,g_se,x_16)
- 链式存储:块链的定义
#define CHUNKSIZE 80 //可由用户定义的块大小 typedef struct Chunk { //首先定义结点类型 char ch [ CHUNKSIZE ]; //结点中的数据域 struct Chunk * next ; //结点中的指针域 }Chunk; typedef struct { //其次定义用链式存储的串类型 Chunk *head; //头指针 Chunk *tail; //尾指针 int curLen; //结点个数 } Lstring; 串的链式存储结构对某些操作比较方便,但是总体来说没有前面的两种存储结构灵活。
3.串的模式匹配算法
- 模式匹配(Pattern Matching) 即子串定位运算(Index函数)
a.BF算法
-
算法思想:
从目标串T的的第一个字符起与模式串P的第一个字符比较。
若相等,则继续对字符进行后续的比较;否则目标串从第二个字符起与模式串的第一个字符重新比较。
直至模式串中的每个字符依次和目标串中的一个连续的字符序列相等为止,此时称为匹配成功,否则匹配失败。
- 算法性能:
假设模式串的长度是m,目标串的长度是n。
最坏的情况是每遍比较都在最后出现不等,即没变最多比较m次,最多比较n-m+1遍。
总的比较次数最多为m(n-m+1),因此BF算法的时间复杂度为O(mn)。
- 代码:
int BF(char S[],char T[]) { int index=0; int i=0; int j=0; while(S[i]!='\0'&&T[j]!='\0') { if(S[i]==T[j]) { //如果匹配成功,则继续比较下一对字符 i++; j++; } else { //如果匹配不成功,主串和子串回溯坐标。index记录的是每次匹配时的开始位置 index++; i=index; j=0; } } if(T[j]=='\0') return index+1; else return 0; }
b.KMP算法
这个kmp算法第一次看还是不太好理解 我建议大家先看一下这个动画演示辅助理解这个算法的精妙之处「天勤公开课」KMP算法易懂版
这个kmp算法写起来篇幅太长了 我推荐大家看一下这个博客详解KMP算法 以后等期末考试完时间充足了 我可能也会写篇关于kmp的博客
4.数组的定义
- 数组: 由一组名字相同、下标不同的变量构成。
5.数组的顺序表示和实现![](https://img-blog.csdnimg.cn/1ec2044532184ce9a9d626941d81d871.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAaGVudV9OZXd4YzAz,size_20,color_FFFFFF,t_70,g_se,x_16)
- N维数组的顺序存储表示
6.矩阵的压缩存储(即数组的应用)
- 什么是压缩存储?
若多个数据元素的值都相同,则只分配一个元素值的存储空间,且零元素不占存储空间。 - 什么样的矩阵具备压缩条件?
特殊矩阵(对称矩阵,对角矩阵,三角矩阵)和稀疏矩阵。 - 什么叫稀疏矩阵?
矩阵中非零元素的个数较少(一般小于5%)
我太讨厌数组这一章了 剩下数组和矩阵的内容太多太恶心了 不想写了
7.广义表的定义
- 定义:
- 在广义表中约定:
① 第一个元素是表头,而其余元素组成的表称为表尾;
② 用小写字母表示原子类型,用大写字母表示列表。 - 广义表与线性表的区别和联系?
广义表中元素既可以是原子类型,也可以是列表;
当每个元素都为原子且类型相同时,就是线性表。 -
广义表的特点:
-
例
-
广义表可以采用顺序存储结构吗?
由于广义表中的数据元素的类型不统一,因此难以采用顺序存储结构来存储。 -
如何采用链接存储结构存储广义表?
8.广义表的存储结构
- 广义表的存储结构——头尾表示法
二.练习题
题组一:
题组二 :
![]()
四、算法设计题
- 编写一个实现串的置换操作Replace(&S, T, V)的算法。
int Replace(Stringtype &S,Stringtype T,Stringtype V);//将串S中所有子串T替换为 V,并返回置换次数 { for(n=0,i=1;i<=Strlen(S)-Strlen(T)+1;i++) //注意i的取值范围 if(!StrCompare(SubString(S,i,Strlen(T)),T)) //找到了与T匹配的子串 { //分别把T的前面和后面部分保存为head和tail StrAssign(head,SubString(S,1,i-1)); StrAssign(tail,SubString(S,i+Strlen(T),Strlen(S)-i-Strlen(T)+1)); StrAssign(S,Concat(head,V)); StrAssign(S,Concat(S,tail)); //把head,V,tail连接为新串 i+=Strlen(V); //当前指针跳到插入串以后 n++; n++; }//if return n; }//Replace