数据结构与算法(二)线性表

7月4日 新开这本日记,也为了督促自己下个学期多下些苦功。先要读完手边的莎士比亚的《亨利八世》。
7月13日 打牌。
7月14日 打牌。
7月15日 打牌。
7月16日 胡适之啊胡适之!你怎么能如此堕落!先前订下的学习计划你都忘了吗?子曰:“吾日三省吾身。”不能再这样下去了!
7月17日 打牌。

空闲的时间段里,玩游戏和看小说占了绝大部分时间,学习总是断断续续,想起了上面广为流传的那段对话。
唉,自从上次,后面不看小说了,bilibili真好玩。。。这篇还能不能发出去了。。。

一、概述

线性结构,是一对一的关系。线性结构中,只存在唯一一个第一个元素,唯一一个最后一个元素,每个元素(除第一个之外)均只有一个前驱,(除最后一个之外)也只有一个后驱。这种存在一一对应关系的数据结构就叫线性数据结构。

线性数据结构有很多,只要是一一对应关系的数据结构都属于线性数据结构。如上所述,类型有线性表,栈,队列,串,数组,广义表这几种,接下来针对这些数据结构进行描述。

二、线性表

线性表是n个数据元素的有限序列。

1.顺序表示

因线性表的长度可变,因此这里用柔性数组来实现线性表的顺序表示,.或者先分配固定空间,后续容量不足时使用动态分配的空间来扩容。

2.链式表示

2.1单链表

由于链式存储结构的特点,除了存储数据本身的信息外,还要存储一些额外的信息来记录对象间的对应关系,这两者分别被称为数据域及指针域。每个链表结点中只包含一个指针域(即存储直接后继存储位置的指针域)的链表,被称为单链表。
整个单链表的存取必须从头指针(链表中第一个结点的存储位置)开始,同时,由于最后一个结点没有后继,所以最后一个结点的指针为空。
链表的存取由于必须挨个遍历,因此是非随机存取的数据结构。
单链表常在第一节结点之前附设一个头结点,头结点的数据域可以不存储任何信息,也可以存储如线性表长度等类的附加信息,头结点的指针域存储指向第一个结点的指针。

2.2静态链表

静态链表即使用一维数组来实现的链表。
他可以在不设“指针”类型的高级程序设计语言中使用链表结构。静态数组的指针域不再存储指针,而是存储的对象在数组中的位置。

2.3循环链表

单链表最后一个结点的指针域为空,而循环链表中最后一个结点的指针域指向头结点,整个链表形成一个环。循环链表从表中任一结点触发均可找到表中其他结点,而单链表每次都要从头结点重新开始。
循环链表有时可以在尾部设一个尾指针而不设头指针,这样可以简化某些操作,比如将两个线性表合并未一个表时,而没有尾指针时,则需要遍历到尾部来改变指针值。

2.4双向链表

在一个指针域的链表中,由于只包含了直接后继的信息,所以想从某个结点出发只能顺指针往后查询其他结点,若想查找它的直接前驱,则要从表头指针出发。而拥有两个结点域的指针可以解决这个问题,即双向链表。
双向链表中的每个结点拥有两个指针域,一个指向直接后继,一个指向直接前驱。

3.典型应用

3.1 一元多项式的表示及相加

三、栈

栈和队列的基本操作是线性表的子集,它们是操作受限的线性表。在“丢失”某些操作时,栈和队列这两种受限线性表反而获得某些提升。受限的操作集合使得使用这些数据结构时,更关注于具体而细分的操作本身,而不是浪费精力在维护不必要的,数据间的关系。因此处理不同的问题需要选定对应的数据结构。
栈是限定仅在表尾进行插入或删除操作的线性表。对栈来说,表尾端称为栈顶,表头端称为栈底。栈是后入先出的线性表。
栈可以看成是一个篮子,每次往里放的东西都依次叠在一起,每次只能从最上面取东西,一次只能取一个。

1.顺序表示

前面说栈是一种操作受限的线性表,同样的,它的顺序表示和线性表是相同的,只不过对他的操作是严格受限的。

2.链式表示

栈的链式表示称为链栈,他可以用一个单链表来实现。由于栈的特殊性,链栈可能在一些特殊应用时才能具有大的收益。

3.典型应用

3.1数制转换

比如十进制转八进制

3.2括号匹配检查

3.3行编辑程序

接受用户输入并存储时,建立输入缓冲区,可以支持退格或一次性全删,而不是输入一个字符就立即保存。

3.4迷宫求解

穷举法找出迷宫通路

3.5表达式求值

表达式中符号的优先级

3.6汉诺塔问题-栈与递归的实现

