1、2018/10/14,通信工程学院,第二章 线性表,1、线性表的定义、逻辑结构及基本运算 2、线性表的顺序存储结构及基本运算 3、线性表的链式存储结构及基本运算 4、数组的定义及其存储方式 5、线性表的应用示例,内容提要,2018/10/14,通信工程学院,线性表:由n(n0)个a1,a2,an组成的有限序列。其逻辑结构是线性结构。,(1)有且仅有一个开始节点(该节点只有一个后继节点,没有前趋节点),(2)有且仅有一个结束节点(该节点只有一个前趋节点,没有后继节点),(3)其余节点有且仅有一个前趋和一个后继节点,2018/10/14,通信工程学院,线性表常用的的运算,初始化线性表 判表是否为空
2、 求线性表的长度 读取线性表中第i个元素 查找满足给定条件的数据元素 在线性表的第i个位置之前插入一个新的数据元素,2018/10/14,通信工程学院,删除线性表中的第i个数据元素 表置空 查找表中第i个元素的前驱 查找表中第i个元素的后继 按一个或多个数据项值的递增或递减次序重新排列线性表中的数据元素,线性表上常用的运算,2018/10/14,通信工程学院,在逻辑上相邻的数据元素,如果它们的物理位置也是邻接的。即线性关系利用物理上的相邻关系来体现,叫做顺序表。,顺序存储结构的特点,2018/10/14,通信工程学院,顺序存储的线性表,线性表的顺序分配是指:用一组连续编号的存储单元依次存放各个
3、数据元素。根据一个数据元素占据m个存储单元,有:LOC(ai)=LOC(a1)+(i-1)m用一维数组来实现线性表顺序存储结构 数组元素: a0、a1、a2、a3线性表元素:a1、a2、 a3、 a4,2018/10/14,通信工程学院,#define Maxsize maxlen /*maxlen表示线性表可能的最大数据元素数目*/typedef int elemtype; /*elemtype表示数据元素类型,此处定义为int*/typedef structelemtype vMaxsize;/*存放线性表元素的数组,关系隐含*/int len; /*表示线性表的长度*/sqlist; /
4、* sqlist是数据类型,此处表示线性表的顺序存储结构*/,顺序表的类型定义,2018/10/14,通信工程学院,例 typedef struct card int num;char name20;char author10;char publisher30;float price; DATATYPE; DATATYPE libraryM;,数据元素不是简单类型时,可定义结构体数组,顺序表的图示,2018/10/14,通信工程学院,顺序表的基本运算,一.求线性表的长度(算法 2.1 )int lenth(sqlist *L) int lenth; lenth=(*L).len; return
5、(lenth); /*返回线性表的长度*/ ,2018/10/14,通信工程学院,二、插入运算(算法 2.2) 线性表的插入运算是指在线性表的第i个数据元素之前插入一个新的数据元素,使长度为n的线性表 (a1, , ai-1, ai, , an) 变为长度为n1的线性表 (a1, , ai-1, X ,ai, , an),a1,a2,ai-1,ai,an,a1,a2,ai-1,new node,ai+1,顺序表的基本运算,2018/10/14,通信工程学院,插入运算,从ai开始,到an开始向后逐个移动从an开始,向前的每个元素向后移一格,a1,a2,ai-1,ai,ai+1,an,ai,ai,
6、ai,ai,a1,a2,ai-1,ai,ai+1,an,an,ai+1,ai,for( j = i; j =list.n ; j+) list.a j+1 = list.a j ; ,for( j = list.n; j = i; j-)list.a j+1 = list.a j ; ,new node,2018/10/14,通信工程学院,插入运算,搬动节点,腾空位置,在腾空的位置处,放入新的元素,数组长度因为增加了一个新元素而加一,void insert( table, new_node, location),;补充:(考虑需要补充的情况)a.位置已满,放不进去新元素b.元素放入位置不正确(
7、in),2018/10/14,通信工程学院,(1)判断线性表的存储空间是否已满,若已满,则进行“溢出”处理。 (2)检查i值是否超出所允许的范围(1in+1),若超出,则进行“超出”处理。 (3)将线性表的第i个元素和它后面的所有元素均后移一个位置。 (4)将新的数据元素写入到空出的下标为i-1的位置上。 (5)线性表的长度增加1,顺序表的插入算法,2018/10/14,通信工程学院,插入运算程序,void insert (list, x, i) ,for(j = list.n-1 ; j = i-1 ; j-)list.a j+1 = list.a j ; ,list.a i-1 = x;,
8、list.n = list.n +1;,if (list. n+1 m)error(1); else,if (i list.n )error(2); else,2018/10/14,通信工程学院,出错处理,void error(int number)switch(number)case 1:printf(“the list is full”);break;case 2:printf(“cant insert at i”);break;default:printf(“unknown error”); ,2018/10/14,通信工程学院,插入算法的算法效率,假设在第i个元素之前插入一个元素的概率
9、为Pi,所需移动数据元素的平均次数为:,2018/10/14,通信工程学院,an,ai,删除运算,三、删除运算(将第i个数据元素删去),a1,a2,ai,an,ai1,a1,a2,ai1,ai+1,2018/10/14,通信工程学院,for( j = i; j list.n; j+)list.a j = list.a j1 ; ,删除运算,从an开始向前逐个元素向前移动从ai1开始向后逐个元素向前移动,a1,a2,ai+1,ai,an,ai-1,an,an,an,for( j = list.n; j i; j- -)list.a j = list.a j1 ; ,a1,a2,ai-1,ai,a
10、i+1,an,2018/10/14,通信工程学院,删除运算,从ai1节点开始向后,逐个向前搬动节点, 挤掉第i个元素,2、数组长度因为减少了一个新元素而减一,void delete( list, i , y ),补充算法:a.删除了空表b.删除位置i不正确,2018/10/14,通信工程学院,算法思路: (1)判断顺序表是否为空 (2)判断i值是否超出所允许的范围(1in),若是,则进行“超出范围”处理; (3)把第i个元素赋给y ;把第i个元素后的所有元素依次向前移动一个位置; (4)线性表长度减1。,顺序表的删除算法,2018/10/14,通信工程学院,void delete( list,
11、 i , y ),for(j = i ; j list . n ; j+)list . a j = list . a j+1 ; ,list . n = list . n -1;,if( list .n 1)error(3); else,if( i list .n )error(4); else,2018/10/14,通信工程学院,出错处理,void error(int number)switch(number)case 3:printf(“the list is empty”);break;default:printf(“the position is wrong”); ,2018/10/1
12、4,通信工程学院,删除算法的算法效率,假设删除第i个元素的概率为qi,所需移动数据元素的平均次数为:,2018/10/14,通信工程学院,四查找(算法2.4)线性表的查找是指找出数据元素x在表中的位序号,若vi=x,则算法返回值为i+1,若不存在数据元素x则返回0,顺序表上的基本运算,2018/10/14,通信工程学院,顺序存储的线性表,数组顺序存储结构的特点 任意元素获取容易实现,定位简单 存取时间短,存取时间与元素位置无关 元素更动(比如:插入和删除)时的搬移性 需要移动大量的节点,平均N/2次的搬移 线性表的大小在声明数组时固定,无法更改。 因此引入链表形式的存储结构:(离散存放,用指针
13、来表示元素之间的关系),2018/10/14,通信工程学院,链式存储的线性表,分配一些任意的存储单元来存储线性表中的数据元素,这些单元可以是连续的,也可以是离散的。这样的存储单元(称为结点)包括两个域:数据域和指针域。,数据域,指针域,2018/10/14,通信工程学院,链式存储的线性表,Head,头节点 头指针,链表尾,链表长度(节点数目),所以,由链点及链点相互间的链接构成链表。 如果每个链点只包含一个指针域,则此链表为线性链表或单链表。,2018/10/14,通信工程学院,例:线性表(ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG),43,13,1,NULL,37
14、,7,19,25,头指针,2018/10/14,通信工程学院,链表的定义,typedef struct node_typeelemtype data;struct node_type * next; node_type ;,typedef struct list_typenode_type *head;node_type *tail;int length; list_type;,链点的定义,链表的定义,2018/10/14,通信工程学院,单链表使用注意,P,P=(LNode*)malloc(sizeof(LNode); 通过按结点的类型向系统申请建立一个新结点,free(P)释放P所指向的结点
15、空间。,(*P).data P-data,(*P).next P-next,2018/10/14,通信工程学院,链表的基本操作,创建链表 求链表长度 插入链点 删除链点 查找链点,2018/10/14,通信工程学院,链表的创建(算法2.5/2.6),从链点的生成过程中体会动态内存申请的动作.,第一种方法:(头插法) 每次输入的元素插入到链表头,实质是从表的最后一个元素开始,从后往前的生成结点来建立链表。,第二种方法:(尾插法) 每次输入的元素插入到链表尾,实质是从第一个元素开始,从前往后的生成新结点来建立链表的。,2018/10/14,通信工程学院,算法思路 1)首先建立一个头结点,并使头结点
16、的指针域为空; 2)读入值ch; 3)建立一个新结点p; 4)将ch赋给p的数据域; 5)改变指针p的值,使p成为h的直接后继; 6)重复(2)到(5),直到不满足循环条件为止。 见演示部分。,头插法建立单链表算法,2018/10/14,通信工程学院,typedef char elemtype; Lnode *creat() /*表尾插入法,建立单链表*/lnode *h,*p,*t;elemtype x; h=(lnode*)mallod(sizeof(lnode);h-next=NULL; t=h; while (ch=getchar()!=n)p=(lnode*)mallod(sizeo
17、f(lnode);p-data=ch; p-next=NULL; t-next=p;t=p; /*t始终指向最后一个元素*/ return (h);,尾插法建立单链表算法,2018/10/14,通信工程学院,算法思路:1)设置一个工作指针变量p,再设置一个整型变量i作计数器;2)让p指向第一个结点,置i为0;3)指针p在单链表中后移并且i值加1;4)当p=NULL时说明单链表结束,计数完毕,这时i的值正好是表长。,求单链表的长度(算法2.7),2018/10/14,通信工程学院,如何在P结点之后插入值为x的新结点S:,插入算法1, s-next=p-next; p-next=s;,能否对换?,
18、2018/10/14,通信工程学院,(P结点之后插入值为x的结点S ) 核心算法思路: (1)生成一个新结点S; (2)将 x 赋给将新结点 S 的数据域; (3)定位P结点; (4)新结点S链入单链表中P结点之后;,插入算法1,2018/10/14,通信工程学院,(单链表中第i个元素之前插入一个元素x)1、定位:使 P 指向第 号结点2、新产生一个结点,存放元素 x3、插入:在 P 之后插入 S 所指向的新结点,插入算法,?,i-1,2018/10/14,通信工程学院,while( counter next; ,插入操作,void insertl(list, new_node , locat
19、ion),找到ai-1,P-next = S;,S-next =,P,P head-next; counter = 1,P-next;,S,2018/10/14,通信工程学院,删除单链表中p的后继结点q,删除算法,q=p-next; p-next=q-next; free (q);,q,2018/10/14,通信工程学院,找到ai-1,执行删除动作,ai-1 -next= ai -next ;,void delete(list, location),注意,从链表上取下的链点ai需要挂在一个指针上, 否则可能丢失,a1,ai+1,an,head,ai-1,.,.,temp,2018/10/14,
20、通信工程学院,算法思路:1)将q指向p结点的直接后继;2)改变指针链接,把q结点的直接 后继作为p结点的直接后继;3)从单链表中删除q结点;4)释放q结点空间。,删除算法,2018/10/14,通信工程学院,查找单链表中是否存在数据域为x的结点。若有该结点,则返回指向该结点的指针,否则返回空。 算法思路:1)从第一个结点开始扫描整个单链表,逐个比较数据域值和x ;2)找到该结点后返回指向该结点的指针。,按值查找算法(算法2.11),2018/10/14,通信工程学院,p,j=1,p,j=2,读取单链表中的第i个元素。如果找到,则 返回第i个结点的存储地址,否则返回空。 算法思路: 1)p从单链
21、表的第一个结点出发,并定义j=1; 2)在单链表中移动指针p,同时累计j ; 3)通过j的累计查找j=i的结点; 4)重复2)3)直到p为空或p指向第i个元素。,读取元素算法(算法2.12),2018/10/14,通信工程学院,、操作的顺序性 、离散存放,动态结构 不受链表大小限制 不进行链点内容的搬移 查找操作:数组效率优于链表 插入、删除操作:链表效率优于数组,单链表的特点,2018/10/14,通信工程学院,循环链表,循环链表是指在单链表中最后一个结点的链域值不是NULL,而是指向头结点,整个表形成一环。 特点:是从表中任意结点出发均可以找到表中其它的结点。 操作与单链表基本一致,循环的
22、条件不同: 单链表p或p-next=NULL 循环链表p或p-next=H,2018/10/14,通信工程学院,循环链表按值查找算法,Lnode *get( Lnode *h, elemtype x) Lnode *p; p=h-next; while (p!=h ,2018/10/14,通信工程学院,双向链表,每一个结点中有两个指针域,其一指向直接后继,另一指向直接前趋的链表叫双向链表。 双向链表的结点描述为: typedef struct dulnodeelemtype data;struct dulnode *next, *prior;dulnode;,2018/10/14,通信工程学院
23、,双向链表,具有恒等关系:p-prior-next p p-next-proir;,2018/10/14,通信工程学院,在双向循环链表结点p之前插入结点s,双向循环链表的插入算法1,2018/10/14,通信工程学院,可见,改变指针链接的语句有: s-prior=p-prior; p-prior-next=s; s-next=p; p-prior=s; 请思考其他排列顺序是否可行?,2018/10/14,通信工程学院,含义:在双向环形链表的第i个结点之前插入值为x的新结点。如果成功,返回1,否则,返回0。算法思路:1)通过p的移动在双向链表中依次查找第i个元素;2)如果找到,则建立一个新结点s
24、 ;3)将s和p以及p的前驱链接起来,使s的前驱是p原来的前驱,s的后继是p;4)插入成功返回1,否则返回0。,双向循环链表的插入算法(2.14),2018/10/14,通信工程学院,删除双向环形链表的结点p,双向循环链表的删除算法,改变指针链接的语句有:p-prior-next=p-next;p-next-prior=p-prior;,2018/10/14,通信工程学院,含义:删除双向环形链表的第i个结点p。如果成功,返回1;否则,返回0。 算法思路:1)通过p的移动在双向链表中依次查找第i个元素;2)如果找到,改变指针链接,即使p的前驱指向p的后继,p的后继结点的前驱指针指向p原来的前驱;
25、3)释放p;,双向循环链表的删除算法(2.15),2018/10/14,通信工程学院,线性表的顺序存储和链式存储在操作上的比较:,2018/10/14,通信工程学院,本节学习要点,1. 了解顺序表、链表的概念、含义、区别。 2. 熟练掌握这两类存储结构的描述方法。 3. 熟练掌握线性表在顺序存储结构上实现基本操作:查找、插入和删除的算法。 4. 熟练掌握在各种链表结构中实现线性表操作的基本方法,能在实际应用中选用适当的链表结构。 5. 能够从时间和空间复杂度的角度综合比较线性表两种存储结构的不同特点及其适用场合。,2018/10/14,通信工程学院,数组的定义,数组结构可以简单地定义为:若线性
26、表中的数据元素为非结构的简单元素,则称为一维数组,即为向量;若一维数组中的数据元素又是一维数组结构,则称为二维数组;依次类推,若二维数组中的元素又是一个一维数组结构,则称作三维数组。结论:数组是一种特殊的线性表,而数组结构又是线性表结构的扩展。,2018/10/14,通信工程学院,数组,数组是线性表的一个推广。一维数组是一种顺序表结构,多维数组是一种特殊的线性结构。 数组:由(下标,数值)组成的序对的有限集合。二维数组中的每个元素A i, j对应二维空间的一个数值A(i,j),二维数组的每一行、每一列都是一个线性表。 数组的主要操作有: Create 建立 Value 存取元素 Assign
27、修改元素值,2018/10/14,通信工程学院,二维数组,数组A可以看成一个线性表:A=(1,2 ,.,k ) (k=m或n) 其中每一个数据元素i 可以是由第i行元素组成的一维数组,即一个行向量的线性表: i =(ai1 ,ai2 ,.,ain ) 0im 或j是由第j列元素组成的一维数组,即一个列向量的线性表。,2018/10/14,通信工程学院,数组的特点及运算,数组特点 数组结构固定 数据元素同构 数组运算 给定一组下标,存取相应的数据元素 给定一组下标,修改数据元素的值 数组一般不做插入或删除操作,2018/10/14,通信工程学院,次序约定 以行序为主序 以列序为主序,数组的顺序存
28、储结构,2018/10/14,通信工程学院,对称矩阵的压缩存储方法,地址计算方式: LOC(Ai,j)=LOC(A0,0)+ i*(i+1)/2+ j (i=j) 若ij,修改上公式,对换i和j。或者找a24时 ,换找a42。,2018/10/14,通信工程学院,三角矩阵的压缩存储方法,2018/10/14,通信工程学院,Loc(aij)= Loc(a11)+ 3*(i-1)-1+j-i+1=Loc(a11)+2(i-1)+(j-1),对角矩阵的压缩存储方法,2018/10/14,通信工程学院,M由(1,2,12), (1,3,9), (3,1,-3), (3,6,14), (4,3,24),
29、 (5,2,18), (6,1,15), (6,4,-7) 和矩阵维数(6,7)唯一确定,定义:非零元较零元少,且分布没有一定规律的矩阵。压缩存储原则:只存矩阵的行列维数和每个非零元的行列下标及其值。,稀疏矩阵,2018/10/14,通信工程学院,三元组表所需存储单元个数为3(t+1) 其中t为非零元个数,6 7 8,1 2 12,1 3 9,3 1 -3,3 6 14,4 3 24,5 2 18,6 1 15,6 4 -7,ma0.i,ma0.j,ma0.v分别存放 矩阵行列维数和非零元个数,稀疏矩阵的三元组表示法,#define MAX 10 typedef struct int i,j;
30、elemtype v; node; typedef structint mu,nu,tu;node dataMAX; mat;,mu, nu, tu分别存放 矩阵行列维数和非零元个数,2018/10/14,通信工程学院,十字链表的结点类型定义如下: typedef struct nodeint i,j,v;struct node *down,*right;szjd;,稀疏矩阵的十字链表表示法,2018/10/14,通信工程学院,稀疏矩阵的十字链表表示法,2018/10/14,通信工程学院,线性表的应用示例,1、线性表的逆置运算顺序存储结构上的实现(算法2.18)链式存储结构上的实现(算法2.1
31、9)2、链表合并(算法2.20)3、多项式相加(算法2.21),2018/10/14,通信工程学院,线性表的逻辑结构、物理结构、基本运算及运算实现的算法。 顺序表上的插入和删除算法及链表的建立、查找、插入和删除算法。 线性表的典型算法的应用及解决问题的方法。 数组的压缩存储及运算,本章要点,2018/10/14,通信工程学院,线性表,教材P59习题2.3 上机做习题2.5,2018/10/14,通信工程学院,链表部分修改,创建链表并根据菜单选择需要进行的插入、删除操作 void main() creat(); while (k) printf (“1:insert 2:delete 3:end ”);scanf (“%dn”, ,