视频课程:数据结构(浙江大学: 陈越、何钦铭)(第二讲)
┏━━━━━━目录━━━━━━┓
2.1 线性表及其实现
2.2 堆栈2.3 队列
2.4 应用实例:多项式加法运算
┗━━━━━━━━━━━━━━┛
2.1 线性表及其实现
1、引子——多项式:项数n,各项系数ai,指数i
方法1:顺序存储结构直接表示(数组)
方法2:顺序存储结构表示非零项(结构数组:按指数大小有序存储)
方法3:链表结构存储非零项包(链表:括系数和指数两个数据域以及一个指针域)
* 1.同一个问题可以有不同的表示(存储)方法
* 2. 有一类共性问题:有序线性序列的组织和管理
2、线性表(Linear List):由同类型数据元素构成有序序列的线性结构
(1)表中元素个数称为线性表的长度
(2)线性表没有元素时,称为空表
(3)表起始位置称表头,表结束位置称表尾
3、线性表的抽象数据类型描述
类型名称:线性表(List)
数据对象集:线性表是 n (≥0)个元素构成的有序序列( a1, a2, ...,an )
操作集:线性表L属于 List,整数i表示位置,元素X ElementType,
4、线性表的顺序存储实现——利用数组的连续存储空间顺序存放线性表的各元素
* 【插入】平均移动次数为 n /2,平均时间性能为 O(n)
* 【删除】平均移动次数为 (n-1) /2,平均时间性能为 O(n)
* 【查找】成功的平均比较次数为(n +1)/2,平均时间性能为 O(n)。
5、线性表的链式存储实现——不要求逻辑上相邻的两个元素物理上也相邻;通过“链”建立起数据元素之间的逻辑关系。
* 【求表长】【查找】 时间性能为 O(n)。
* 【插入】【删除】平均查找次数为 n /2,平均时间性能为 O(n),不需要移动数据元素,只需要修改“链”。删除元素后需要释放空间。
6、广义表:表中元素不仅可以是单元素也可以是另一个广义表。(广义表是一种多重链表)
〖例〗 二元多项式的表示
7、多重链表:链表中的节点可能同时隶属于多个链
(1)多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域;
(2)但包含两个指针域的链表并不一定是多重链表,比如在双向链表不是多重链表。
* 多重链表有广泛的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。
〖例〗 矩阵的表示
(1)可以用二维数组表示,但二维数组表示有两个缺陷:
a. 数组的大小需要事先确定,
b. 对于“稀疏矩阵 ”,将造成大量的存储空间浪费。
(2)采用一种典型的多重链表——十字链表来存储稀疏矩阵
a. 只存储矩阵非0元素项——结点的数据域:行坐标Row、列坐标Col、数值Value
b. 每个结点通过两个指针域,把同行、同列串起来—— 行指针(或称为向右指针)Right、列指针(或称为向下指针)Down
2.2 堆栈(Stack)
堆栈是具有一定操作约束的线性表——只在一端(栈顶,Top)做 插入、删除
1、堆栈的抽象数据类型描述:
(1)插入数据:入栈(Push)——入栈前检查堆栈是否已满
(2)删除数据:出栈(Pop)——出栈前检查是否为空
(3)后入先出:Last In First Out(LIFO)
2、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的所有排列都可能是出栈的序列吗? NO
•可以产生CAB这样的序列吗? NO
3、栈的顺序存储实现:栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成。
〖例〗请用一个数组实现两个堆栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功。
〖分析〗使这两个栈分别从数组的两头开始向中间生长;当两个栈的栈顶指针相遇时,表示两个栈都满了。
4、堆栈的链式存储实现:栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。
〖思考〗栈顶指针Top应该在链表的哪一头?(链头部作为TOP)
5、后缀表达式
中缀表达式:运算符号位于两个运算数之间。如 ,a + b * c - d / e
后缀表达式:运算符号位于两个运算数之后。如, a b c * + d e / -
后缀表达式求值策略:从左向右“扫描”,逐个处理运算数和运算符号
〖例〗 6 2 / 3 - 4 2 * + = ? 8
6、堆栈应用:表达式求值
● 后缀表达式:从左到右读入后缀表达式的各项(运算符或运算数);
1.运算数:入栈;
2.运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
3.最后,堆栈顶上的元素就是表达式的结果值。
● 基本策略:将中缀表达式转换为后缀表达式,然后求值
〖思考〗如何将中缀表达式转换为后缀?
(1)运算数相对顺序不变(遇到运算数则输出)
(2)运算符号顺序发生改变
* 需要存储“等待中”的运算符号
* 要将当前运算符号与“等待中”的最后一个运算符号比较(用到堆栈)
〖例〗2+9/3-5 ——→ 2 9 3 / + 5 -
输出 2 9 3
堆栈 + / (此时-优先级低于/,/可以输出了)
〖例〗a * ( b + c ) / d = ? ——→ abc + * d /
* 左括号入栈之后,优先级降至最低
● 中缀表达式如何转换为后缀表达式
(1)从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
① 运算数:直接输出;
② 左括号:压入堆栈;
③ 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
④ 运算符:
•若优先级大于栈顶运算符时,则把它压栈;
•若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
⑤ 若各对象处理完毕,则把堆栈中存留的运算符一并输出。
7、堆栈的其他应用:
(1)函数调用及递归实现
(2)深度优先搜索
(3)回溯算法
(4)……
2.3 队列(Queue)
队列是具有一定操作约束的线性表——只能在一端插入,而在另一端删除。
1、队列的抽象数据类型描述
(1)数据插入:入队列(AddQ)
(2)数据删除:出队列(DeleteQ)
(3)先来先服务——先进先出:FIFO
2、队列的顺序存储实现:由一个一维数组和一个记录队列头元素位置的变量front以及一个记录队列尾元素位置的变量rear组成。
* 顺环队列(如何实现队列首尾相接:求余)
(1)这种顺环队列的方案:堆栈空和满的判别条件是什么?
〖分析〗front==rear表示队列空,但是顺环队列时,队列满也出现 front==rear
(2)为什么顺环队列会出现空、满无法区分?根本原因?
〖分析〗举例n=6的顺环队列,front-rear的距离有n种,0~n-1 (0,1,2,3,4,5)
队列装载元素情况有n+1种 (7个情况:0队列空,1一个元素,2,3,4,5,6);n不能表示n+1种情况
解决方案:①使用额外标记(size或者tag域)②仅使用n-1数组空间
3、队列的链式存储实现:队列的链式存储结构也可以用一个单链表实现。插入和删除操作分别在链表的两头进行。
〖思考〗队列指针front和rear应该分别指向链表的哪一头?(链表头作front,链表尾作rear)
2.4 应用实例:多项式加法运算
(1)主要思路:相同指数的项系数相加,其余部分进行拷贝。
(2)采用不带头结点的单向链表,按照指数递减的顺序排列各项
(3)算法思路:两个指针P1和P2分别指向这两个多项式第一个结点(最高项),不断循环:
①P1->expon==P2->expon: (指数相等)系数相加,若结果不为0,则作为结果多项式对应项的系数。同时,P1和P2都分别指向下一项;
②P1->expon>P2->expon: (P1指数大)将P1的当前项存入结果多项式,并使P1指向下一项;
③P1->expon<P2->expon: (P2指数大)将P2的当前项存入结果多项式,并使P2指向下一项;
④当某一多项式处理完时,将另一个多项式的所有结点依次复制到结果多项式中去。