递归,即调用自身,来完成计算的一种方式。
汉诺塔问题,即将一摞圆盘移到另一根柱子。若根据给出的条件正向推理,繁杂的令人不能思考,所以可以逆向的来想这个问题。假如可以完成这个操作,那么必定有一个中间态,最大圆盘上面的所有圆盘已整体移到目标柱子旁边的空柱,这样接下来最大圆盘就可以移到目标柱子上。完成这一步,接下来就是把那一大摞在移动到目标柱子上,假设这个操作耗费次数为n,那么总次数就是n+1+n,即把这一大摞圆盘移动了两次再加最大圆盘移动的一次。而其实移动这一大摞圆盘也是重复之前的那个中间态的,所以就可以一直倒推,从而计算出全部的次数。这种算法由于最开始的次数未知,所以可以用递归的方式,直到计算出最简单的情况,移动一个圆盘,然后再依次返回,从而得到总的次数。而递归的操作就依赖于程序栈了。

四、队列

前面说栈是先入后出,队列与其相反,是先入先出。它只允许在表的一端进行插入,而在另一端删除元素。允许插入的一端叫队尾,允许删除的一端叫队头。队列可以和平时的排队相类比。

1.分类

上面所述的只能在固定的一端插入和删除的表叫做普通队列,除了普通队列还有一种叫双端队列。双端队列的插入端和删除端并不固定,可以有以下几种组合:
双端都可插入删除
一端可插入删除,另一端只能插入
一端可插入删除,另一端只能删除
从哪端插入只能从哪端删除,实际上是蜕变为两个栈底相接的栈
双端队列的两端被称为端点1和端点2
尽管双端队列看起来比栈和队列更灵活,但实际上在应用程序中远不及栈和队列有用。

2.链式表示

用链表表示的队列叫链队列,它有指向队头和队尾的两个指针唯一确定这个链队列。

3.顺序表示

使用数组及指向头尾的两个指针来实现队列的顺序表示,由于队列和栈不同,被删除的结点还占用着数组空间,因此可以用循环队列来实现,即当结点占用满数组最后一个空间,而最先加入的结点已被删除,此时再增加则重新从数组开头来作为新加结点的存储空间。

4.典型应用

4.1排队问题

五、串

这里的串指的是字符串。
涉及概念:主串 子串 子串在主串中的位置 空格串 截断

1.顺序表示

顺序表示有两个方式,定长和不定长顺序表示
因为字符串经常作为输入输出常量出现,因此可以用定长顺序表示来存储。定长的顺序存储可以用一个定长数组来存储。而不定长的顺序表示即为动态分配的串值空间。

2.链式表示

用链表来存储串值,称为块链。为方便对串的操作,除头指针外,还可以附设一个尾指针,并给出当前串的长度。块链总体来说不如前两种存储结构灵活。

3.模式匹配算法

子串的定位操作通常称作串的模式匹配。被查找的串称为模式串。

3.1KMP算法

目的:减少模式匹配算法的复杂度。
传统匹配算法中,使用双指针来匹配,即字符串扫描指针,匹配模式扫描指针。匹配成功两个指针都加1,否则回退字符串扫描指针到下一位置,匹配模式扫描指针置为0;当存在多次不完全匹配可能引发多次回退,比如用"0001"匹配"000000000000000000000000001";
使用kmp算法时,当匹配失败,不再是回退字符串扫描指针,而是尽可能的向右滑动匹配模式字符串,原理是利用已匹配的部分。举个例子,想象有这样两个字符串,s1和s2,s1=“abc”,s2=“def”,他们组成这样一个字符串:s1+s2+s1+s2,即"abcdefabcdef",把这个字符串作为模式字符串,待匹配字符串为"abcdefabcghi".匹配到第四段的第一个字符匹配失败,则匹配情况如下

abc def abc ghi
abc def abc def

,到第10个不匹配,传统算法中,将从第2个字符即b开始从头匹配模式字符串,而kmp算法则直接将匹配模式字符串滑动从第10个匹配模式字符串的第4个字符:

abc def abc ghi
       (abc)def abc def

kmp算法的核心原理就是这样,当第j个字符匹配失败,如果存在一个s1,分别位于模式字符串开头与匹配失败字符前,1<s1.length<j。这时候就可以直接把模式字符串开头的s1滑动到待匹配位置。
整个匹配过程变为,如果第一个就匹配失败,跳过当前一个。非第一个,如果存在长度最长的s1,则将模式匹配指针回退到前一个s1的位置。如果不存在s1,则将模式匹配指针回退到第一个。
对于s1的规则可以描述为:next(j)=k;j为匹配失败的模式字符串位置。k为要回退的位置。

4.典型应用

4.1文本编辑

4.2建立词索引表

六、数组

七、广义表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值