ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数据结构-串,数组,广义表

2021-12-13 22:30:27  阅读:133  来源: 互联网

标签:字符 存储 数组 元素 矩阵 next 广义 数据结构


数据结构-串,数组,广义表

王卓老师课程的听课笔记

串,数组,和广义表

  • 对于串而言,数据元素只能是字符
  • 可以看作是线性表(内容受限的线性表)
  • 是线性结构的推广(某种意义上讲,已经不再属于线性结构)

  • 串(String)是由零个或多个任意字符组成的有限序列

  • 串的内容称之为串值,串的名称称为串名

  • 当元素大小等于0时,被称为空串,用空集的符号表示,空串连空格都没有。

  • 子串:一个串中任意个连续字符组成的子序列(含空串),称为该串的子串。

  • 真子串是指不包含自身的所有子串

  • 主串:包含子串的串相应地称为主串

  • 字符位置:字符在序列中的序号为该字符在串中的位置

  • 字串位置:子串第一个字符在主串中的位置

  • 空格串:由一个或多个空格组成的串,与空串不同

  • 串相等:当且仅当两个串长度相等并且各个对应位置上的字符都相同时,这两个串才相等,空串全部都相等

串的类型定义以及存储结构

  • 串中元素逻辑关系与线性表相同,串可以采用与线性表相同的存储结构。
  • 串的顺序存储结构

    #define MAXLEN 255
    typedef struct{
    	char ch[MAXLEN+1];//存储串的一维数组
    	int length;//串的当前长度
    }SString;
    
    • 串的顺序存储,通常将数组的第一个元素空开。这样有利于后续的一些字符串算法的实现。
  • 串的链式存储结构

    • 优点:操作方便
    • 缺点:存储密度较低
    • 存储密度=串值所占的存储/实际分配的存储

如果将多个字符存放在一个结点中,就可以克服存储密度低的缺点。

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

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

串的模式匹配算法

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

  • 算法应用:

    • 搜索引擎、拼写检查、语言翻译、数据压缩
  • 算法种类:

    • BF算法(穷举法)
    • KMP算法
  • BF算法

    主串S(正文串),子串T(模式串)

    • 从S的每一个字符开始一次与T的字符进行匹配。
    • 匹配的内容时每一个字符,一一比较看是否相同。
    • 匹配失败:i = i-j+2 = 2(回溯,回溯岛上一个匹配的位置);j = 1(从头开始);
    • 核心思想:
      • 将主串的第pos个字符和模式串的第一个字符比较
      • 若相等,继续逐个比较后续字符
      • 若不等,从主串的下一个字符起,重新与模式串的第一个字符比较。
      • 直到主串的一个连续子串字符序列与模式串相等。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。
      • 否则,匹配失败,返回值0;
  • BF算法代码:

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

      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 + 2;
                  j = 1;//主串、子串指针回溯重新开始下一次匹配
              }
          }
          if(j>=T.length) return i-T.length;//返回匹配的第一个字符的下标
          else return 0;//模式匹配不成功
      }
      
      • 这个pos是开始的位置。第一个算法是默认从第一位开始匹配的,第二个算法默认是从pos位置开始。
  • KMP算法
    • 相较于BF算法,主串i的指针不再回溯了,假如在子串中有和子串首元素相同的字符,而且在之前的匹配过程中已经匹配完毕了,那么J也没有必要回溯至0。
    • 为此,定义next[j]数组 ,表明当模式串中第j个字符与主串中相应字符“失配”时,在模式串中需重新和主串中该字符进行比较的字符的位置。
    • next[j]存储三种数据:
      • 当j = 1时,next[j]=0
      • 其他情况,next[j]=1
      • 当主串从头开始的k-1个元素(前缀)和j前面的k-1个元素(后缀)相同时。next[j]=max(1,j)
int Index KMP(SString S,SString T,int pos)
{
	i = pos,j = i;
    while(i<S.length && j<T.length)
    {
        if(j==0||S.ch[i]==T.ch[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];/*i不变,j后退*/
        }
    }
    if(j>T.length)
    {
        return i-T.length;/*匹配成功*/
    }
    else return 0;
}

求next[j]的算法

void get_next(SString T,int &next[])
{
	i = 1;next[1] = 0;j = 0;
	while(i<T.length)
	{
		if(j == 0||T.ch == T.ch[j])
		{
			++i;
			++j;
			next[i] = j;
		}
		else
		{
			j = next[j];
		}
	}
}
  • 针对next函数的改进

    • 如果前面出现了很多相同的字符,字符本身没有匹配上,那么后面也不需要重复的对已经失配的字符城府匹配了。

      void get_nextval(SString T,int &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];
      	}
      }
      

