640 likes | 762 Views
第 2 章 线 性 表. 2 . 1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示及相加. 2.1 线性表的类型定义. 线性表: 是 n 个类型相同的数据元素的有限序列,数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继 。. 例 1 英文字母表( A , B , C , … , Z) 是一个线性表 例 2. 数据元素. 线性表的定义. 线性表 是由 n (n≥0) 个类型相同的数据元素 a 1 ,a 2 , … , a n 组成的有限序列,记做
E N D
第 2 章 线 性 表 2.1 线性表的类型定义 2.2 线性表的顺序表示和实现 2.3 线性表的链式表示和实现 2.4 一元多项式的表示及相加
2.1 线性表的类型定义 线性表:是n个类型相同的数据元素的有限序列,数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继 。
例1 英文字母表(A,B,C,…,Z)是一个线性表 例2 数据元素
线性表的定义 线性表是由n (n≥0)个类型相同的数据元素a1,a2,…,an组成的有限序列,记做 (a1,a2,…,ai-1,ai,ai+1, …,an)。 n为线性表的长度,n=0时称为空表. 线性表的特点 同一性:线性表由同类数据元素组成,每一个ai必须属于同一数据对象。 有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。n=0时称为空表。 有序性:线性表中相邻数据元素之间存在着序偶关系<ai,ai+1>。
线性表的抽象数据类型定义 ADT List{ 数据元素:D={ai| ai∈D0, i=1,2,…,n , n≥0 ,D0为某一数据对象} 关系:R1={<ai-1,ai> | ai, ai-1∈D,i=2, …,n} 基本操作: (1)InitList(&L) 操作前提:L为未初始化线性表。 操作结果:将L初始化为空表。 (2)DestroyList(&L) 操作前提:线性表L已存在。 操作结果:将L销毁。
(3)ClearList(&L) 操作前提:线性表L已存在 。 操作结果:将表L置为空表。 (4)EmptyList(L) 操作前提:线性表L已存在。 操作结果:如果L为空表则返回真,否则返回假。 (5)ListLength(L) 操作前提:线性表L已存在。 操作结果:如果L为空表则返回0,否则返回表中的元素个数。 (6)Locate(L,e) 操作前提: 表L已存在,e为合法元素值。 操作结果: 如果L中存在元素e,则将“当前指针”指向元素e所在位置并返回真,否则返回假。
(7)GetData(L,i) 操作前提:表L存在,且i值合法,即1≤i≤ListLength(L)) 操作结果:返回线性表L中第i个元素的值。 (8)ListInsert (&L,i,e) 操作前提:表L已存在,e为合法元素值且1≤i≤ListLength(L)+1。 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1。 (9)ListDelete(&L,i,&e) 操作前提:表L已存在且非空, 1≤i≤ListLength(L)。 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。 }ADT List
2.2.1 线性表的顺序存储结构 线性表的顺序存储结构(顺序映像): 用一组地址连续的存储单元依次存储线性表的数据元素。 顺序表: 采用顺序存储结构的线性表通常称为顺序表
存储地址 内存 元素序号 a1 LOC(a1) 1 LOC(a1) +L a2 2 n LOC(a1) +(n-1)L an 备用空间 • 元素地址计算方法: • LOC(ai)=LOC(a1)+(i-1)*L • LOC(ai+1)=LOC(ai)+L • 其中: • L—一个元素占用的存储单元个数 • LOC(ai)—线性表第i个元素的地址 • 特点: • 实现逻辑上相邻—物理地址相邻 • 实现随机存取
顺序存储结构的实现 可以借助于高级程序设计语言中的数组来表示,一维数组的下标与元素在线性表中的序号相对应。
以C语言为例 : // 线性表的顺序存储结构 #define LIST_INIT_SIZE 100 /*存储空间的初始分配量*/ typedef struct { ElemType elem [LIST_INIT_SIZE]; //存储空间基址 int length; //当前长度 }SqList;
2.2.2 线性表顺序存储结构上的运算 • 查找 • 插入 • 删除 • 合并
顺序表的查找运算 线性表有两种基本的查找运算。 按序号查找GetData(L,i): 要求查找线性表L中第i个数据元素,其结果是L.elem[i-1]或L->elem[i-1]。 按内容查找Locate(L,e):要求查找线性表L中与给定值e相等的数据元素,其结果是:若在表L中找到与e相等的元素,则返回该元素在表中的序号;若找不到,则返回一个“空序号”,如-1。
顺序表的查找运算 算法分析:查找运算可采用顺序查找法实现,即从第一个元素开始,依次将表中元素与e相比较,若相等,则查找成功,返回该元素在表中的序号;若e与表中的所有元素都不相等,则查找失败,返回“-1”。
算法实现: int Locate(SqList L,ElemType e) { i=0 ; //i为计数器,从第一个元素开始比较 while ((i<=L.length-1)&&(L.elem[i]!=e)) /*顺序扫描表,直到找到值为e的元素,或扫描到表尾而没找到*/ i++; if (i<=L.length-1) return(i+1); /*若找到值为e的元素,则返 回其序号*/ else return(-1); /*若没找到,则返回空序号*/ }
顺序表的插入运算 定义: 在第i(1i n+1)个元素前插入一个新的数据元素e,使长度为n的线性表 (a1,a2,……,ai-1,ai,……,an) 变成长度为n+1的线性表 (a1,a2,……,ai-1,e,ai,……,an) 算法分析:
数组下标 数组下标 内存 元素序号 元素序号 内存 0 0 a1 a1 1 1 a2 2 2 a2 1 1 插入e i-1 i-1 ai i i ai i i ai+1 i+1 i+1 ai+1 n n an an-1 n-1 n-1 n+1 n+1 n n an e
算法实现: Status ListInsert_Sq(Sqlist &L,int i,ElemType e){ if(i<1||i>L.length+1) return ERROR; if(L.length>=LIST_INIT_SIZE){ //当前存储已满 return ERROR ;} q=&(L.elem[i-1]); // q为插入位置 for(p=&(L.elem[L.length-1]);p>=q;- -p) *(p+1)=*p; //插入位置之后元素后移 *q=e; ++L.length; return OK;}
算法实现2: Status ListInsert_Sq(Sqlist &L,int i,ElemType e){ if(i<1||i>L.length+1) return ERROR; if(L.length>=LIST_INIT_SIZE){ //当前存储已满 return ERROR ;} for(k=L.length-1;k>=i-1;- -k) L.elem[k+1]=L. elem[k]; //插入位置之后元素后移 L.elem[i-1]=e; ++L.length; return OK;}
算法时间复杂度T(n) 设Pi是在第i个元素之前插入一个元素的概率,则在长度为n的线性表中插入一个元素时,所需移动的元素次数的期望值(平均次数)为:
顺序表的删除运算 • 定义: 线性表的删除是指将第i(1i n)个元素删除,使长度为n的线性表 (a1,a2,……,ai-1,ai,ai+1,……,an) 变成长度为n-1的线性表 (a1,a2,……,ai-1, ai+1, ……,an) • 算法分析: 需将第i+1至第n共(n-i)个元素前移
数组下标 内存 元素序号 数组下标 内存 元素序号 0 a1 1 a1 0 1 a2 2 a2 2 1 1 ai-1 ai-1 i-2 i-1 i-2 i-1 删除ai i-1 ai i i-1 ai+1 i i ai+1 i+1 i ai+2 i+1 an n-1 n-2 n an n-1 n-1 n n+1 n
算法实现: Status ListDelete_Sq(Sqlist &L,int i,ElemType &e){ if(i<1||i>L.length) return ERROR; p=&(L.elem[i-1]); // p为被删除元素的位置 e=*p; // 被删除元素的值赋给e q=&L.elem[L.length-1]; // 表尾元素的位置 for(++p;p<=q;++p) *(p-1)=*p; // 被删除元素之后的元素左移 - -L.length; // 表长减1 return OK; }
算法实现2: Status ListDelete_Sq(Sqlist &L,int i,ElemType &e){ if(i<1||i>L.length) return ERROR; e= L.elem[i-1]; // 被删除元素的值赋给e for(k=i;k<=L.length-1;++k) L.elem[k-1]=L.elem[k]; // 被删除元素之后的元素左移 - -L.length; // 表长减1 return OK; }
算法时间复杂度 设Qi是删除第i个元素的概率,则在长度为n的线性表中删除一个元素所需移动的元素次数的(期望值)平均次数为:
算法评价: • 在顺序表中插入或删除一个元素时,平均移动表的一半元素,当n很大时,效率很低。
例:顺序表的合并.有两个顺序表LA和LB,其元素均为非递减有序排列,编写一个算法,将它们合并成一个顺序表LC,要求LC也是非递减有序排列。例如LA=(2, 2, 3), LB=(1, 3, 3, 4), 则LC=(1, 2, 2, 3, 3, 3, 4)。 算法分析:设表LC是一个空表,为使LC也是非递减有序排列,可设两个指针i、j分别指向表LA和LB中的元素,若LA.elem[i]>LB.elem[j],则当前先将LB.elem[j]插入到表LC中,若LA.elem[i]≤LB.elem[j] ,当前先将LA.elem[i]插入到表LC中,如此进行下去,直到其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。
算法模拟 LA LB LC 下标 值 下标 值 下标 值 i 0 j k 2 2 0 1 1 0 k i 2 2 j 1 1 1 3 3 k i j 2 2 2 3 3 3 3 j k i 3 4 4 3 k 4 j k 5 6 k k
算法实现: void merge(SqList LA, SqList LB, SqList &LC) { i=0;j=0;k=0; while(i<=LA.length-1&&j<=LB.length-1) if(LA.elem[i]<=LB.elem[j]) { LC.elem[k]= LA.elem[i]; i++; k++; } else { LC.elem[k]=LB.elem[j]; j++; k++; } while(i<=LA.length-1) /*当表LA有剩余元素时,则将表LA余下的元素赋给表LC*/ { LC.elem[k]= LA.elem[i]; i++; k++; } while(j<=LB.length-1) /*当表LB有剩余元素时,则将表LB余下的元素赋给表LC*/ { LC.elem[k]= LB.elem[j]; j++; k++; } LC.length=LA.length+LB.length; }
顺序存储结构的优缺点: 优点 逻辑相邻,物理相邻 可随机存取任一元素 缺点 插入、删除操作需要移动大量的元素
结点 数据域 指针域 2.3 线性表的链式存储结构 特点: • 用一组任意的存储单元存储线性表的数据元素 • 利用指针实现了用不相邻的存储单元存放逻辑上相邻的元素 • 每个数据元素ai,除存储本身信息外,还需存储其直接后继的信息 • 结点 • 数据域:元素本身信息 • 指针域:指示直接后继的存储位置
H 存储地址 数据域 指针域 LI ZHAO SUN QIAN 1 LI 7 QIAN 13 WANG ZHOU ZHENG WU ^ SUN 19 H WANG 25 31 WU 31 ZHAO 37 ZHENG 43 ZHOU 例 线性表 (ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG) 43 头指针 13 1 NULL 37 7 19 25
next data p 结点(*p) 2.3.1 线性链表 • 定义:结点中只含一个指针域的链表叫~,也叫单链表 typedef struct LNode { ElemTypedata; struct LNode *next; }LNode,*LinkList; • 实现 LinkList L,p; (*p)表示p所指向的结点 (*p).datap->data表示p指向结点的数据域 (*p).nextp->next表示p指向结点的指针域 生成一个LNode型新结点:p=(LinkList)malloc(sizeof(LNode)); 系统回收p结点:free(p)
头结点:在单链表第一个结点前附设一个结点叫头结点.头结点指针域为空表示线性表为空头结点:在单链表第一个结点前附设一个结点叫头结点.头结点指针域为空表示线性表为空 头结点 p …... L an ^ a1 a2 L ^ 空表 第一个数据元素的值:L->next->data 第二个数据元素的指针:L->next->next p->data p->next p->next->data p->next->next
单链表的基本运算 1.头插法建立单链表 2.尾插法建立单链表 3.查找 4.插入 5.删除
p L a b p 1.头插法建立单链表 算法描述: 逆位序输入n个元素的值,建立带头结点的单链表 算法演示:如输入二个元素a,b,头插法建立单链表 ^ ^
1.头插法建立单链表 算法实现: void CreateList_L(LinkList &L,int n){ L=(LinkList)malloc(sizeof(LNode)); L->next=NULL;//先建立一个带头结点的单链表 for(i=n;i>0;--i){ p=(LinkList)malloc(sizeof(LNode));//生成新结点 scanf(&p->data);//输入元素值 p->next=L->next;L->next=p;//插入到表头 } }
r r L p 2.尾插法建立单链表 算法描述: 正位序输入n个元素的值,建立带头结点的单链表 算法演示:如输入二个元素a,b,尾插法建立单链表 ^ b a ^
2.尾插法建立单链表 算法实现: void CreateList_L2(LinkList &L,int n){ L=(LinkList)malloc(sizeof(LNode)); L->next=NULL;//先建立一个带头结点的单链表 r=L;//r为表尾元素指针 for(i=n;i>0;--i){ p=(LinkList)malloc(sizeof(LNode));//生成新结点 scanf(&p->data);//输入元素值 p->next=NULL;r->next=p;r=p; //插入到表尾 } }
3.查找 • 按序号查找 • 按值查找
按序号查找 算法描述:设带头结点的单链表的头指针为L,要查找表中第i个结点,则需从单链表的头指针L出发,从头结点开始,顺着链域扫描.用指针p指向当前扫描到的结点,初值指向第一个结点(p=L->next).用j做计数器,累计当前扫描过的结点数(初值为1),当j=i时,指针p所指的结点就是要找的第i个结点.
p a b m n i 算法演示 L Status GetElem_L(LinkList L,int i,ElemType &e) {p=L->next;j=1; while(p&&j<i){ p=p->next;++j;} if(!p||j>i) return ERROR; e=p->data; return OK;} …… …… ^
p a b e n 算法演示 L LinkList Locate_L(LinkList L,ElemType e)} p=L->next; while(p){ if(p->data==e) break; else p=p->next;} return p;} …… …… ^
b p a x s 4.插入 插入:在线性表两个数据元素a和b间插入x,已知p指向a p->next=s; s->next=p->next; 算法评价
算法实现 Status ListInsert_L(ListList &L,int I,ElemType e){ //在带头结点的单链表L中第i个位置之前插入元素e p=L;j=0; while(p&&j<i-1){p=p->next;++j;}//寻找第i-1个结点 if(!p||j>i-1) return ERROR; s=(LinkList)malloc(sizeof(LNode));//生成新结点 s->data=e;s->next=p->next; p->next=s; return OK; }
p b a c q 5.删除 • 删除:单链表中删除b,设p指向a p->next=q->next; 算法评价
算法实现 Status ListDelete_L(ListList &L,int I,ElemType &e){//在带头结点的单链表L中删除第i个元素,由//e返回其值 p=L;j=0; while(p->next&&j<i-1){p=p->next;++j;}//寻找第i-1个结点 if(!(p->next)||j>i-1) return ERROR; q=p->next;p->next=q->next; e=q->data;free(q); return OK; }