数据结构与算法 | 2.线性结构——线性表
博客内容为 浙江大学:陈越姥姥 的教学视频笔记

- 线性结构的特点:在数据元素的非空有限集中,
- (1)存在唯一的一个被称作“第一个”的数据元素
- (2)存在唯一的一个被称作“最后一个”的数据元素
- (3)除第一个之外,集合中的每个数据元素均只有一个前驱
- (4)除最后一个之外,集合中每个元素均只有一个后继
1.线性表的类型定义
线性表是最常用且最简单的数据结构。简言之,线性表是n个数据元素的有限序列。
两个注意点:序列(即元素之间是有顺序的),有限(即元素个数是有限的)
(1)简单线性表、复杂线性表举例
- 举例1(简单).26个英文字母的顺序表 (A,B,C,...,Z)
- 举例2(复杂).学生登记表
- 在复杂线性表中,一个数据元素可以由若干个数据项(item)组成。此时,常把数据元素成为记录(record),含有大量记录的线性表又称文件(file)。
- 在这个线性表中,共有5条记录(数据元素,一行为一条记录)
- 每条记录(数据元素)共由5个数据项组成,分别是:学号、姓名、性别、年龄、班级。
(2)线性表 “引子:多项式表示”
- [例]一元多项式及其运算
- 一元多项式:f(x) = a0 + a1x + a2x2 + ... + an-1xn-1 + anxn
- 主要运算:多项式相加、相减、相乘等。
- [分析]如何表示多项式?
- 多项式的关键数据
- 多项式项数n
- 各项系数ai及指数i
- 多项式的关键数据
方法1:顺序存储结构直接表示
- 数组各分量对应多项式各项:
- a[i]:项xi的系数ai
- 例如: f(x) = 4x5 - 3x2 + 1
- 表示成:
- 两个多项式相加:两个数组对应分量相加
- 问题:如何表示多项式 x + 3x2000?
- 如果按照上述表示,则数组大小需要为 2001 ,而只有两项为非零,造成了空间的极大浪费(遍历时也浪费时间)。
方法2:顺序存储结构表示非零项
- 每个非零项aixi涉及两个信息:系数ai和指数i
- 可以将一个多项式看作是一个 (ai,i) 二元组的集合。
- 用结构数组表示:数组分量是由系数 ai、指数i组成的结构,对应一个非零项
- 例如:P1(x) = 9x12 + 15x8 + 3x2 和 P2(x) = 26x19 - 4x8 -13x6 + 82

- 按指数大小有序存储!!!
- 上述两个多项式P1 和 P2 相加过程
- P1 和 P2 按照指数递减排列
- P1:(9,12), (15,8), (3,2)
- P2:(26,19), (-4,8), (-13,6), (82,0)
- 对比 P1 的第一项和 P2 的第一项比较,找到指数大的项。得到最大项并输出(26,19)
- 对比 P1 的第一项和 P2 的下一项比较,找到指数大的项。得到第二项项并输出(9,12)
- 对比 P1 的下一项和 P2 的下一项比较,得知此时指数大小相等,因此把系数相加。得到第三项并输出(11,8)
- ...
- 最终得到 P3:(26,19), (9,12), (11,8), (-13,6), (-13,6), (82,0)
- 对应的多项式表示为: P3 = 26x19 + 912 + 118 - 136 + 32 + 82
方法3:链表结构存储非零项
- 链表中每个结点存储多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域

- 结构定义
typedef struct PolyNode *Polynomial; struct PolyNode{ int cofe; int expon; Polynomial link; }
- 例如: P1(x) = 9x12 + 15x8 + 3x2 和 P2(x) = 26x19 - 4x8 -13x6 + 82 的链表存储形式为:

