数据结构概念

一、数据结构概念

数据结构概念:
数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。

1、数据

程序设计 = 数据结构 + 算法。
“数据”是描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合。也就是说,我们这里说的数据其实就是符号,而且这些符号必须具备两个前提:
1.可以输入到计算机中
2.能被计算机程序处理,比如,数值、声音、图像、视频等都是数据。

数据元素

“数据元素”是组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理。也被称为记录。 比如,在人类中,人就是数据元素。

数据项

一个数据元素可以由若干个数据项组成。数据项是数据不可分割的最小单位。 比如人这个数据元素,可以有眼、耳、嘴等这些数据项,也可以有姓名、年龄、性别等这些数据项。 数据对象 “数据对象”是性质相同的数据元素的集合,是数据的子集。 性质相同,是指数据元素具有相同数量和类型的数据项。比如人都有姓名、性别等相同的数据项。

数据结构

“数据结构”是相互之间存在一种或多种特定关系的数据元素的集合。
它们之间的关系: 数据(数据对象) | 数据元素 | 数据项
逻辑结构:数据之间的相互关系。

  1. 集合
    结构中的数据元素除了同属于一种类型外,别无其它关系。

  2. 线性结构
    数据元素之间一对一的关系。

  3. 树形结构
    数据元素之间一对多的关系。

  4. 图状结构或网状结构
    结构中的数据元素之间存在多对多的关系。

  5. 物理结构/存储结构
    数据在计算机中的表示。物理结构是描述数据具体在内存中的存储(如:顺序结构、链式结构、索引结构、哈希结构)等。

在数据结构中,从逻辑上可以将其分为线性结构和非线性结构。
数据结构的基本操作的设置的最重要的准则是,实现应用程序与存储结构的独立。实现应用程序是“逻辑结构”,存储的是“物理结构”。逻辑结构主要是对该结构操作的设定,物理结构是描述数据具体在内存中的存储(如:顺序结构、链式结构、索引结构、希哈结构)等。
顺序存储结构中,线性表的逻辑顺序和物理顺序总是一致的。但在链式存储结构中,线性表的逻辑顺序和物理顺序一般是不同的。

2、算法

算法五个特性
  • 有穷性
  • 确定性
  • 可行性
  • 输入
  • 输出
算法设计要求

