数据结构mooc(1&2)

1.1什么是数据结构

关于数据组织

-例1:如何在书架上摆放图书?(书-数据,书架-存储空间)
-图书的摆放要使2个相关操作方便实现:
1.新书怎么插入?
2.怎么找到某本指定的书?
-方法:
(a)随便放:插入简单,查找麻烦
(b)按照书名拼音字母顺序排放:插入麻烦(可能需要移动所有书来腾空间),二分查找更容易。
©分类,在类别内按照书名拼音字母数序排放:查找和插入都更容易。
但空间如何分配?类别要分多细?
-说明解决问题方法的效率,跟数据的组织方式是直接相关的。

关于空间使用

-例2:实现PrintN函数(输入正整数N,打印1到N的正整数)

​ -方法:循环实现/递归实现(代码可读性强但占用空间)
​ -说明解决问题方法的效率,跟空间的利用效率有关。

关于算法效率

-例3:计算多项式值f(x)=a0+a1x+…+a_x^n

在这里插入图片描述

-测试9阶多项式(n=10),f1和f2的duration都是0(图2)。因为两个函	数跑的太快了,无法捕捉两个函数的区别。
-解决方法:让被测函数重复运行充分多次,使得测出的总的时钟打点间隔	充分长,最后计算被测函数平均每次运行的时间即可。
-第一个算法比第二个算法慢了一个数量级,第二个算法更好。
-说明解决问题方法的效率,跟算法的巧妙程度有关。

到底什么是数据结构?

数据结构是数据对象在计算机中的组织方式。在数据对象上的一系列操作方法是算法。

	# 1.数据对象的逻辑结构:

​ (a)一对一:线性结构
​ 例:一个编号对应一本书
​ (b)一对多:树形结构
​ 例:图书分类,每个类别一个编号,对应多本书
​ ©多对多:图形结构

例:一本书有那些人买过,买了这本书的人还买过哪些书?

# 2.数据对象的物理存储结构:

在内存里怎么放?连续着放还是隔开放?(数组还是链表?)

在这里插入图片描述

抽象数据类型(Abstract Data Type)

  1. 抽象:描述数据类型的方法不依赖于具体实现。
    (a)与存放数据的机器无关
    (b)与数据存储的物理结构无关
    ©与实现操作的算法和编程语言均无关
  2. 数据类型:C语言分开独立处理,面向对象语言(C++/Java)把两个 集封装在一个类里。
    (a)数据对象集
    (b)数据集合相关联的操作集

在这里插入图片描述

1.2什么是算法

算法的定义

# 算法(Algorithm)定义:

​ (a)一个有限指令集:一堆(有限个数的)指令放在一起,去做一件事。
​ (b)接受一些输入(有些情况下不需要输入)
​ ©一定至少产生一个输出(否则算法无意义)
​ (d)一定在有限步骤之后终止
​ (e)每一条指令必须:
​ *有充分明确的目标,不可以有歧义
​ *目标要在计算机能处理的范围之内
​ *描述应不依赖于任何一种计算机语言以及具体的实现手段
​ -例1:选择排序算法的伪码描述
​ *选择排序:从未排序好的这堆元素中,每次选一个最小的元素贴到已经 排好序的子序列最后面,最后得到从小到大排好序的序列。

在这里插入图片描述

	*以上伪码描述很抽象,因为没有写具体实现的细节,比如:
	List到底是数组还是链表(虽然看上去很像数组)?
	Swap用函数还是用宏去实现?用List还是数组还是链表?
	*这些具体实现的细节,在我们描述算法的时候是不关心的。

什么是好的算法?

​ -通常关心两个指标:空间(Space)复杂度和时间(Time)复杂度。
# (1)空间复杂度S(n)——根据算法写成的程序在执行时点用存储单元的长度。

这个长度往往与输入数据的规模有关。空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断。

​ *例2:递归实现PrintN函数打印N个整数

在这里插入图片描述

