串、数组和广义表

目录

案例引入

串的类型定义

串的存储结构

顺序存储结构

链式存储结构(块链结构)

串的模式匹配算法

BF算法        O(m*n)

KMP算法        O(m+n)

数组

数组的顺序存储

二维数组的存储

三维数组的存储按页/行/列 存放,页优先的顺序存储

特殊矩阵的压缩存储

对称矩阵

三角矩阵 

对角矩阵(非零元素都集中在以主对角线为中心的带状区域中)

 稀疏矩阵的存储

稀疏矩阵的三元组存储

稀疏矩阵的链式存储结构---十字链表

 广义表

 广义表的链式存储结构

  • 零个或多个任意字符组成的有限序列
  • 串中的数据元素只能是字符
  • 一个串中任意个连续字符组成的子序列(含空串)称为该串的子串;真子串是指不包含自身的所有子串
  • 主串:包含子串的串相应地称为主串
  • 字符位置:字符在序列中的序号为该字符在串中的位置
  • 子串位置:子串第一个字符在主串中的位置
  • 空格串:由一个或多个空格组成的串,与空串不同
  • 串相等:如果两个串每个位置的串值对应相等(相同),称这两个串相等
  • 所有的空串是相等的

案例引入

病毒感染检测        P106

人的DNA序列是线性的,而病毒的DNA序列是环状的

串的类型定义

ADT String {
    数据对象:D = { ai|ai∈CharacterSet,  i=1,2,…,n, n ≥0 }
    数据关系:R = {<ai-1, ai>| ai-1, ai∈D,  i=2,3,…,n }
    基本操作:
    StrAssign(t , chars)
    初始条件: chars是一个字符串常量。
    操作结果:生成一个值为chars的串t 。
    StrConcat(s, t)
    初始条件:串s, t 已存在。
    操作结果:将串t联结到串s后形成新串存放到s中。
    StrLength(t)
    初始条件:字符串t已存在。
    操作结果:返回串t中的元素个数,称为串长。
    SubString (s, pos, len, sub)
    初始条件:串s, 已存在, 1≦pos≦StrLength(s)且 0≦len≦StrLength(s) –pos+1。
    操作结果:用sub返回串s的第pos个字符起长度为len的子串。
    ……
} ADT  String

串的存储结构

顺序存储结构

#define MAXLEN 255
typedef struct{
    char ch[MAXLEN+1];    //存储串的一维数组
    int length;        //串的当前长度
}SString;

链式存储结构(块链结构)

优点:操作方便;                缺点:存储密度较低 

可以将多个字符存放在一个结点中,以克服其缺点

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

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

串的模式匹配算法

确定主串中所含子串(模式串)第一次出现的位置(定位)

BF算法        O(m*n)

i的回溯可以表示为(i-j+1)+1

匹配成功之后,子串在主串中第一次出现的位置为i-T.length

int Index_BF(SString S,SString T,int pos){
    int i = pos,j = 1;
    while(i <= S.length && j <= T.length){
        if(s.ch[i] == T.ch[j]){    //主串和子串依次匹配下一个字符
            ++i; ++j;    
        }else{                //主串、子串指针回溯重新开始下一次匹配
            i = i-j+1+1;    j = 1;
        }
    }
    if(j > T.length)    return i-T.length;    //返回匹配的第一个字符的下标
    else return 0;    //模式匹配不成功
}

若n为主串长度,m为子串长度

总共比较次数:(n-m)*m+m = (n-m+1)*m

若m << n,则算法复杂度为O(m*n)

KMP算法        O(m+n)

主串S的指针i不必回溯,可提速到O(m+n)

int Index_KMP(SString S,SString T,int pos){
    int i = pos,j = 1;
    while(i <= S.length && j <= T.length){
        if(j == 0 || s.ch[i] == t.ch[j]){    //主串和子串依次匹配下一个字符
            ++i; ++j;    
        }else{               
           j = next[j];    //模式串向后移动
        }
    }
    if(j > T.length)    return i-T.length;    //返回匹配的第一个字符的下标
    else return 0;    //模式匹配不成功
}

计算NEXT函数值

void get_next(SString T,int next[]){
        //求模式串T的next函数值并存入数组next
    i = 1; next[1] = 0; j = 0;
    while(i < T.length){
	    if(j == 0 || T.ch[i] == T.ch[j]){
		    ++i;++j;
            next[i] = j;
	    }else
		    j = next [j];
	}
}

计算NEXTVAL函数修正值

void get_nextval(SString T, int nextval[]){
//求模式串T的next函数修正值并存入数组nextval
    i = 1; nextval[1] = 0; j = 0;
    while(i < T.length){
	    if(j == 0 || T.ch[i] == T.ch[j]){
		    ++i;++j;
		    if(T.ch[i] != T.ch[j])	nextval[i]	= j;
		    else	nextval[i] = nextval[j];
	    }
	    else	j = nextval[j];
    }
}

数组

数组:按一定格式排列起来的具有相同类型的数据元素的集合

一维数组:若线性表中的数据元素为非结构的简单元素,则称为一维数组(线性结构)

数组的顺序存储

数组的特点:

        结构固定--维数和维界不变;

        数组的基本操作:初始化、销毁、取元素、修改元素值;一般不做插入和删除操作。所以,一般都是采用顺序存储结构来表示数组