①正确性
②可读性
③健壮性
④高效率与低存储量需求。
⑤好的算法,算法的描述有伪程序、流程图、N-S结构图等。E-R图是实体联系模型,不是程序的描述方式。
⑥设计算法在执行时间时需要考虑:算法选用的规模、问题的规模

  • 时间复杂度:算法的执行时间与原操作执行次数之和成正比。时间复杂度有小到大:O(1)、O(logn)、O(n)、O(nlogn)、O(n2)、O(n3)。
    幂次时间复杂度由小到大O(2n)、O(n!)、O(n²)
  • 空间复杂度:若输入数据所占空间只取决于问题本身,和算法无关,则只需要分析除输入和程序之外的辅助变量所占额外空间。
  • 线性表线性表是一种典型的线性结构。头结点无前驱有一个后继,尾节点无后继有一个前驱。
  • 链表只能顺序查找,定位一个元素的时间为O(N),删除一个元素的时间为O(1)
  • 线性表的顺序存储结构:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。用这种方法存储的线性表简称顺序表。是一种随机存取的存储结构。顺序存储指内存地址是一块的,随机存取指访问时可以按下标随机访问,存储和存取是不一样的。如果是存储,则是指按顺序的,如果是存取,则是可以随机的,可以利用元素下标进行。
  • 数组比线性表速度更快的是:原地逆序、返回中间节点、选择随机节点。便于线性表的构造和任意元素的访问。 插入:插入新结点,之后结点后移。平均时间复杂度:O(n) ;删除:删除节点,之后结点前移。平均时间复杂度:O(n)
  • 线性链表:用一组任意的存储单元来依次存放线性表的结点,这组存储单元即可以是连续的,也可以是不连续的,甚至是零散分布在内存中的任意位置上的。因此,链表中结点的逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点的地址。data域是数据域,用来存放结点的值。next是指针域(亦称链域),用来存放结点的直接后继的地址(或位置)。不需要事先估计存储空间大小。
  • 单链表中每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。同时,由于最后一个结点无后继,故结点的指针域为空,即NULL。
  • 头插法建表(逆序)、尾插法建表(顺序)。增加头结点的目的是算法实现上的方便,但增大了内存开销。 查找:只能从链表的头指针出发,顺链域next逐个结点往下搜索,直到搜索到第i个结点为止。因此,链表不是随机存取结构。 插入:先找到表的第i-1的存储位置,然后插入。新结点先连后继,再连前驱。 删除:首先找到ai-1的存储位置p。然后令p–>next指向ai的直接后继结点,即把ai从链上摘下。最后释放结点ai的空间.r=p->next;p->next=r->next;delete r。 判断一个单向链表中是否存在环的最佳方法是快慢指针。
  • 静态链表:用一维数组来实现线性链表,这种用一维数组表示的线性链表,称为静态链表。静态:体现在表的容量是一定的。(数组的大小);链表:插入与删除同前面所述的动态链表方法相同。静态链表中指针表示的是下一元素在数组中的位置。
  • 静态链表是用数组实现的,是顺序的存储结构,在物理地址上是连续的,而且需要预先分配大小。动态链表是用申请内存函数(C是malloc,C++是new)动态申请内存的,所以在链表的长度上没有限制。动态链表因为是动态申请内存的,所以每个节点的物理地址不连续,要通过指针来顺序访问。静态链表在插入、删除时也是通过修改指针域来实现的,与动态链表没有什么分别。
  • 循环链表:是一种头尾相接的链表。其特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。
  • 单链表中,将终端结点的指针域NULL改为指向表头结点的或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样判断p或p—>next是否为空,而是判断它们是否等于某一指定指针,如头指针或尾指针等。
  • 双向链表:在单链表的每个结点里再增加一个指向其直接前趋的指针域prior。这样就形成的链表中有两个方向不同的链。双链表一般由头指针唯一确定的,将头结点和尾结点链接起来构成循环链表,并称之为双向链表。设指针p指向某一结点,则双向链表结构的对称性可用下式描述:p—>prior—>next=p=p—>next—>prior。
  • 从两个方向搜索双链表,比从一个方向搜索双链表的方差要小。 插入:先搞定插入节点的前驱和后继,再搞定后结点的前驱,最后搞定前结点的后继。 在有序双向链表中定位删除一个元素的平均时间复杂度为O(n) 。可以直接删除当前指针所指向的节点。而不需要像单向链表中,删除一个元素必须找到其前驱。因此在插入数据时,单向链表和双向链表操作复杂度相同,而删除数据时,双向链表的性能优于单向链表
  • 栈和队列栈栈(Stack)是限制在表的一端进行插入和删除运算的线性表,通常称插入、删除的这一端为栈顶(Top),另一端为栈底(Bottom)。先进后出。top= -1时为空栈,top=0只能说明栈中只有一个元素,并且元素进栈时top应该自增顺序存储栈:顺序存储结构
  • 链栈:链式存储结构。插入和删除操作仅限制在链头位置上进行。栈顶指针就是链表的头指针。通常不会出现栈满的情况。 不需要判断栈满但需要判断栈空。 两个栈共用静态存储空间,对头使用也存在空间溢出问题。栈1的底在v[1],栈2的底在V[m],则栈满的条件是top[1]+1=top[2]。 基本操作:删除栈顶元素、判断栈是否为空以及将栈置为空栈等 对于n各元素的入栈问题,可能的出栈顺序有C(2n,n)/(n+1)个。
  • 堆栈溢出一般是循环的递归调用、大数据结构的局部变量导致的应用,代码:进制转换 括号匹配的检验 行编辑程序 迷宫求解:若当前位置“可通”,则纳入路径,继续前进;若当前位置“不可通”,则后退,换方向继续探索;若四周“均无通路”,则将当前位置从路径中删除出去。 表达式求解:前缀、中缀、后缀。 操作数之间的相对次序不变; 运算符的相对次序不同; 中缀式丢失了括弧信息,致使运算的次序不确定。
  • 前缀式的运算规则为:连续出现的两个操作数和在它们之前且紧靠它们的运算符构成一个最小表达式。 后缀式的运算规则为:运算符在式中出现的顺序恰为表达式的运算顺序;每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。
  • 实现递归:多个函数嵌套调用的规则是:后调用先返回。 浏览器历史纪录,Android中的最近任务,Activity的启动模式,CPU中栈的实现,Word自动保存,解析计算式,解析xml/json。解析XML时,需要校验节点是否闭合,节点闭合的话,有头尾符号相对应,遇到头符号将其放入栈中,遇到尾符号时,弹出栈的内容,看是否有与之对应的头符号,栈的特性刚好符合符号匹配的就近原则。
  • 不是所有的递归程序都需要栈来保护现场,比方说求阶乘的,是单向递归,直接用循环去替代从1乘到n就是结果了,另外一些需要栈保存的也可以用队列等来替代。不是所有的递归转化为非递归都要用到栈。转化为非递归主要有两种方法:对于尾递归或单向递归,可以用循环结构算法代替队列队列(Queue)也是一种运算受限的线性表。它只允许在表的一端进行插入,而在另一端进行删除。允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。先进先出。
  • 顺序队列:顺序存储结构。当头尾指针相等时队列为空。在非空队列里,头指针始终指向队头前一个位置,而尾指针始终指向队尾元素的实际位置 循环队列。在循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。只不过当头尾指针指向向量上界(MaxSize-1)时,其加1操作的结果是指向向量的下界0。除非向量空间真的被队列元素全部占用,否则不会上溢。因此,除一些简单的应用外,真正实用的顺序队列是循环队列。故队空和队满时头尾指针均相等。因此,我们无法通过front=rear来判断队列“空”还是“满” 链队列:链式存储结构。限制仅在表头删除和表尾插入的单链表。显然仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。
  • 设尾指针的循环链表表示队列,则入队和出队算法的时间复杂度均为O(1)。用循环链表表示队列,必定有链表的头结点,入队操作在链表尾插入,直接插入在尾指针指向的节点后面,时间复杂度是常数级的;出队操作在链表表头进行,也就是删除表头指向的节点,时间复杂度也是常数级的。
  • 队空条件:rear==front,但是一般需要引入新的标记来说明栈满还是栈空,比如每个位置布尔值 。
  • 队满条件:(rear+1) % QueueSize==front,其中QueueSize为循环队列的最大长度。
  • 计算队列长度:(rear-front+QueueSize)% QueueSize 入队:(rear+1)% QueueSize 出队:(front+1)% QueueSize.
  • 假设以数组A[N]为容量存放循环队列的元素,其头指针是front,当前队列有X个元素,则队列的尾指针值为(front+X mod N)串串(String)是零个或多个字符组成的有限序列。长度为零的串称为空串(Empty String),它不包含任何字符。通常将仅由一个或多个空格组成的串称为空白串(Blank String)
  • 注意:空串和空白串的不同,例如“ ”和“”分别表示长度为1的空白串和长度为0的空串。串的表示和实现:定长顺序存储表示。静态存储分配的顺序表。
  • 堆分配存储表示。存储空间是在程序执行过程中动态分配而得。所以也称为动态存储分配的顺序表 串的链式存储结构。串匹配:将主串称为目标串,子串称之为模式串。蛮力法匹配。KMP算法匹配。Boyer-Moore算法匹配。数组和广义表数组和广义表可看成是一种特殊的线性表,其特殊在于: 表中的元素本身也是一种线性表。内存连续。根据下标在O(1)时间读/写任何元素。二维数组,多维数组,广义表、树、图都属于非线性结构数组数组的顺序存储:行优先顺序;列优先顺序。数组中的任一元素可以在相同的时间内存取,即顺序存储的数组是一个随机存取结构。
  • 关联数组(Associative Array),又称映射(Map)、字典( Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。 不是线性表。
  • 矩阵的压缩:对称矩阵、三角矩阵:直接存储矩阵的上三角或者下三角元素。注意区分i>=j和i广义表广义表(Lists,又称列表)是线性表的推广。
  • 广义表是n(n≥0)个元素a1,a2,a3,…,an的有限序列,其中ai或者是原子项,或者是一个广义表。若广义表LS(n>=1)非空,则a1是LS的表头,其余元素组成的表(a2,…an)称为LS的表尾。广义表的元素可以是广义表,也可以是原子,广义表的元素也可以为空。表尾是指除去表头后剩下的元素组成的表,表头可以为表或单元素值。所以表尾不可以是单个元素值。
  • 例子:A=()——A是一个空表,其长度为零。 B=(e)——表B只有一个原子e,B的长度为1。 C=(a,(b,c,d))——表C的长度为2,两个元素分别为原子a和子表(b,c,d)。 D=(A,B,C)——表D的长度为3,三个元素都是广义 表。显然,将子表的值代入后,则有D=(( ),(e),(a,(b,c,d)))。E=(a,E)——这是一个递归的表,它的长度为2,E相当于一个无限的广义表E=(a,(a,(a,(a,…)))).
  • 三个结论:广义表的元素可以是子表,而子表的元素还可以是子表。由此,广义表是一个多层次的结构,可以用图形象地表示。广义表可为其它表所共享。例如在上述例4中,广义表A,B,C为D的子表,则在D中可以不必列出子表的值,而是通过子表的名称来引用。
  • 广义表的递归性考点:广义表是0个或多个单因素或子表组成的有限序列,广义表可以是自身的子表,广义表的长度n>=0,所以可以为空表。广义表的同级元素(直属于同一个表中的各元素)具有线性关系 广义表的表头为空,并不代表该广义表为空表。广义表()和(())不同。前者是长度为0的空表,对其不能做求表头和表尾的运算;而后者是长度为l的非空表(只不过该表中惟一的一个元素是空表),对其可进行分解,得到的表头和表尾均是空表() 已知广义表LS=((a,b,c),(d,e,f)),运用head和tail函数取出LS中原子e的运算是head(tail(head(tail(LS)))。
  • 根据表头、表尾的定义可知:任何一个非空广义表的表头是表中第一个元素,它可以是原子,也可以是子表,而其表尾必定是子表。也就是说,广义表的head操作,取出的元素是什么,那么结果就是什么。但是tail操作取出的元素外必须加一个表——“()“。tail(LS)=((d,e,f));head(tail(LS))=(d,e,f);tail(head(tail(LS)))=(e,f);head(tail(head(tail(LS))))=e。
  • 二维以上的数组其实是一种特殊的广义表。在(非空)广义表中:1、表头head可以是原子或者一个表 2、表尾tail一定是一个表 3.广义表难以用顺序存储结构 4.广义表可以是一个多层次的结构。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值