1.12k likes | 1.23k Views
第 2 章 线性表. 概述. 线性表是最很简单也是最重要的数据结构,它是线性结构。所以我们学习数据结构从线性表开始。从本章开始到第四章都是讨论线性。是整个数据结构的基础 按逻辑结构 、存储结构、算法设计这三个顺序进行分析。. 2.1线性表的逻辑结构. 线性表( Linear List) 是由 n(n≥0) 个数据元素(结点) a 1 ,a 2 ,…,a n 组成的有限序列。 ① 数据元素的个数 n 定义为表的长度( n=0 时称为空表)。 ② 将非空的线性表( n>0) 记作:( a 1 ,a 2 ,…,a n )
E N D
概述 线性表是最很简单也是最重要的数据结构,它是线性结构。所以我们学习数据结构从线性表开始。从本章开始到第四章都是讨论线性。是整个数据结构的基础 按逻辑结构 、存储结构、算法设计这三个顺序进行分析。
2.1线性表的逻辑结构 线性表(Linear List)是由n(n≥0)个数据元素(结点)a1,a2,…,an组成的有限序列。 ① 数据元素的个数n定义为表的长度(n=0时称为空表)。 ② 将非空的线性表(n>0) 记作:(a1,a2,…,an) ③ 数据元素ai(1≤i≤n)只是个抽象符号,其具体含义在不同情况下可以不同。
线性表的逻辑结构图示 a1 ai an
线性表的例子 【例1】英文字母表(A,B,…,Z)是线性表,表中每个字母是一个数据元素(结点) 【例2】一副扑克牌的点数(2,3,…,10,J,Q,K,A)也是一个线性表,其中数据元素是每张牌的点数和花色 【例3】学生成绩表(见概论中表1.1)中,每个学生及其成绩是一个数据元素,其中数据元素由学号、姓名、各科成绩及平均成绩等数据项组成。
线性表的特征 有且仅有一个开始结点a1,没有直接前趋,有且仅有一个直接后继a2; 有且仅有一个终结结点an,没有直接后继,有且仅有一个直接前趋an-1; 其余的内部结点ai(2≤i≤n-1)都有且仅有一个直接前趋ai-1和一个ai+1。
常见的线性表的基本运算 常见的基本运算有:初始化,求表长,求第i个结点,查找,插入和删除等操作。为了尽可能地比较准确地描述些运算过程,我们采用类似于函数的形式对它进行描述。 这些基本运算并不是凭空想出来,而是根据实际应用总结出来的。
线性表的ADT表示 ADT LinearList { 数据元素:D={ai| ai∈D0,i=1,2,3…n,n≥0,D0某一数据对象} 逻辑结构:R={<ai,ai+1>, ai, ai+1 ∈D0,i=1,2,…,n-1} 基本操作:
1. InitList(L) 操作前提:L为一个未初始化的线性表。 操作结果:将L初始化为空表。 构造一个空的线性表L,即表的初始化。
2. DestroyList(L) 操作前提:线性表L已存在。 操作结果:将L销毁。
3. ClearList(L) 操作前提:线性表L已存在。 操作结果:将L置空
4. IsEmptyList(L) 判断L是否为空表。如为空返回true,否则返回faslse 前提:L已存在,
5. ListLength(L) 求线性表L中的结点个数,即求表长。
6. Locate(L,e) 确定元素e在线性表L中的位置。(与书上不同) 在L中查找值为x 的结点,并返回该结点在L中的位置。若L中有多个结点的值和x 相同,则返回首次找到的结点位置;若L中没有结点的值为x ,则返回一个特殊值表示查找失败
7. GetData(L,i) 操作前提:表L已存在,且i值 合法,返回线性表L中第i个元素的值。
8. InsList(L,i,e) 在线性表L的第i个位置上插入一个元素e,i+1,…,n的结点变为编号为i+1,i+2,…,n+1的结点。这里1≤i≤n+1,而n是原表L的长度。插入后,表L的长度加1。
9. DeleteList(L,i,e) 删除线性表L的第i个结点,使得原编号为i+1,i+2,…,n的结点变成编号为i,i+1,…,n-1的结点。这里1≤i≤n,而n是原表L的长度。删除后表L的长度减1。并且用e返回这个删除元素的值。 长度减1. }ADT LinearList
说明 这些操作只是从逻辑结构的角度来讨论的,主要用来说明这些运算的功能,是“做什么”,至于如何实现,则要等到讨论存储结构后才考虑。
2.2 线性表的顺序存储结构 概述 顺序表 顺序表的算法实现
2.2.1. 概述 简单来说了, 本节就是讨论一维数组的使用。这在C语言中已经详细讨论过。
2.2.2. 顺序表 1.定义 2.存储地址公式 3.特点
2.2.1顺序表 顺序存储方法即把线性表的结点按逻辑次序依次存放在一组地址连续的存储单元里的方法 (2) 顺序表(Sequential List)用顺序存储方法存储的线性表简称为顺序表(Sequential List)。
内存表示 a1,a2,a3,...,ai,...,an 每个结点占C个存储单元。
2. 结点ai的存储地址 设线性表中所有结点的类型相同,则每个结点所占用存储空间大小亦相同。假设表中每个结点占用c个存储单元,其中第一个单元的存储地址则是该结点的存储地址,并设表中开始结点a1的存储地址(简称为基地址)是LOC(a1),那么结点ai的存储地址LOC(ai)可通过下式计算: LOC(ai)= LOC(a1)+(i-1)*c (1≤i≤n)
思考题 int a[10], a[2]= a[3]+a[5];为什么我们在程序中引用数组元素时,没有应用此地址公式?
顺序表的特点 在顺序表中,每个结点ai的存储地址是该结点在表中的位置i的线性函数。只要知道基地址和每个结点的大小,就可在相同时间内求出任一结点的存储地址。是一种随机存取结构。 顺序表是用向量实现的线性表,向量的下标可以看作结点的相对地址。因此顺序表的的特点是逻辑上相邻的结点其物理位置亦相邻。
4. 存储结构 #define MaxSize 100 //表空间的大小可根据实际需要而定,这里假设为100 typedef struct StudInfo{ } STUDINFO; typedef int ElemType; //DataType的类型可根据实际情况而定,这里假设为int typedef struct { ElemType elem[MaxSize];//向量data用于存放表结点int last; //最后一个元素的下标 }SeqList;
ElemType是什么? 根据实际需要,把某个数据类型定义为ElemType,而且针对不同的数据类型,在程序上机测试,要对程序做相应的修改。
序号与下标(index)关系 第1个元素的下标为0 第2个元素的下标为1 ….. 第last+1个元素的下标为last
表的长度与last的关系 表的长度=last+1
2.2.3算法实现 定义了存储结构后,就可以讨论运算实现。 插入操作 删除操作 查找操作
1. 插入操作 插入运算的逻辑描述 线性表的插入运算是指在表的第i(1≤i≤n+1)个位置上,插入一个新结点x,使长度为n的线性表: (a1,…,ai-1,ai,…an)变成长度为n+1的线性表: (a1,…,ai-1,e,ai,…an)要考虑两种极端情况: ① 当线性表已满时,怎么办? ② 当插入位置不正确怎么办?
插入过程说明 在顺序表中,结点的物理顺序必须和结点的逻辑顺序保持一致,因此必须将表中位置为n ,n-1,…,i上的结点,依次后移到位置n+1,n,…,i+1上,空出第i个位置,然后在该位置上插入新结点x。仅当插入位置i=n+1时,才无须移动结点,直接将e插入表的末尾。
插入算法 #define OK 1 #define ERROR 0 void InsertList(SeqList *L,ElemType e,int i) {//将新结点 x插入L所指的顺序表的第i个结点的位置 int j; if (i<1||i>L->last+2) { printf(“position error”);//非法位置,退出运行 return ERROR; } if (L->last>=MaxSize) { printf(“Over flow\n”); return ERROR; }
for(k=L->last;k>=i-1;k--) L->elem[k+1]=L->elem[k]; //结点后移L->elem[i-1]=e; //插入xL->last++; //表长加1return OK; }//插入算法结束
算法性能分析 最好情况,插入到表的最后,不需要移动元素,可以直接插入e。 最坏情况,插入到第1个位置,需要移动n次。 一般情况,移动次数与插入位置i有关。
平均情况 设n+1种等几率情况。
插入示例 假设在i=3位置插入一个35
算法分析 现把一个数x插入到第i个位置。假设表里已有n个结点。
分析 移动结点的次数由表长n和插入位置i决定 算法的时间主要花费在for循环中的结点后移语句上。该语句的执行次数是n-i+1。 当i=n+1:移动结点次数为0,即算法在最好时间复杂度是0(1)。 当i=1:移动结点次数为n,即算法在最坏情况下时间复杂度是0(n)。 那么平均来说时间复杂度为多少呢?
时间复杂度的推导过程 插入位置有1到n+1,假设机率均等,每个位置的可能性为1/(n+1)。
平均时间复杂度 EIS(n)=n/2 表长的一半。 所以算法的平均时间复度为O(n)。即与表中已有元素的个数 成正比。
2. 删除操作 删除操作的逻辑说明 删除操作的算法实现 删除操作的性能分析
删除运算的逻辑描述 线性表的删除运算是指将表的第i(1≤i≤n)个结点删去,使长度为n的线性表 (a1,…,ai-1,ai,ai+1,…,an) 变成长度为n-1的线性表 (a1,…,ai-1,ai+1,…,an) 注意:当要删除元素的位置i不在表长范围(即i<1或i>L->length)时,为非法位置,不能做正常的删除操作
顺序表删除操作过程 后面的元素向前移动过程
删除算法 int DelList(SeqList *L,int i, ElemType *e) { //从L所指的顺序表中删除第i个结点aiint k; if(i<1||i>L->last+1) { printf(“删除位置不存在!”); return ERROR; } *e= L->elem[i-1]; for(k=i;k<=L->last;j++) L->elem[k-1]=L->elem[k]; //结点前移L->last--; //表长减小 return OK; }
时间复杂度 Ede(n)=(n-1)/2 所以平均时间复杂度为O(n)
3. 查找操作 按序号查找 GetData(L,i) return L.elem[i-1]; 按内容查找 Locate(L,e)
Locate(L,e) int Locate(SeqList L,ElemType e) { int i=0; while((i<=L.last) && (L.elem[i]!=e)) i++; if(i<=L.last) return i+1; else return -1; }
时间复杂度 O(n)