二维数组的存储

        若二维数组(Aij)m*n 的每个元素占用的存储单元数为L个, LOC[a00]表示元素a11的首地址,即数组的首地址
行优先顺序存储
        ①第1行中的每个元素对应的(首)地址是:
            LOC[a1j] = LOC[a11] + j*L       j=1,2, …,n
        ②第2行中的每个元素对应的(首)地址是:
            LOC[a2j]=LOC[a10]+ n*L + j*L      j=1,2, …,n
        ③第m行中的每个元素对应的(首)地址是:
            LOC[anj]=LOC[a00]+(n*j+j)*L     j=1,2, …,n 

三维数组的存储按页/行/列 存放,页优先的顺序存储

a[m1][m2][m3]各维元素个数为m1,m2,m3

Loc(i1,i2,i3) = a + i1*m2*m3 + i2*m3 + i3

特殊矩阵的压缩存储

  • 矩阵的常规存储的特点:可以对其元素进行随机存取;矩阵元素非常简单;存储的密度为1
  • 不适宜常规存储的矩阵:值相同的元素很多且呈某种规律分布;零元素多
  • 矩阵的压缩存储:为多个相同的非零元素只分配一个存储空间;对零元素不分配空间

对称矩阵

存储方法:只存储下(上)三角(包括主对角线)的数据元素,共占用n(n+1)/2个元素空间 

可以以行序为主序将元素存在在一个一维数组sa[n(n+1)/2]中

元素在矩阵中的位置aij,其存放在下标以0开始的一维数组中,存放的位置k=[i(i-1)]/2+(j-1)

三角矩阵 

对角线以上(以下)的数据元素怒(不包括对角线)全部为常数c

重复元素c共享一个元素存储空间,共占用n(n+1)/2+1个元素空间

右上三角矩阵:k = (i-1)*(2n-i+2)/2+j-i+1        i<=j;        k=n(n+1)/2+1        i>j

左下三角矩阵:k = i*(i-1)/2+j        i>=j;        k=n(n+1)/2+1        i<j

对角矩阵(非零元素都集中在以主对角线为中心的带状区域中)

以主对角线为0

 稀疏矩阵的存储

稀疏矩阵的三元组存储

  • 对于稀疏矩阵,采用压缩存储方法,只存储非0元素。必须存储非零元素的行下标值、列下标值、元素值。因此,一个三元组(i,j,aij)唯一确定稀疏矩阵的一个非零元素
  • 三元组结点定义
#define MAX_SIZE 101
    typedef int elemtype;
    typedef struct{
        int row;	//行下标
        int col;	//列下标
        elemtype value;	//元素值
}Triple;
  • 三元组顺序表定义
typedef struct{
    int rn;		//行数
    int cn;		//列数
    int tn;		//非零元素个数
    Triple data[MAX_SIZE];
}TMatrix;

 三元组顺序表的优点:非零元素在表中按行序有序存储,因此便于进行依次顺序处理的矩阵运算

三元素顺序表的缺点:不能随机存取,若按行号存取某一行中的非零元,则需从头开始进行查找

稀疏矩阵的链式存储结构---十字链表

优点: 它能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的各种运算

 typedef struct Clnode{
    int row,col;    //行号和列号
    elemtype value;    //元素值
    struct Clnode *down, *right;//分别指示同一列中的下一个非零元素和同一行中的下一个非零元素
}OLNode; //非0元素结点

 广义表

  • 线性表定义为n(n≧0 )个元素a1, a2 ,…, an的有穷序列,该序列中的所有元素具有相同的数据类型且只能是原子项(Atom)。所谓原子项可以是一个数或一个结构,是指结构上不可再分的
  • 习惯上,原子用小写字母,子表用大写字母
    广义表LS非空时:
    ◆ a1(表中第一个元素)称为表头;head(LS) = a1      表头可能是一个原子,也可能是一个子表
    ◆ 其余元素组成的子表称为表尾;tail(LS) = (a2,a3,…,an)        表尾一定是一个表,加个()
    ◆ 广义表中所包含的元素(包括原子和子表)的个数称为表的长度。
    ◆ 广义表中括号的最大层数称为表深 (度)
  • 广义表可以被其它广义表所共享,也可以共享其它广义表。广义表共享其它广义表时通过表名引用A=()    B=(())也就是B(A)
  • 广义表本身可以是一个递归表,递归表的深度是无穷值,长度是有限值

 广义表的链式存储结构

  • 一类是表结点,用来表示广义表项,由标志域,表头指针域,表尾指针域组成
  • 另一类是原子结点,用来表示原子项,由标志域,原子的值域组成
  • 只要广义表非空,都是由表头和表尾组成。即一个确定的表头和表尾就唯一确定一个广义表

typedef struct GLNode{
	int tag;	//标志域,为1:表结点;	为0:原子结点
	union {	//共用体
		elemtype value;	//原子结点的值域
		struct {
			struct GLNode *hp, *tp;
		}ptr;		//ptr和atom两成员公用
	}Gdata;
}GLNode;	//广义表结点类型

对于上述存储结构,有以下特点:
    ①若广义表为空,表头指针为空;否则,表头指针总是指向一个表结点,其中hp指向广义表的表头结点(或为原子结点,或为表结点);tp指向广义表的表尾(表尾为空时,指针为空,否则必为表结点)
    ②表结点太多,会造成空间浪费。可以使用以下结点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值