1 / 34

第 4 章 字符串和数组

第 4 章 字符串和数组. 北京师范大学 教育技术学院 杨开城. 堆存储 # define MAXSIZE 100 char str1[MAXSIZE+1]; /* 静态定义的字符数组,可容纳最大字符串长度是 MAXSIZE*/ char *pstr=NULL; /* 字符指针,将指向存放字符串的内存* / int len; scanf(“%d”,&len); /* 获得字符串的长度* / pstr=(char *)malloc(len+1); /* 根据字符串的长度分配一块内存* /. 块链存储 typedef struct strnode {

ken
Download Presentation

第 4 章 字符串和数组

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第4章 字符串和数组 北京师范大学 教育技术学院 杨开城

  2. 堆存储 #define MAXSIZE 100 char str1[MAXSIZE+1]; /*静态定义的字符数组,可容纳最大字符串长度是MAXSIZE*/ char *pstr=NULL; /*字符指针,将指向存放字符串的内存*/ int len; scanf(“%d”,&len); /*获得字符串的长度*/ pstr=(char *)malloc(len+1); /*根据字符串的长度分配一块内存*/ 块链存储 typedef struct strnode { char *block; /*根据需要动态分配的内存,存放一段字符串*/ int size; /*block所指内存的大小*/ struct strnode *next; /*指向下一段字符串*/ }STRNODE,*STRNODEPTR,*BLOCKLINKSTR; 一、字符串——存储方式

  3. 一、字符串——简单模式匹配(1) • 【任务描述】已知一字符串S和字符串T,请确定字符串T在S中的位置,即字符串T的首字母在字符串S中的下标值。这里字符串S被称为主串,T被称为子串,又称为模式串。通常情况下,S串的长度比T大。 • 【算法思想】以pos作为S串的下标。设T串的长度是lent。pos从0开始从左向右扫描字符串S,检查以S[pos]为起点的长度为lent的子串是否与T串完全相同。 • 如果完全相同,则意味着匹配成功,返回pos值; • 如果不同,说明匹配失败,需要将pos向后移动一个单元,继续检查以S[pos]为起点的长度为lent的子串是否与T串完全相同。 这样循环反复,直到匹配成功或者以S[pos]为起点的子串长度不够为止。

  4. 一、字符串——简单模式匹配(2) int StrIndex(char *s,char *t) /*返回t在s中的位置,找不到t,则返回-1*/ { int i,j; int pos=0; /*匹配的起点*/ while(1) { i=pos;j=0; while(s[i]&&t[j]&&s[i]==t[j])/*匹配循环*/ { i++; j++;} if(t[j]==0)return pos; /*匹配成功*/ else if(s[i]==0) return -1; /*匹配到了主串的末尾还没有成功*/ else pos++;/*匹配的起点向后移动一个单元,重新匹配*/ }//while(1 } 时间复杂度:

  5. 一、字符串——模式匹配的KMP算法(1) • 简单匹配算法的最大问题是: 每次匹配失败时,S串要回到S[pos+1]处,而T串要回到T[0]处,从头开始比较。 • 下面的例子蕴含着改进的空间 已知主串是:"aabcdabcdabceab" 模式串是:"abcdabce“ • KMP算法的基本思想: 只要发现S[i]≠T[j]时,i保持不动,j跳到k处(滑动T串),直到k是-1时,i才向右移动1位。k被称为j的next值,即k=next(j)。 j跳到k处的条件是:k是T串在j处的自身最大重复子串的长度。

  6. 一、字符串——模式匹配的KMP算法(2) • 关键是next(j)如何求解 • next(j)表达的是T串自身的特征,与S串无关 • next函数的数学定义 • next(j)函数值的求解算法 已知k=next(j),下面的循环可以求出next(j+1)的值: ①比较T[j]和T[k],如果T[j]=T[k],那么T[0..j]之间最大重复子串的长度就是k+1,也就是说next(j+1)=k+1,求值完毕; ②如果T[j] ≠T[k],我们只能在T[0..k-1]内寻找最大重复子串。T[0..k-1]最大重复子串长度是next(k),这时设定k=next(k),也就是说,k回退到自己的next值处。我们再次比较T[j]和T[k],即转到步骤①。

  7. 一、字符串——模式匹配的KMP算法(3) void NextVal(char *T,int *next)/*求模式串T各单元的next值*/ { int j,k,len; next[0]=-1; j=0; len=strlen(T); while(j<len-1) {/*已知next[j],推算next[j+1]*/ k=next[j]; while(k>=0&&T[j]!=T[k]) k=next[k]; /*k回退到自己的next值处,重复子串的长度变小*/ next[j+1]=k+1; /*无论是k==-1,还是T[j]==T[k],next[j+1]的值都是k+1*/ j++; /*准备推算下一个next值*/ } }

  8. 一、字符串——模式匹配的KMP算法(4) #define MAXSIZE 50 int next[MAXSIZE]; int StrIndexKMP(char *S, char *T)/*返回T在S中的位置,查找失败,返回-1*/ { int i=0, j=0; NextVal(T,next); /*计算T串的next值*/ while(S[i]!=0&&T[j]!=0) /*如果没到字符串末尾,就循环继续匹配*/ { if(S[i]==T[j]) /*i和j都向后移动一个单元*/ { i++; j++;} else { j=next[j]; /*匹配失败,j滑动到自己的next值处*/ if(j==-1) /*说明滑动之前j已经是0,需要将i向后移动,同时置j为0*/ { i++; j=0; } }//else }/*属于while语句*/ if(T[j]==0)return i-j; /*匹配成功,返回位置*/ else return -1; /*查找失败*/ }

  9. 二、数组与矩阵——数组的定义 #define N 10 #define M 20 #define P 10 elemtype a[N];/*定义了一个N个单元的一维数组*/ 假设a[0]的地址是Loc,elemtype数据类型的大小是S(字节),那么数组单元a[i]的地址就是: Loc+i×S。 elemtype b[M][N];/*定义了一个M×N个单元的二维数组*/ 假设b[0][0]的地址是Loc,那么数组单元b[i][j]的前面一共有i行(i×N个)单元,外加j个单元,它的地址就是:Loc+(i×N+j)×S elemtype c[P][M][N];/*定义了一个P×M×N个单元的三维数组*/ 假设c[0][0][0]的地址是Loc,那么c[i][j][k]地址是:Loc+(i×M×N+j×N+k)×S

  10. 二、数组与矩阵——矩阵的压缩存储(1) 1.特殊矩阵之三角矩阵的压缩存储 上三角矩阵:k=i×(2×N-i+1)/2+j-i 下三角矩阵:k=(1+2+3+…+i)+j=i×(i+1)/2+j

  11. 二、数组与矩阵——矩阵的压缩存储(2) 2.特殊矩阵之对称矩阵的压缩存储,按照三角矩阵的方式 3.特殊矩阵之带状矩阵的压缩存储 k=(i×3-1)+(j-(i-1))=2×i+j

  12. 二、数组与矩阵——矩阵的压缩存储(3) 4.特殊矩阵之稀疏矩阵的压缩存储 在一个M×N的矩阵中,非零元素的个数是T,我们将 称为稀疏因子。通常认为,当稀疏因子小于等于0.05时,这个矩阵就称为稀疏矩阵。 ⑴稀疏矩阵的顺序存储 #define MAXSIZE 500 typedef struct { int i,j; /*在矩阵中的位置下标*/ elemtype v; /*非零元素*/ }TRIPLE;/*三元组*/ typedef struct { TRIPLE data[MAXSIZE]; /*三元组数组*/ int rnum,cnum,sum; /*行数、列数、非零元素的个数*/ }TRIPLEMATRIX;

  13. 二、数组与矩阵——矩阵的压缩存储(4) 下面的算法创建一个顺序存储结构的稀疏矩阵: void InputData(TRIPLEMATRIX *m) { int count; TRIPLE t; /*开始输入稀疏矩阵的行数、列数和非零元素的个数*/ scanf("%d,%d,%d",&m->rnum,&m->cnum,&m->sum); count=0; while(1) {scanf("%d,%d,%d",&t.i,&t.j,&t.v);/*获取三元组数据,要求按行序输入*/ if(t.i>=0&&t.i<m->rnum&&t.j>=0&&t.j<m->cnum) /*三元组数据合法*/ { m->data[count]=t; count++; /*计数器增1*/ if(count==m->sum) break; /*所有数据都输入完毕*/ } else break; /*遇到非法输入*/ } }

  14. 二、数组与矩阵——矩阵的压缩存储(5) 4.特殊矩阵之稀疏矩阵的压缩存储 ⑵稀疏矩阵的十字链表存储 typedef struct crsnode_tag { int i,j; elemtype v; /*三元组数据*/ struct crsnode_tag * right, * down; /*行链指针和列链指针,right指向同一行下一列非零元素,down指向同一列下一行非零元素*/ }CROSSNODE,* CROSSNODEPTR; /*定义十字链表结点及其指针*/ typedef struct { CROSSNODEPTR rhead,chead; /*指向行链数组和列链数组*/ int rnum,cnum,sum; /*行数、列数、非零元素的个数*/ }CROSSLINKLIST;

  15. 二、数组与矩阵——矩阵的压缩存储(6) 1.初始化十字链表 int InitCrossLinklist(CROSSLINKLIST *m,int row,int col,int sum) {/*初始化一个十字链表,成功则返回1,否则返回0*/ int i; /*根据行数和列数分配相应数量的行链和列链的表头结点*/ m->rhead=(CROSSNODEPTR)malloc(row*sizeof(CROSSNODE)); m->chead=(CROSSNODEPTR)malloc(col*sizeof(CROSSNODE)); if(m->rhead==NULL||m->chead==NULL) return 0; /*分配失败*/ for(i=0;i<row;i++) m->rhead[i].right=NULL; /*将所有行链设为空链*/ for(i=0;i<col;i++) m->chead[i].down=NULL; /*将所有列链设为空链*/ m->rnum=row; m->cnum=col; m->sum=sum; /*设置行数、列数和非零元素的个数*/ return 1; }

  16. 二、数组与矩阵——矩阵的压缩存储(7) 2.建立一个十字链表的稀疏矩阵 void InputData(CROSSLINKLIST *m) /*建立一个存储数据的十字链表*/ { int len,count; CROSSNODEPTR p,q; int i,j; elemtype v; int row,col,sum; scanf("%d,%d,%d",&row,&col,&sum); /*获取行数、列数和非零元素个数*/ if(!InitCrossLinklist(m,row,col,sum))/*初始化十字链表*/ { printf("no enough memory\n"); exit(0); } count=0;/*计数器清零*/ while(1) { scanf("%d,%d,%d",&i,&j,&v);/*输入三元组数据*/ if(i>=0&&i<m->rnum&&j>=0&&j<m->cnum) /*三元组数据合法*/ { p=(CROSSNODEPTR)malloc(sizeof(CROSSNODE)); if(!p) /*分配失败*/ {printf("no enough memory\n"); return; } p->i=i; p->j=j; p->v=v; /*设置三元组结点p的数据域*/

  17. 二、数组与矩阵——矩阵的压缩存储(8) /*开始按列序插入行链,读者应该很熟悉操作细节了*/ q=&m->rhead[i]; while(q->right!=NULL&&q->right->j<j) q=q->right; p->right=q->right; q->right=p; /*开始按行序插入列链*/ q=&m->chead[j]; while(q->down!=NULL&&q->down->i<i) q=q->down; p->down=q->down; q->down=p; count++; if(count==m->sum)break;/*数据输入完毕*/ } else/*三元组数据非法*/ { printf("illegal input data\n"); break; } }//while }

  18. 二、数组与矩阵——矩阵的压缩存储(9) 3.销毁十字链表 void DestroyCrossLinklist(CROSSLINKLIST *m) /*销毁一个十字链表*/ { CROSSNODEPTR p; int i; for(i=0;i<m->rnum;i++) {/*沿着行链,释放链上所有的三元组结点*/ while(m->rhead[i].right!=NULL) { p=m->rhead[i].right; m->rhead[i].right=p->right; free(p); } } free(m->rhead); free(m->chead); /*释放行链和列链表头结点数组*/ m->rhead=m->chead=NULL; m->rnum=0; m->cnum=0; m->sum=0; /*其他成员设置成安全值*/ }

  19. 二、数组与矩阵——稀疏矩阵的转置(1) • 转置的含义: • 三元组数组的转置算法 • 遍历三元组数组,记下这个稀疏矩阵中每列的非零元素个数,存储数组count • 推算出转置后矩阵的各三元组数据的正确的存储位置 ,存储位置存放在数组rpos中 • 再次遍历三元组数组,对每个数据单元进行转置,按照rpos中的数据存放到新的三元组数组中

  20. 二、数组与矩阵——稀疏矩阵的转置(2) TRIPLEMATRIX TransposeMatrix(TRIPLEMATRIX m) {/*返回矩阵m的转置矩阵*/ int *count,*rpos; TRIPLEMATRIX T; int k,i,r; count=(int *)malloc(sizeof(int)*m.cnum); /*count[i]将存放矩阵m第i列非零元素的个数*/ rpos=(int *)malloc(sizeof(int)*m.cnum); /*rpos[i]将存放转置后的矩阵行非零元素的存储起点*/ if(count==NULL||rpos==NULL) { printf("no enough memory\n"); return; } for(i=0;i<m.cnum;i++) count[i]=0;/*计数器清零*/ for(i=0;i<m.sum;i++) /*遍历三元组数组,记录各列非零元素的个数*/ { k=m.data[i].j; count[k]++; } rpos[0]=0;/*第0行的存储起点是0*/ for(i=1;i<m.cnum;i++)/*计算各行非零元素的存储起点*/ rpos[i]=rpos[i-1]+count[i-1];

  21. 二、数组与矩阵——稀疏矩阵的转置(3) T.sum=m.sum; T.rnum=m.cnum; T.cnum=m.rnum; /*行、列转置,非零元素总量不变*/ for(i=0;i<m.sum;i++) {k=m.data[i].j; r=rpos[k];/*r是转置后三元组数据的正确的存储位置*/ T.data[r].i=m.data[i].j; T.data[r].j=m.data[i].i; T.data[r].v=m.data[i].v;/*三元组转置*/ rpos[k]++;/*存储起点增1,是下一个同一行三元组数据的存储位置*/ } free(count); free(rpos); return T; }

  22. 二、数组与矩阵——稀疏矩阵的乘法(1) • 矩阵相乘的含义 已知一个M×N的矩阵A和N×P的矩阵B,令C=A×B,则C是一个M×P的矩阵,并且: • 二维数组的矩阵乘法 void MatrixMulti(int A[M][N],int B[N][P],int C[M][P]) { int i,j,k; for(i=0;i<M;i++) for(j=0;j<P;j++) { C[i][j]=0;/*矩阵C的数据单元清零*/ for(k=0;k<N;k++) C[i][j]+=A[i][k]*B[k][j]; /*见矩阵乘法公式*/ } }

  23. 二、数组与矩阵——稀疏矩阵的乘法(2) 稀疏矩阵十字链表的矩阵乘法 void MatrixMulti(CROSSLINKLIST *A, CROSSLINKLIST *B,CROSSLINKLIST* C) {/*矩阵C将是矩阵A乘以矩阵B的结果*/ int i,j,k; elemtype v; CROSSNODEPTR p,q,pC,qC; InitCrossLinklist(C,A->rnum,B->cnum,0); /*初始化矩阵C的十字链表*/ for(i=0;i<A->rnum;i++) /*遍历矩阵A的所有的行链*/ { p=A->rhead[i].right; while(p!=NULL) /*找到非零元素结点p,某个(i,j,v)*/ { k=p->j; /*k是A[i][j]的列号j,也就是矩阵B的行号*/ q=B->rhead[k].right; while(q!=NULL) /*遍历矩阵B的第k行行链,找到所有非零元素结点q*/ { j=q->j; v=p->v*q->v; /*计算A[i][k]×B[k][j]*/

  24. 二、数组与矩阵——稀疏矩阵的乘法(3) /*试图将(i,j,v)插入到矩阵C中*/ pC=&C->rhead[i]; while(pC->right!=NULL&&pC->right->j<j) /*寻找插入的位置*/ pC=pC->right; if(pC->right!=NULL&&pC->right->j==j) pC->right->v+=v; /*原行链中有(i,j,v’)结点,执行累加*/ else/*否则新生成结点(i,j,v)结点qC,插入到pC的后面*/ { qC=(CROSSNODEPTR)malloc(sizeof(CROSSNODE)); if(qC==NULL) { printf("no enough memory\n"); return; } qC->i=i; qC->j=j; qC->v=v; qC->right=pC->right; pC->right=qC; /*再将结点qC插入到列链中*/ pC=&C->chead[j]; while(pC->down!=NULL&&pC->down->i<i) pC=pC->down; qC->down=pC->down; pC->down=qC; C->sum++; /*矩阵非零元素个数增1*/ }/*属于else语句 */

  25. 二、数组与矩阵——稀疏矩阵的乘法(4) /*寻找矩阵B第k行的下一个非零元素*/ q=q->right; }//while(q) /*寻找矩阵A第i行的下一个非零元素*/ p=p->right; }//while(p) }//for /*下面清除十字链表中的零元素结点*/ for(i=0;i<C->rnum;i++) /*检查所有的行链*/ { p=&C->rhead[i]; while(p->right!=NULL) /*遍历行链,摘除所有的零元素结点*/ { if(p->right->v==0)/*发现零元素结点,从行链中摘除*/ p->right=p->right->right; else p=p->right; /*否则p向后移动*/ } }

  26. 二、数组与矩阵——稀疏矩阵的乘法(5) for(i=0;i<C->cnum;i++) {/*检查所有的列链*/ p=&C->chead[i]; while(p->down!=NULL) {/*遍历列链,摘除零元素结点*/ if(p->down->v==0) {/*发现零元素结点,这时它已经从行链中被摘除了*/ q=p->down; p->down=q->down; free(q);/*摘除结点并释放内存*/ C->sum--;/*这时矩阵非零元素个数减1*/ } else p=p->down; } } }

  27. 导航图(1)

  28. 导航图(2)

  29. 导航图(3)

  30. 导航图(4)

  31. 导航图(5)

  32. 导航图(6)

  33. 导航图(7)

  34. 导航图(8)

More Related