数组(就是那个你熟悉的)

  • 按照一定的格式排列起来的,具有相同类型的数据元素的集合。

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

  • 一维数组的逻辑结构:线性结构。定长的线性表。

  • 声明格式:数据类型 变量名称[长度];

  • 可以单个赋值,也可以统一初始化赋初值。

  • 二维数组:若一维数组中的数据元素又是一维数组结构,则称为二维数组

  • 二维数组的逻辑结构

    • 非线性结构:每一个数据元素既在一个行表中,又在一个列表中。(不只有一个前驱,一个后继,有多个前驱,多个后继)
    • 线性结构,定长的线性表:该线性表的每个数据元素也是一个定长的线性表。
  • 声明格式:数据类型 变量名称[行数] [列数];

  • 在C语言中,一个二维数组类型也可以定义为一维数组类型(其分量类型为一维数组类型),即:

    typedef elemtype array2[m][n];
    
    //等价于:
    typedef elemtpye array1[n];
    typedef array1 array2[m];
    
  • 三维数组:若二维数组中的元素又是一个一维数组,则称作三维数组。

  • n维数组:若n-1维数组中的元素又是一个一维数组结构,则称作n维数组。

  • 线性表结构是数组结构的一个特例,而数组结构又是线性表结构的扩展。

  • 数组特点:结构固定——定义后,维数和维界不再改变。

  • 数组基本操作:除了结构的初始化和销毁之外,只有取元素和修改元素值的操作。

  • 数组的结构固定,维数和维界不变,所以,一般采用顺序存储结构来表示数组。
  • 注意:数组可以是多维的,但存储数据元素的内存单元地址是一维的,因此,在存储数组结构之前,需要解决将多维关系映射到一维关系的问题
  • 二维数组在内存中如何存储呢?

    C,PASCAL,Java,Basic,是以行序为主序(一行一行来存储)

    个别语言以列序为主序

  • 设数组开始存储位置LOC(0,0),存储每个元素需要L个存储单元,数组元素a[i] [j]的存储位置是:LOC(i,j) = LOC(0,0) + (n*i+j) *L

  • 三维数组,按页/行/列存放,计算地址位置同理二维数组。

特殊矩阵的压缩存储

  • 矩阵的常规存储:

    将矩阵描述为一个二维数组

  • 矩阵的常规存储的特点:

    • 可以对其元素进行随机存取;
    • 矩阵运算非常简单;存储的密度为1.
  • 不适宜常规存储的矩阵:值相同的元素很多,且呈某种规律分布;零元素多。

  • 矩阵的压缩存储:为多个相同的非零元素只分配一个存储空间;对零元素不分配空间。

  • 什么是压缩存储?

    若多个数据元素的值都相同,则只分配一个元素值的存储空间,且零元素不占存储空间。

  • 什么样的矩阵能够压缩?

    一些特殊矩阵,如:对称矩阵,对角矩阵,三角矩阵,稀疏矩阵等。

  • 什么叫稀疏矩阵?

    矩阵中非零元素的个数较少(一般小于5%)

  • 对称矩阵

    • 特点:在n*n的矩阵中,沿主对角线对称的元素相同。
    • 存储方法:只存储下(或者上)三角(包括主对角线)的数据元素。共占用n(n+1)/2个元素空间。

存储时可以以行序为主序储存下三角

按顺序存储到一维数组当中

三角矩阵的压缩存储

  • 特点:对角线一以下(或者以上)的数据元素(不包括对角线)全部为常数C。
  • 存储方法:重复元素c共享一个元素存储空间,共占用n(n+1)/2+1个元素

对角矩阵(带状矩阵)

  • 在n*n的方阵中,所有非0 元素都集中在以主对角线为中心的带状区域中,区域外的值全为0,则称为对角矩阵。常见的有三对角矩阵、五对角矩阵、七对角矩阵等。(有几条,就叫做几对角矩阵)
  • 用二维数组去存,从上往下,一条对角线存一行。

稀疏矩阵

  • 三元组法确定稀疏矩阵的内容
  • 由于稀疏矩阵里面的元素特别的少,所以三元组里面包含三个元素(行,列,数据本身),这样就知道了该矩阵的所有情况。
  • 三元组用顺序表存储,通常还得再加一个“总体”信息:即总行数、总列数、非零元素总个数。这样可以按照这个顺序表复现稀疏矩阵。
  • 三元组顺序表又称有序的双下标法。
  • 三元组顺序表的优点:非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。
  • 三元组顺序表的缺点:不能随机存取,若按行号存取某一行中的非零元,则需从头开始进行查找。
  • 十字链表:
    • 优点:能够灵活地插入因运算而产生的新的非0元素,删除因运算而产生的新的零元素,实现矩阵的各种运算。
    • 在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了三元组(row,col,value)以外,还要有两个域:
      • right:用于链接同一行中的下一个非零元素。
      • down:用于链接同一列中的下一个非零元素。
      • 还需要行的头指针数组和列的头指针数组

广义表

  • 又称List,是n>=0个元素的有限序列,其中每一个元素,可以是任何形式的数据。

  • 广义表通常记作LS = (a1,a2,a3…,an)

    其中:LS为表名,n为表的长度,每一个ai为表的元素。

  • 习惯上,一般用大写字母表示广义表,小写字母表示原子。

  • 表头:若LS非空,则第一个元素a1就是表头。记作head(LS) = a1.表头可以是原子,也可以是子表。

  • 表尾:除表头之外的其他元素组成的表。

    记作 tail(LS) = (a2,…,an)

    注:表尾不是最后一个元素,而是一个子表。

  • 广义表中的数据元素有相对次序:一个直接前驱和一个直接后继

  • 广义表的长度定义为最外层所包含元素的个数;

  • 广义表的深度定义为该广义表展开后所含括号的重数;

  • 广义表可以为其他广义表共享。

  • 广义表可以是一个递归的表,长度有限,深度无限。

  • 广义表是多层次结构,广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表…

广义表可以看成是线性表的推广,线性表是广义表的特例

常见算法

  • 求表头GetHead(L):表头可以是元素也可以是子表
  • 求表尾GetTail(L):表尾一定是一个表

存储方式

  • 链表(共用体链表?)

标签:字符,存储,数组,元素,矩阵,next,广义,数据结构
来源: https://blog.csdn.net/theglasssky/article/details/121915950

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有