·N-100000,程序爆掉,因为空间不够
·调用函数之前,系统需要把当前的这个函数所有的现有的状态存在内存的某一 个地
·S(N)随着N线性增长

·循环实现:占用的空间始终固定,不会随着N的增长而增长,S(N)是一个常量。

(2) 时间复杂度T(n)

—根据算法写成的程序在执行时耗费时间的长度。这个长度往往也与输入数据的规模有关。时间复杂度过高的低效算法可能导致我们在有生之年都等不到运行结果。

例3:计算多项式的值

在这里插入图片描述

·机器运算加减法的速度比乘除法快得多,所以我们只数函数做了多少次乘除法,加减法忽略不计。
·程序1的for循环内:pow函数运行i-1次,再乘以i,共i次乘法。所以共(1+2+…+n)次。
·程序2每一次循环只有一次乘法,执行n次,共n次。
·n很大的时候,第一个程序n平方起主要作用。所以第二个程序比第一个程序跑的快得多。

在分析一般算法的效率时,我们经常关注下面两种复杂度:

(1)最坏情况复杂度 T worst(n)

(2)平均复杂度T avg(n)

•我们在分析的时候,比较喜欢分析的是最坏情况复杂度。因为平均的定义 很难搞定。

在这里插入图片描述

复杂度的渐进表示

​ -分析算法复杂度的时候,其实没必要做精细的分析,只关心随着数据规模 的增大,增长的趋势会怎么样。
​ -复杂度的渐进表示法:

在这里插入图片描述

*n很大时,f(n)是T(n)的某种上界;
g(n)是T(n)的某种下界;
h(n)对T(n)来说,既是上界,也是下界。
*上界和下界不唯一,但太大的上界/太小的下界,对分析算法的效率没有帮 助。 希望上界/下界和真实情况越贴近越好(找到能力范围内最小的上界/ 最大的下界)
*不同函数的增长速率:

| 在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

*当算法复杂度为0(n^2)时,我们应该尽可能把它改进成O(nlogn)。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.3应用实例:最大子列和问题

