760 likes | 873 Views
第 2 章 线 性 表. 2 . 1 线性表的定义和运算 2.2 线性表的顺序存储结构 2.3 链表 2.4 一元多项式的表示及相加. 2.1 线性表的定义和运算. 2.1.1 线性表的定义 2.1.2 线性表的运算. Return. 2.1.1 线性表的定义. 线性表: 是 n 个类型相同的数据元素的有限序列,数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继 。. 例 1 英文字母表( A , B , C , … , Z) 是一个线性表 例 2. 数据元素. 线性表的定义.
E N D
第 2 章 线 性 表 • 2.1 线性表的定义和运算 • 2.2 线性表的顺序存储结构 • 2.3 链表 • 2.4 一元多项式的表示及相加
2.1 线性表的定义和运算 2.1.1 线性表的定义 2.1.2 线性表的运算 Return
2.1.1 线性表的定义 线性表:是n个类型相同的数据元素的有限序列,数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继 。
例1 英文字母表(A,B,C,…,Z)是一个线性表 例2 数据元素
线性表的定义 线性表是由n (n≥0)个类型相同的数据元素a1,a2,…,an组成的有限序列,记做L=(a1,a2,…,ai-1,ai,ai+1, …,an)。 线性表的特点 同一性:线性表由同类数据元素组成,每一个ai必须属于同一数据对象。 有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数(即n)。n=0时称为空表,记作L=()。 有序性:线性表中表中相邻数据元素之间存在着序偶关系<ai,ai+1>。
2.1.2线性表的运算 基本运算: (1)initial_list(L) 操作前提:L为未初始化线性表。 操作结果:将L初始化为空表。 (2)list_length(L) 操作前提:线性表L已存在。 操作结果:如果L为空表则返回0,否则返回表中的元素个数。
(3)get_element(L,i,&x) 操作前提:表L存在,且i值合法,即1≤i≤list_length(L)) 操作结果:用变量x返回线性表L中第i个元素的值。 (4)list_locate(L,x) 操作前提: 表L已存在,x为合法元素值。 操作结果: 如果L中存在元素x,则返回元素x所在位置,否则返回一个不存在的地址或标记。
(5)list_insert (L,i,x) 操作前提:表L已存在,x为合法元素值且1≤i≤list_length(L)+1。 操作结果:在L中第i个位置之前插入新的数据元素x,L的长度加1。 (6)list_delete(L,i) 操作前提:表L已存在且非空, 1≤i≤list_length(L)。 操作结果:删除L的第i个数据元素,L的长度减1。
2.2 线性表的顺序表存储结构 2.2.1 顺序存储结构 2.2.2 顺序表基本运算的实现 Return
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 maxlen 100 //存储空间的初始分配量 typedef struct { elementtype data[maxlen]; //定义顺序表中元素的数组 int listlen; //定义表长度 }seqlist; seqlist *L; seqlist L; L.listlen; L.data[0] L->listlen; L->data[0]
2.2.2 顺序表运算的实现 • 表初始化 • 求表长度 • 按序号求元素 • 查找元素 • 插入元素 • 删除元素
表初始化initial_list(L) void initial_list(seqlist *L) {L->listlen=0;}
求表长度list_length(L) int list_length(seqlist L) {return L.listlen;}
按序号求元素get_element(L,i,elementtype &x) void get_element(seqlist L,int i,elementtype &x) { if (i<1||i>L.listlen) error("超出范围"); else x=L.data[i-1] }
查找运算list_locate(L,x) int list_locate(seqlist L,elementtype x) {//返回元素x在顺序表L中的序号 int i; for( i=0 ; i<L.listlen ; i++) if (L.data[i]==x) return i+1; return 0; }
插入运算list_insert(L,i,x) 定义: 在第i(1i n+1)个元素前插入一个新的数据元素x,使长度为n的线性表 (a1,a2,……,ai-1,ai,……,an) 变成长度为n+1的线性表 (a1,a2,……,ai-1,x,ai,……,an) 算法分析:
数组下标 数组下标 内存 元素序号 元素序号 内存 0 0 a1 a1 1 1 a2 2 2 a2 1 1 插入x 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 x
算法实现: void list_insert (seqlist L,int i,elementtype x){ int j; if(i<1||i>L.listlen+1) error("position error"); if(L.listlen>=maxlen) error ("overflow");} for(j=L.listlen-1;j>=i-1;j--) L.data[j+1]=L.data[j]; //插入位置之后元素后移 L.data[i-1 ]=x; ++L.listlen; }
算法时间复杂度T(n) 设Pi是在第i个元素之前插入一个元素的概率,则在长度为n的线性表中插入一个元素时,所需移动的元素次数的期望值(平均次数)为:
顺序表的删除运算list_delete(L,i) • 定义: 线性表的删除是指将第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
算法实现: void list_delete(seqlist L,int i){ int k; if(i<1||i>L.listlen) error("删除位置错"); for(k=i;k<=L.listlen-1;++k) L.elem[k-1]=L.elem[k]; // 被删除元素之后的元素左移 - -L.listlen; // 表长减1 }
算法时间复杂度 设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.data[i]>LB.data[j],则当前先将LB.data[j]插入到表LC中,若LA.data[i]≤LB.data[j] ,当前先将LA.data[i]插入到表LC中,如此进行下去,直到其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。
例如LA=(2, 2, 3), LB=(1, 3, 3, 4), 则LC=(1, 2, 2, 3, 3, 3, 4)。 LA i LB j j 1 2 2 3 3 3 4 LC k k
算法实现: void merge(seqlist &LA, seqlist &LB, seqlist &LC) { i=0;j=0;k=0; while(i<=LA.listlen-1&&j<=LB.listlen-1) if(LA.data[i]<=LB.data[j]) { LC.data[k]= LA.data[i]; i++; k++; } else { LC.data[k]=LB.data[j]; j++; k++; } while(i<=LA->listlen-1) /*当表LA有剩余元素时,则将表LA余下的元素赋给表LC*/ { LC.data[k]= LA.data[i]; i++; k++; } while(j<=LB.listlen-1) /*当表LB有剩余元素时,则将表LB余下的元素赋给表LC*/ { LC.data[k]= LB.data[j]; j++; k++; } LC.listlen=LA.listlen+LB.listlen; }
顺序存储结构的优缺点: 优点 逻辑相邻,物理相邻 可随机存取任一元素 缺点 插入、删除操作需要移动大量的元素
第 2 章 线 性 表 • 2.1 线性表的定义和运算 • 2.2 线性表的顺序存储结构 • 2.3 链表 • 2.4 一元多项式的表示及相加
结点 数据域 指针域 2.3 链表 Return 特点: • 用一组任意的存储单元存储线性表的数据元素 • 利用指针实现了用不相邻的存储单元存放逻辑上相邻的元素 • 每个数据元素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 node{ elementtypedata; struct node *next; }node,*LinkList; 实现 node letter[8]; 用数组来存储元素的值和地址。 数据元素的个数固定不变,为 静态链表。 node *L,*p; (*p)表示p所指向的结点 (*p).datap->data表示p指向结点的数据域 (*p).nextp->next表示p指向结点的指针域 动态链表 生成一个node型新结点: p=(node *)malloc(sizeof(node)); 系统回收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.插入 6.删除 7.头插法建立单链表 8.尾插法建立单链表
L 1.初始化单链表 ^ //产生带头结点的单链表 void initial_list(node *&L) { L =(node *)malloc(sizeof(node)); L->next=NULL; }
p 头结点 L …... an ^ a1 a2 2.求链表长度 int list_length(node *L) { int n=0; node *p=L->next; while(p!=NULL) { n++; p=p->next; } return n; }
p 头结点 L …... an ^ a1 a2 3.按序号取元素 node *get_element(node *L , int i) { node *p=L->next; int j=1; while( j!=i && p!=NULL) { p=p->next; j++; } return p; }
p a b e n 算法演示 L node *list_locate(node *L;elementtype e) { node *p=L->next; while(p!=Null && p->data!=e) { p=p->next; } return p; } …… …… ^
b p a x s 5.插入 插入:在线性表两个数据元素a和b间插入x,已知p指向a p->next=s; s->next=p->next; 算法评价
算法实现 void list_insert(node *L, int i, elementtyp x){ //在带头结点的单链表L中第i个位置之前插入元素x node *s, p=*L ; int j=0; while(p&&j<i-1){p=p->next;++j;}//寻找第i-1个结点 if (!p | |j>i-1) error("序号错"); s=(node *)malloc(sizeof(node));//生成新结点 s->data=x; s->next=p->next; p->next=s; }
p b a c q 6.删除 • 删除:单链表中删除b,设p指向a p->next=q->next; • 算法评价
算法实现 void list_delete(node *L, int i) {//在带头结点的单链表L中删除第i个元素 node *p=L,*q; int j=0; while (p->next && j<i-1) {p=p->next; ++j; } //寻找第i-1个结点 if( !(p->next) || j>i-1) error("删除序号错"); q=p->next; p->next=q->next; free (q); }
p L a b p 7.头插法建立单链表 算法描述: 逆位序输入n个元素的值,建立带头结点的单链表 算法演示:如输入二个元素a,b,头插法建立单链表 ^ ^
头插法建立单链表算法实现 void createlist_H(node * &L) { node *p; elementtype x; L=(node *)malloc(sizeof(node)); L->next=NULL;//先建立一个带头结点的单链表 cin >>x ; //输入第一个元素的值 while ( x!=End_of_Num) //x不是结束符时 {p=(node *)malloc(sizeof(node)); //生成新结点 p->data=x; p->next=L->next; L->next=p; //插入到表头 cin >> x ; //输入下一个元素的值} }
r r L p 8.尾插法建立单链表 算法描述: 正位序输入n个元素的值,建立带头结点的单链表 算法演示:如输入二个元素a,b,尾插法建立单链表 ^ b a ^
尾插法建立单链表算法实现: void createlist_R( node * &L) { node *r,*p; elementtype x ; L=(node *)malloc(sizeof(node)); L->next=NULL; //先建立一个带头结点的单链表 r=L; //r为表尾元素指针 cin>> x; while( x!=End_of_Num) { p=(node *)malloc(sizeof(node)); //生成新结点 p->data=x; p->next=NULL; r->next=p; r=p; //插入到表尾 cin>>x; }}