线性表的实现与多项式表示
<线性结构>笔记目录
线性表的实现与多项式表示 http://www.zsenhe.com/article/83
堆栈与表达式求值问题 http://www.zsenhe.com/article/84
队列,顺环队列 http://www.zsenhe.com/article/85
引子——多项式表示
线性结构是数据结构里最基础,最简单的一种类型;其中最典型的便是“线性表”,什么是线性表呢?
考虑一下一元多项式的表示:image.png
这样的多项式基本运算包括: 两个多项式相加,相乘,相减等;那么,怎么用程序设计语言来表示这样一个多项式及运算呢?在程序设计语言里面,要表示一个问题,首先要分析一下问题的关键数据和关键信息;对于多项式而言,它的关键信息主要有它的项数和最高指数。
其中一种最简单的方法便是 顺序存储的直接表示
直接使用一个数组来表示多项式的有关信息,数组各分量对应多项式各项
a[i] 项 x^i 的系数;i为对应的指数
比方对于下面这个多项式:
image.png
表示为:
image.png
这样用6个分量来表示该多项式;这样的表示是很简单的,且运算也方便
如对于两个多项式的相加,即变成了 两个数组对应分量的相加
但该表示方式也是有问题的,如果我们要存储
http://cdn.zsenhe.com/QQ%E5%9B%BE%E7%89%8720230129004327.jpg
那么需要2001长度的数组来表示,而这2001个分量,只有两项是非0的,于是便造成了巨大的空间浪费;且运算的时候会多一些没必要的操作(大量的+0)
有没有可能只表示非0项呢?我们可以将多项式看成一个由 系数和指数 组成的二元组集合,使用结构数组来表示这样的多项式image.png
对于前面的x+2x^2000,只需要使用两个分量来存储;且同样运算很方便,其中要点是每一项按指数大小降序存储
例如加法运算,图中的两个多项式,他们用分量表示为
p1:
(9,12) (15,8) (3,2)
p2:
(26,19) (-4,8) (-13,6) (82,0)
从头开始,比较两个多项式当前指数较大的那一项,例如第一项选择 (9,12) 与(26,19),前面提过我们需要按照指数大小降序排放,那么选择较大那一项输出(26,19),接下来(9,12)与(-4,8)比较,显然输出(9,12) ;接下来比较的两个分量指数相同,系数相减,输出和(11,8),接下来的比较输出(-13,6) (3,2);最后是p2的剩余项(82,0)
最后得到新的多项式image.png
对比一下,我们使用结构体的形式存储在数组中表示非0项,这是一种节省空间的方法
当然,我们也可以使用一种其他形式存储多项式 链表
它的表示方法为: 每个节点存储多项式中的一个非0项,包括系数和指数两个数据域以及一个指针域image.png
1 | typedef struct PolyNode *Polynomial; |
前面的两个多项式,他们以链表的形式存储状态为:image.png
运算的逻辑过程与数组表现的形式一样
线性表
由多项式的问题得知: 同一个问题可以有不同的表现方法,也就是不同的存储方法;对于多项式的表示,要么使用数组要么使用链表。实际上很多问题与多项式表示是有共性的,我们的最终目标都是:管理,组织一个有序的线性序列,归结为线性表问题
什么是线性表?
由同类型数据元素构成有序序列的线性结构:
表中元素个数称为线性表的 长度
线性表没有元素时称为 空表
表起始位置称 表头,表结束位置称 表尾
线性表的存储
学习数据结构时我们最关心的是它的存储,对于线性表的存储其中最简单的一种方式便是顺序存储,即使用数组的方法实现image.png
这种方式需要关心的是线性表的 数据类型 以及 长度
1 | struct LNode{ |
使用指针last代表线性表的最后一个元素,访问长度时执行 L.last+1
这样的结构就可以抽象的实现线性表,比如说当想要访问下标为i的元素时使用 L.Data[i]
主要操作的实现
为了方便,我们使用指针来传递线性表
1 |
|
1.初始化操作
1 | List MakeEmpty(){ |
注意初始化时表长度为-1,即表示数组长度为0
2.查找操作
1 | int Find(char x,List Ptrl){ |
从0开始遍历表,当while循环退出时,要么i==ptrl->last+1要么data[i]=x,这两种情况分别对应着 未寻找到元素 和 找到元素 它的时间复杂度显然为O(n)
3.插入
插入的目的是在线性表的第i个位置上插入一个新的元素,传入的i指的是[1,last+1] (1即是表头,last+1表尾);所以实际上我们要把元素插入到i-1的位置上image.png
那么首先要做的就是把i-1之后的所有元素往后挪动一位,我们用一个循环来做这件事
1 | void Insert(char x,int i,List Ptrl){ |
该操作的时间复杂度依然是O(n)
4.删除元素
我们要把第i-1个元素移出,那么实际上要做的把i及之后的元素全部往前挪,这个过程按从左往右的顺序做
1 | void Delete(int i,List Ptrl) { |
链式存储
线性表除了用数组实现,同样可以使用链表来组织
线性表使用数组实现时,两个相邻的元素不止逻辑上是相邻的,在物理上也是相邻的(数组在内存中也是连续存放的一片空间);而我们使用链表实现时,实际上是要求两个元素只在逻辑上相邻(通过“链”建立),而不要求物理上相邻
这样的好处是进行插入,删除操作时,只需要修改“链”的指向,而不需要像数组实现那样移动整个空间,这会节省很多时间image.png
1 |
|
如图,链表中每个节点拥有两个分量,Data是节点所对应的数据,Next指向下一个节点
访问序号为i的元素?求线性表的长度?
上文提到链实现线性表在插入删除方面有着数组无可比拟的高效,那除此之外呢?在数组实现中访问序号为i的元素是很简单的事,直接ElelementType[i-1]即可;同样,我们也可以直接取last来知道线性表的长度.但在链表中,这两个操作比起数组实现就复杂了许多
求表长
1 | int length(List ptrl){ |
因为需要遍历整个链表,它的时间复杂度为O(n);在数组实现中该操作是O(1)的
按序号访问节点
1 | List findKth(int k,List ptrl){ |
当while循环退出时,意味着!p或者i<k这两个条件之一被破坏了,当p不为null时,即找到了目标节点
按值查找
1 | List findKth0(char target,List ptrl){ |
插入与删除
插入
将数据插入到i的位置上,意味着我们要将数据插入到i-1的后面,为了完成这个操作
我们需要进行以下几个步骤的操作
1.申请一片新的内存,使用s指向它
2.找到链表的i-1个节点,使用p指向
3.修改指针,插入节点image.png
修改指针的动作有两步,将s->next指向p->next,再将p->next指向s(注意不要搞反)
1 | List insert(char x,int i,List ptrl){ |
删除
删除同理是对指针的修改,同样的使用p指向i-1,申请一个新的指针s指向p->next,最后将p->next指向s->next即可;通过malloc申请的空间要记得free回去
1 | List Delete(int i,List ptrl) { |
完整的代码存档:
1 |
|
1 |
|
本文标题:线性表的实现与多项式表示
文章作者:meteor
发布时间:2023-01-28
最后更新:2023-01-28
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
分享