数据结构C语言 Part4 串、数组和广义表

首先,我们目前提到的(Part1-Part4)都是线性结构。

这一节,我们主要是要掌握:

1. 了解串的存储方法,理解串的两种模式匹配算法,重点掌握BF算法。

2. 明确数组和广义表这两种数据结构的特点,掌握数组地址计算方法,了解几种特殊矩阵的压缩存储方法。

3.掌握广义表的定义、性质及其GetHead和GetTail的操作。

我们先来看看串(string):

其参数有串名,串值,串长,主串,字串,串相等,空串,字符位置,字串位置。

//顺序存储
typedef struct
{
    char *ch;
    int length;
}MyString;

//链式,多个字符存在一个结点
#define CHUNKSIZE 80       //可由用户定义的块大小
typedef struct Chunk{
   char  ch[CHUNKSIZE];
   struct Chunk *next;
}Chunk;

typedef struct{
   Chunk *head,*tail;      //串的头指针和尾指针
   int curlen;             //串的当前长度
}LString;

串的模式匹配算法:确定主串中所含子串第一次出现的位置(定位),方法主要是:

BF算法(又称古典的、经典的、朴素的、穷举的) &    KMP算法(特点:速度快)

对于BF算法,将主串的第pos个字符和模式的第一个字符比较,    

若相等,继续逐个比较后续字符;     若不等,从主串的下一字符起,重新与模式的第一个字符比较。  

直到主串的一个连续子串字符序列与模式相等 。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。 否则,匹配失败,我们设返回值 -1。

可见,它的最坏的时间复杂度是O(n*m)这个Scale的。

//暴力检索算法描述
int ViolentMatch(char* s, char* p)
{
	int sLen = strlen(s);
	int pLen = strlen(p);
	int i = 0;
	int j = 0;
	while (i < sLen && j < pLen)
	{
		if (s[i] == p[j])
		{
			//如果当前字符匹配成功(即S[i] == P[j]),则i++,j++    
			i++;
			j++;
		}
		else
		{
			//②如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0    
			i = i - j + 1;
			j = 0;
		}
	}
	//匹配成功,返回模式串p在文本串s中的位置,否则返回-1
	if (j == pLen)
		return i - j;
	else
		return -1;
}

对于KMP(Knuth Morris Pratt所提出的)算法,另一位CSDN的大神写的很好:深度理解KMP链接?。核心是利用已经部分匹配的结果二加快模式串的滑动速度,主串的指针i不必回溯,这样可以提速到O(n+m)

int KmpSearch(char* s, char* p)
{
	int i = 0;
	int j = 0;
	int sLen = strlen(s);
	int pLen = strlen(p);
	while (i < sLen && j < pLen)
	{
		//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++    
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]    
			//next[j]即为j所对应的next值      
			j = next[j];
		}
	}
	if (j == pLen)
		return i - j;
	else
		return -1;
}
//优化过后的next 数组求法
void GetNext(char* p,int next[])
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀
		if (k == -1 || p[j] == p[k]) 
		{
			++k;
			++j;
			next[j] = k;
		}
		else 
		{
			k = next[k];
		}
	}
}

原理图:

next数组和val数组的求法:next数组咱就不说了,可以目测

对于nextval而言,有一个很巧的算法,是我从大神那学来的。

3、next计算过程
不管第一位和第二位是什么,第一位next(1)=0,第二位next(2)=1,这是固定的。

当j=3时,P(j)=C,C之前有”AB”,不存在重复(0位),所以next(3)=0+1=1; 
当j=4时,P(j)=D,C之前有”ABC”,不存在重复(0位),所以next(4)=0+1=1; 
当j=5时,P(j)=A,C之前有”ABCD”,不存在重复(0位),所以next(5)=0+1=1; 
当j=6时,P(j)=B,C之前有”ABCDA”,存在重复”A”(1位),所以next(6)=1+1=2; 
当j=7时,P(j)=D,C之前有”ABCDAB”,存在重复”AB”(2位),所以next(7)=2+1=3;

4、nextval=对next的优化
观察第5位”A”,当它不匹配时,按照next行回溯到标号1也为字母A,这时再匹配A是徒劳的,因为已知A不匹配,所以就继续退回到标号1字母A的next(1)=0。为了直接点进行优化,就有了nextval行:

只看前面有重复字母的几位就可以。
j=5,P(5)=A,next(5)=1,P(1)=A=P(5),所以nextval(5)=next(1)=0; 
j=6,P(6)=B,next(6)=2,P(2)=B=P(6),所以nextval(6)=next(2)=1; 
j=7,P(7)=D,next(7)=3,P(3)=C!=P(7),所以nextval(7)=next(7)=3;

结果:
标号(j)    1    2    3    4    5    6    7
模式串(P)    A    B    C    D    A    B    D
next    0    1    1    1    1    2    3
nextval    0    1    1    1    0    1    3


原文:奈奈酱看nextval 哈哈,开心,谢谢大佬啦
 

细节我们不一一赘述了,毕竟时间有限。

然后我们看看数据结构书上的数组:

我们就看看题算了:

同理,我们引申到三维数组,也有LOC ( i1, i2, i3 ) = a + i1* m2 * m3 + i2* m3 + i3。a代表起始地址。

要引起注意的是,这里我们是以0为起点,每一行有m个元素,从0到m-1.有的题可能会比较绕。

然后我们再看看特殊矩阵的压缩:

1. 什么是压缩存储? 若多个数据元素的值都相同,则只分配一个元素值的存储空间,且零元素不占存储空间。

2. 什么样的矩阵能够压缩?       一些特殊矩阵,如:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等。

3. 什么叫稀疏矩阵? 矩阵中非零元素的个数较少(一般小于5%)。

举几个例子:

最后看看广义表:

 广义表(列表):  n ( >=0 )个表元素组成的有限序列,记作LS = (a0, a1, a2, …, an-1)  , LS是表名,ai是表元素,它可以是表 (称为子表),可以是数据元素(称为原子)。   n为表的长度。n = 0 的广义表为空表。

  基本运算:

(1)求表头GetHead(L):非空广义表的第一个元素,可以是一个单元素,也可以是一个子表

(2)求表尾GetTail(L):非空广义表除去表头元素以外其它元素所构成的表。表尾一定是一个表

 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值