- 两个多项式相加的逻辑和方法2一样
2.线性表的抽象数据类型描述
- 线性表的抽象数据类型描述
ADT List{ 类型名称:线性表(List) 数据对象集: }
3.线性表及其顺序存储
多项式表示问题的启示:
a.同一个问题可以有不同的表示(存储)方法
b.有一类共性问题:有序线性序列的组织和管理
(1)什么是线性表
- 线性表(Linear List):由同类型数据元素构成有序序列的线性结构
- 表中元素个数称为线性表的长度
- 线性表没有元素时,称为空表
- 表起始的位置称为表头,表结束位置称为表尾
(2)线性表的顺序存储实现
- 利用数组的 连续存储空间顺序存放 线性表的各元素
数据结构的定义
- (线性表的顺序存储)数据结构的定义
typedef struct LNode *List; struct LNode{ ElementType Data[MAXSIZE]; int Last; }; struct LNode L; List PtrL; // 访问下标为 i 的元素:L.Data[i] 或 PtrL->Data[i] // 线性表的长度:L.Last+1 或 PtrL->Last+1
主要操作的实现
-
1)初始化(建立空的顺序表)
List MakeEmpty(){ List PtrL; PtrL = (List)malloc(sizeof(struct LNode)); Ptrl->Last = -1; return PtrL; } -
2)查找
int Find(ElementType X, List PtrL){ int i = 0; while(i <= PtrL->Last && PtrL->Data[i] != X) i++; if(i > PtrL->Last) return -1; // 如果没找到,返回-1 else return i; // 找到后返回的是存储位置 }- 查找成功的平均比较次数为 (1 + 2 + ... + n)/n = (n+1)/2;平均时间性能为O(n)。
-
3)插入(第i(1 <= i <= n+1)个位置上插入一个值为X的新元素)
- 先移动,再插入。(下标为 i - 1 及其以后部分往后挪)
void Insert(ElementType X, int i, List PtrL){ int j; if(PtrL->Late == MAXSIZE - 1 ){ /* 判断是否表满 */ printf("表满"); return; } if(i <= 1 || PtrL->Last+2){ /* 检查插入位置的合法性 */ printf("位置不合法"); return; } for(j = PtrL->Last; j >= i-1; j--) PtrL->Data[j+1] = PtrL->Data[j]; /* 将ai ~ an倒叙后移 */ PtrL->Data[i-1] = X; /* 插入新元素 */ PtrL->Last++; /* Last仍指向最后元素 */ return; }- 平均时间复杂度O(n)
-
4)删除(删除表的第i(1<=i<=n)个位置上的元素)
- 将ai+1~an向前移动一个位置
void Delete(int i, List PtrL){ int j; if(i<=1 || i>PtrL->Last+1){ printf("不存在第%d个元素",i); return; } for(j=i; j<=PtrL->Last; j++) PtrL->Data[j-1] = PtrL->Data[j]; PtrL->Last--; return; }- 平均时间复杂度O(n)
4.线性表的链式存储实现
不要求逻辑上相邻的两个元素在物理上也相邻;通过“链”建立起数据元素之间的逻辑关系。
- 插入、删除不需要移动数据元素,只需要修改“链”。
- 数组时,可以根据下标直接访问第i个元素,并且Last指针可以知道表的长度。
- 链式存储时,这两个问题怎么解决呢?
主要操作的实现
-
1)求表长
int Length(List PtrL){ List p = PtrL; int j = 0; while(p){ p = p->Next; j++; } return j; } -
2)查找
- 按序号查找:FindKth;
List FindKth(int K; List PtrL){ List p = PtrL; int i = 1; while(p!=NULL && i<k){ p = p->Next; i++; } if(i==k)return p; else return NULL; } - 按值查找:Find
List Find(ElementType X, List PtrL){ list p = PtrL; while(p != NULL && p->Data != X) p = p->Next; return p; }
- 按序号查找:FindKth;
-
3)插入(第i(1 <= i <= n+1)个位置上插入一个值为X的新元素)
- 先构造一个新结点,用s指向;
- 再找到链表的第i-1个结点,用p指向;
- 然后修改指针,插入结点(p之后插入新节点是s)

代码实现(前面省略)... 注意顺序: s = (List)malloc(sizeof(struct LNode)); s->Data = X; s->Next = p->Next; p->next = s; -
4)删除(删除表的第i(1<=i<=n)个位置上的元素)
- 先找到链表的第i-1个结点,用p指向;
- 再用指针s指向要被删除的结点(P的下一个结点)
- 然后修改指针,删除s所指向结点
- 最后释放s所指向结点的空间(防止内存泄漏)

代码实现(前面省略)... 如果是第一个结点(表非空)PtrL: s = PtrL; PtrL = PtrL->Next; free(s); 如果不是第一个结点 s = p->Next; p->Next = s->Next; free(s);
5.双向链表和循环链表
6.广义表和多重链表
我们知道了一元多项式的表示,那么二元多项式又该如何表示呢?
P(x,y) = 9x12y2 + 4x12 + 15x8y3 - x8y + 3x2
(1)广义表
- 广义表是线性表的推广
- 对于线性表而言,n个元素都是基本的单元素
- 广义表中, 这些元素不仅可以是单元素,也可以是另一个广义表
(2)多重链表
- 链表中的节点可能同时 隶属于多个链表
- 多重链表中结点的指针域会有多个
- 包含两个指针域的链表不一定是多重链表(双向链表不是多重链表)
- 多重链表有广泛的用途,基本上如树、图这样相对复杂的数据结构可以采用多重链表方式实现存储。


浙公网安备 33010602011771号