​ -最大子列和问题:给定N个整数的序列[A1,…AN,求函数f(i,)的最大值。
​ 如果最大子列和为负数,返回0。
​ -算法1(暴力算法):找出所有连续子列和,然后选出最大的那一个。

在这里插入图片描述

*问题:重复计算,每次都从头开始加。当增加了1的时候,其实只要在 前面那个i到j的部分加1个元素就好,k的循环没必要。
-算法2:解决了算法1的问题,去除k循环。

在这里插入图片描述

*问题:能不能把O(n^2)改进成O(nlogn)?
-算法3(分而治之):解决算法2的问题。
*分而治之:把一个大的复杂问题切分成许多小问题,然后分头解决 它们,最后把结果合并。
*把数组一分为二,递归的解决左右两边的问题,分别找到左边和右 边的最大子列和,其次找到跨越边界的最大子列和,最后比较这三 个结果,找出最大的那一个。

在这里插入图片描述

关于时间复杂度(倒数第二行->倒数第一行):

2^K=N,k=logN (base 2),T1(n)+T2(n)=max(O(n),O(nlogn)),所以算法3的时间复杂度为0(nlogn)。程序比较长,可以在MOOC浙大数据结构1.3讲义中找到代码。

在这里插入图片描述

-算法4(在线处理):如果当前和为负,则不能使后面的部分和增大,当前和	归0。
*“在线”的意思是指每输入一个数据就进行即时处理,在任何一个地方中	止输入,算法都能正确给出当前的解。
*比算法3快,是最理想的速度(总归要把所有数都读一遍)但代码可读性差。

在这里插入图片描述
在这里插入图片描述

2.1线性表及其实现

•引子:多项式表示
-例:一元多项式及其运算(多项式相加、相减、相乘等) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

-表示多项式的关键数据:(1)多项式项数n (2)各项系数a[i]及指数i

## 方法1:顺序存储结构直接表示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

*问题:表示多项式 x+3x^2000需要用有2001个分量的数组表示 (每个分量是系数),其中只有两项非零,这种表示方法非常浪费空间。做 加法时,需要做很多无效循环(从0到2000)。那么能不能只表示非零项呢?

方法2:顺序存储结构表示非零项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

*运算很方便,如要相加P[1](x)和P[2](x)。相加过程:从头开始,比	 较两个多项式当前对应项的指数。比较两个多项式当前最大指数的那一	项,然后输出,较小的那一项继续和另一数组的下一项比较。如果指数	一样,系数相加。

在这里插入图片描述

*加法运算的逻辑过程和之前一样。

线性表及顺序存储

​ -多项式表示问题的启示:
​ 1.同一个问题可以有不同的表示(存储)方法。(数组:直接表示或表示非 零项/链表)
​ 2.有一类共性问题:有序线性序列的组织和管理。
​ -线性表(Linear List):由同类型数据元素构成有序序列的线性结构。
​ *表中元素个数称为线性表的长度

*线性表没有元素时,称为空表
*表起始位置称表头,表结束位置称表尾
-线性表的抽象数据类型描述:
*类型名称:线性表(List)
*数据对象集:线性表是n(>=0)个元素构成的有序序列(a1,a2,an)

*操作集
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

*比起顺序存储来,查找更麻烦。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-多重链表:链表中的节点可能同时隶属于多个链。(刚才广义表的例子 就是一个多重链表)
*多重链表中结点的指针域会有多个,

如前面例子包含了Next和SubList两个指针域;

*但包含两个指针域的链表并不一定是多重链表,比如双向链表不是 多重链表。(一个指针往前指,一个指针往后指,两个指针串起来 的 链表是同一 个,只是指向了结点的不同方向,所以不是多重链 表)
*多重链表有广泛的用途:基本上如树、图这样相对复杂的数据结构 都可以采用多重链表方式实现存储。
-【例】矩阵的表示
*最简单的方法是用二维数组表示,但二维数组表示有两个缺陷:
(1)数组的大小需要事先确定;
(2)对于“稀疏矩阵”(很多零),将造成大量的存储空间浪费。

在这里插入图片描述

*方法:采用一种典型的多重链表—十字链表来存储稀疏矩阵
(1)只存储矩阵非0元素项——结点的数据域:行坐标Row、列坐标Col、 数值Value
(2)每个结点通过两个指针域,把同行、同列串起来;
(a)行指针(或称为向右指针)Right
(b)列指针(或称为向下指针)Down
©用一个标识域Tag来区分头结点和非0元素结点:
(d)头节点的标识值为“Head”,矩阵非0元素结点的标识值为

​ “Term” 在这里插入图片描述

*结点有两种类型:head和term,都有两个指针(Down和Right),但是里面内容不一样(一个是Row,Column,Value一个是Next),我们用union把这两个不同的结点串起来。

在这里插入图片描述

*term代表稀疏矩阵的非零项,有两个指针:一个指向同一行,一个指向同一列,都是循环链表,所有某个结点属于某一行也属于某一列,形成了十字结构,所以叫十字链表。
*head作为行链表和列链表的头结点。
*左上角term为整个稀疏矩阵的入口。4,5,7代表整个矩阵有四行/五列/非零项个数有七项,通过这个term可以找到所有行和列的头结点。

在这里插入图片描述

1.当k小于1时会返回错误

2.当k为最后一个的时候也会返回错误

2.2堆栈

什么是堆栈

​ -堆栈一种线性结构,是特殊的线性表。
​ -线性结构和线性表的关系:“线性结构就是说表中各个结点是线性关系,如 栈,队列和串等都是线性结构。线性表就是典型的线性结构,也是最简单最 常用的数据结构。”一来自百度
​ -堆栈的用途包括:函数调用、递归、表达式求值等。
​ -【例】:表达式求值-算术表达式5+6/2-34
正确理解:5+6/2-34=5+3-3
4=8-3*4=8-12=-4
​ *一般算数表达式由两类对象构成:
​ (1)运算数(如2、3、4)
​ (2)运算符号(如+、-、·
​ *不同运算符号优先级不一样
​ *看到运算符号做计算吗?但这时只知道一个运算数,另一个运算数 还在后面,后面的运算数也不一定参与运算(如例中5+6)。—由于 表达式把运算符号放在两个运算数中间,求值变得困难。
如果看到运算符号时,已经知道两个运算数了,这样求值就会容易 很多。
​ -后缀表达式
中缀表达式:运算符号位于两个运算数之间。如,a+bc-d/e
​ 后缀表达式:运算符号位于两个运算数之后。如,abc+de/-(等价)
【例】6 2/3- 4 2 * +=? 6/2-3+42=3-3+8=8
​ (6 2/ -> 6/2=3 -> 3 3- -> 3-3=0 -> 0 4 2 * + -> 0+ 4
2=8)
​ *后缀表达式求值策略:从左向右“扫描”,逐个处理运算数和运算符号
​ 1.遇到运算数怎么办? 如何“记住”目前还不未参与运算的数?
​ 2.遇到运算符号怎么办? 对应的运算数是什么?

*启示:需要有种存储方法,能顺序存储运算数,并在需要时“倒序”输出!

在这里插入图片描述

T(N)=O(N)

-堆栈(Stack):具有一定操作约束的线性表。
*只在一端(栈顶,Top)做插入、删除。
*插入数据:入栈(Push)
*删除数据:出栈(Pop)
*后入先出:Last In First Out(LIFO)
-堆栈的抽象数据类型描述:
*类型名称:堆栈(Stack)
*数据对象集:一个有0个或多个元素的有穷线性表
*操作集:长度为MaxSize的堆栈S in Stack,堆栈元素item in ElementType.堆栈基本操作

主要有:
在这里插入图片描述

在这里插入图片描述

-Push和 Pop 可以穿插交替进行;
*按照操作系列:
(1)Push(S,A),Push(S,B),Push((S,C),Pop(S),Pop(S),Pop(S)。

堆栈输出是?CBA
(2)而Push(S,A), Pop(S),Push(S,B),Push((S,C),Pop(S),Pop(S)。堆 栈输出是?ACB

-【例】:如果三个字符按ABC顺序压入堆栈,ABC的所有排列都可能是出栈 的序列吗?可以产生CAB这样的序列吗?不可能,只能CBA。

A.堆栈的顺序存储实现

​ -栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

B.堆栈的链式存储实现

-栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只 能在链栈的栈顶进行。
*栈顶指针Top应该在链表的哪一头?
–>在链表头插入和删除都很方便,在链尾插入很方便,但是删除很 麻烦,因为找不到前面一个结点。

在这里插入图片描述

-主要操作的实现

\1. 堆栈初始化(建立空栈)

在这里插入图片描述
在这里插入图片描述

2.判断堆栈是否为空

在这里插入图片描述

3.入栈

在这里插入图片描述

相当于在链表头插入一个节点

*不用像数组(大小固定)一样判断堆栈满不满,链表实现不选在满不 满的问题。

4.出栈

在这里插入图片描述

·堆栈应用:表达式求值
-回忆:应用堆栈实现后缀表达式求值的基本过程:
*从左到右读入后缀表达式的各项(运算符或运算数);
*(把运算数放到堆栈里,碰到运算符号的时候,把运算数 抛出来做计算,再放回堆栈)
1.运算数:入栈;
2.运算符:从堆栈中弹出适当数量的运算数,计算并结果入 栈;
3.最后,堆栈顶上的元素就是表达式的结果值。

在这里插入图片描述

-中缀表达式求值
基本策略:将中缀表达式转换为后缀表达式,然后求值。但如 何将中缀表达式转换为后缀?
观察一个简单例子:2+9/3-5->293/+5-;我们可以发现:
1.运算数相对顺序不变
2.运算符号顺序发生改变
(a)需要存储“等待中”的运算符号(堆栈!)
(b)要将当前运算符号与“等待中”的最后一个运算符 号比较
输出:2 9 3 ___ 输出:2 9 3 /
记下:
/ — 记下:

(那有括号怎么办?)
【例】:a(b+c)/d=? 输出:abc+*d/

在这里插入图片描述

*当括号在堆栈外,括号先进行计算;如左括号在堆栈里,先把括号里面算完,左括号优先
级降到最低。遇到右括号,把堆栈顶的运算符号一个个抛出,直到碰到做括号为止。乘号
遇到除号,按从左到右的顺序,抛出乘号,记下除号。遇到d,抛出除号。

在这里插入图片描述

-总结:中缀表达式如何转换为后缀表达式
*从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
1.运算数:直接输出;
2.左括号:压入堆栈;
3.右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不 输出);
4.运算符:
(a)若优先级大于栈顶运算符时,则把它压栈;
(b)若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再 比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止, 然后将该运算符压栈;
5.若各对象处理完毕,则把堆栈中存留的运算符一并输出。
【例】:(2(9+6/3-5) +4)

在这里插入图片描述

*中缀转换为后缀示例*

在这里插入图片描述

在这里插入图片描述

-堆栈的其他应用:

(1) 函数调用及递归实现
(2)深度优先搜索
(3)回溯算法

2.3 队列

什么是队列

-队列(Queue):具有一定操作约束的线性表,
*插入和删除操作:只能在一端插入,而在另一端删除。
*数据插入:入队列(AddQ)
*数据删除:出队列(DeleteQ)
*先来先服务
*先进先出:FIFO

-队列的抽象数据类型描述
*类型名称:队列(Queue)
*数据对象集:一个有0个或多个元素的有穷线性表。
*操作集:长度为MaxSize的队列Q in Queue,队列元素 item in ElementType.队列基本操作

​ 主要有: 在这里插入图片描述

A.队列及顺序存储实现

-队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量front以及一个记录队列尾元素位置的变量rear组成。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

在这里插入图片描述

*队列为空时,front和rear都为-1。front一般指向第一个元素的前一个位置。
*每次加一个job, rear往后挪(+1),front不变。

*删除job1,front指向0,front+1。
*job7排在队列最后,无法加入job8,但队列头还是空的。能不能加到前头 去?(顺环队列)

在这里插入图片描述

​ 操作类似,但如果放满了,可以回过头再放到0。
*队列空和满时,front和rear相等。如何判断队列的状态呢?
*具体根据front和rear的相对关系来判别,取值范围为0到5(n-1)。Front 和rear的差距是6种情况(0~5)。队列装载元素个数情况有7种:0(空队列),1(一个元素),2,3,4,5,6。也就是说,如果队列大小是n,front与rear之间的差距的可能情况也是n种,队列的装载元素的情况有(n+1)种。想用n种状态来区分(n+1)种情况是不可能的。
*解决方法:

(1) 使用额外标记,size(记录元素个数)或者tag域(插入元素tag=1, 删除元素tag=0,可以看最后一次操作是插入还是删除,就可以判断队 列是空还是满)
(2)仅使用(n-1)个数组空间(我们采取这种方案)

-主要操作的实现
1.入队列

在这里插入图片描述

【例】中,为了实现5的下一个是0。5+1对6求余数为0。我们用求余函数实现。

2.出队列

在这里插入图片描述

同上,front+1 指向队列头上元素的位置。

B.队列的链式存储实现

在这里插入图片描述

*front要做删除操作,rear要做插入操作。链表头做插入和删除都很 方便,链表末尾只方便做插入,不能做front。

在这里插入图片描述

2.4 实例应用:多项式加法运算

主要思路:相同指数的项系数相加,其余部分进行拷贝。

在这里插入图片描述

-采用不带头结点的单向链表,按照指数递减的顺序排列各项。

在这里插入图片描述

存储示意图和结构定义
每一项是一个结点,包含三个分量:系数、指数、指针。
算法思路:两个指针P1和P2分别指向这两个多项式第一个结点,不断循环(比较指数)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
pRear是指针的指针

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值