数据结构耿国华第2章_第1页
数据结构耿国华第2章_第2页
数据结构耿国华第2章_第3页
数据结构耿国华第2章_第4页
数据结构耿国华第2章_第5页
已阅读5页,还剩96页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第2章线性表第2章线性表线性表的概念及运算线性表的顺序存储线性表的链式存储一元多项式的表示及相加第2章线性表图2.1线性表的逻辑结构2.1线性表的概念及运算2.1.1线性表的逻辑结构第2章线性表例如:

英文字母表(A,

B,

…,

Z)就是一个简单的线性表,表中的每一个英文字母是一个数据元素,

每个元素之间存在唯一的顺序关系,如在英文字母表字母B的前面是字母A,

而字母B的后面是字母C。

在较为复杂的线性表中,数据元素(dataelements )

可由若干数据项组成,如学生成绩表中,每个学生及其各科成绩是一个数据元素,它由学号、姓名、各科成绩及平均成绩等数据项(item)

组成,

常被称为一个记录(record)

,含有大量记录的线性表称为文件(file)。数据对象(data

object)是性质相同的数据元素集合。第2章线性表表2-1车辆登记表第2章线性表线性表(Linear

List)是由n

(n≥0)个类型相同的数据元素a1,a2,an组成的有限序列,记作(a1,a2,…,ai-1,ai,ai+1,…,an)。这里的数据元素ai(1≤i≤n)只是一个抽象的符号,其具体含义在不同情况下可以不同,它既可以是原子类型,也可以是结构类型但同一线性表中的数据元素必须属于同一数据对象。此外,线性表中相邻数据元素之间存在着序偶关系,即对于非空的线性表(a1,a2,…,ai-1,ai,ai+1,…,an),表中ai-1

领先于ai,称ai-1

是a直接前驱,而称ai是ai-1的直接后继。除了第一个元素a1外,每个元素ai有且仅有一个被称为其直接前驱的结点ai-1,除了最后一个元素an外,每个元素ai有且仅有一个被称为其直接后继的结点ai+1。线性表中元素的个数n被定义为线性表的长度,n=0时称为空表。第2章线性表线性表的特点可概括如下:同一性:线性表由同类数据元素组成,每一个ai必须属于同一数据对象。有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。有序性:线性表中表中相邻数据元素之间存在着序偶关系<ai,ai+1>。由此可看出,线性表是一种最简单的数据结构,因为数据元素之间是由一前驱一后继的直观有序的关系确定;线性表又是一种最常见的数据结构,因为矩阵、数组、字符串、堆栈、队列等都符合线性条件。第2章线性表2.1.2

线性表的抽象数据类型定义ADT

LinearList{数据元素:D={ai|

ai∈D0,i=1,2,…,n,n≥0,D0为某一数据对象}关系:S={<ai,ai+1>|

ai,ai+1∈D0,i=1,2,…,n-1}基本操作:(1)

InitList(L)操作前提:L为未初始化线性表。操作结果:将L初始化为空表。第2章线性表DestroyList(L)操作前提:线性表L已存在。操作结果:将L销毁。ClearList(L)操作前提:线性表L已存在。操作结果:将表L置为空表。EmptyList(L)操作前提:线性表L已存在。操作结果:如果L为空表则返回真,否则返回假。第2章线性表ListLength(L)操作前提:线性表L已存在。操作结果:如果L为空表则返回0,否则返回表中的元素个数。Locate(L,

e)操作前提:表L已存在,e为合法元素值。操作结果:如果L中存在元素e,则将“当前指针”指向元素e所在位置并返回真,否则返回假。GetData(L,

i)操作前提:表L存在,且i值合法,即1≤i≤ListLength(L)。操作结果:返回线性表L中第i个元素的值。第2章线性表InsList(L,

i,

e)操作前提:表L已存在,e为合法元素值且1≤i≤ListLength(L)+1。操作结果:在L中第i个位置插入新的数据元素e,L的长度加1。DelList(L,

i,

&e)操作前提:表L已存在且非空,1≤i≤ListLength(L)。操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1。}

ADT

LinearList第2章线性表2.2

线性表的顺序存储2.2.1线性表的顺序存储结构线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。采用顺序存储结构的线性表通常称为顺序表。第2章线性表假设线性表中有n个元素,每个元素占k个单元,第一个元素的地址为loc(a1),则可以通过如下公式计算出第i个元素的地址loc(a-i):loc(ai)

=loc(a1)+(i-1)×k其中loc(a-1)称为基地址。第2章线性表图2.2顺序表存储示意图第2章线性表顺序存储结构可以借助于高级程序设计语言中的一堆数组来表示,一维数组的下标与元素在线性表中的序号相对应。线性表的顺序存储结构可用C语言定义如下:#define

MAXSIZE=线性表可能达到的最大长度typedef

struct{ElemType

elem[MAXSIZE];/*线性表占用的数组空间*/int

last;/*记录线性表中最后一个元素在数组elem[

]中的位置(下标值),空表置为-1

*/}

SeqList;第2章线性表2.2.2线性表顺序存储结构上的基本运算1.查找操作线性表有两种基本的查找运算。按序号查找GetData(L,

i):

要求查找线性表L中第i个数据元素,其结果是L.elem

[i-1]或L->elem[i-1]。按内容查找Locate(L,e):要求查找线性表L中与给定值e相等的数据元素,其结果是:若在表L中找到与e相等的元素,则返回该元素在表中的序号;若找不到,则返回一个“空序号”,如-1。查找运算可采用顺序查找法实现,即从第一个元素开始,依次将表中元素与e相比较,若相等,则查找成功,返回该元素在表中

的序号;若e与表中的所有元素都不相等,则查找失败,返回“-1”第2章线性表算法描述:int

Locate(SeqList

L,

ElemType

e)/*在顺序表L中依次存放着线性表中的元素,在表中查找与e相等的元素,若L[i]=e,则找到该元素,并返回i值,若找不到,则返回“-1”*/{i=0;

/*

i为扫描计数器,初值为0,即从第一个元素开始比较*/while

((i<=L.last)&&(L.elem[i]!

=e)

)

/*

顺序扫描表,

直到找到值为key

的元素,i++;

或扫描到表尾而没找到*/if

(i<=L.last)return(i);

/*若找到值为e的元素,则返回其序号*/elsereturn(-1);/*若没找到,则返回空序号*/}第2章线性表【算法2.1线性表的查找运算】2.插入操作线性表的插入运算是指在表的第i

(1≤i≤n+1)个位置,插入一个新元素e,使长度为n的线性表(e1,…,ei-1,ei,…,en)变成长度为n+1的线性表(e1,…,

ei-1,e,ei,…,en)

。用顺序表作为线性表的存储结构时,由于结点的物理顺序必须和结点的逻辑顺序保持一致,因此我们必须将原表中位置n,n-1,…,i上的结点,依次后移到位置n+1,n,…,i+1上,空出第i个位置,然后在该位置上插入新结点e。当i=n+1时,是指在线性表的末尾插入结点,所以无需移动结点,直接将e插入表的末尾即可。第2章线性表例如:已知线性表(4,9,15,28,30,30,42,51,62),需在第4个元素之前插入一个元素“21”,则需要将第9个位置到第4个位置的元素依次后移一个位置,然后将“21”插入到第4个位置,如图2.3所示。请注意区分元素的序号和数组的下标。图2.3顺序表中插入元素第2章线性表算法实现:#define

OK

1#define

ERROR

0int

InsList(SeqList

*L,

int

i,

ElemType

e)/*在顺序表L中第i个数据元素之前插入一个元素e。插入前表长n=L->last+1,i的合法取值范围是1≤i≤L->last+2

*/{int

k;if((i<1)||

(i>L->last+2))/*首先判断插入位置是否合法*/{printf(″插入位置i值不合法″);return(ERROR);}第2章线性表if(L->last>=maxsize-1){printf(″表已满无法插入″);return(ERROR);}for(k=L->last;k>=i-1;k--)/*为插入元素而移动位置*/L->elem[k+1]=L->elem[k];L->elem[i-1]=e;

/*在C语言数组中,第i个元素的下标为i-1

*/L->last++;return(OK);}第2章线性表【算法2.2线性表的插入运算】可以看出,当i=L->last+2时,语句L->elem[k+1]=L->

elem[k]将不会执行,因为循环的终值大于初值,此时不需要移动元素,可直接在表尾插入e。当i=1时,语句L->elem[k+1]=L->elem[k]需执行n次,即将表中已存在的n个元素依次后移一个位置才能将e插入。因此,语句L->elem[k+1]=L->elem[k]的频度与插入位置i有关。第2章线性表3.删除操作线性表的删除运算是指将表的第i(1≤i≤n)个元素删去,使长度为n的线性表(e1,…,ei-1,ei,ei+1,…,en)变成长度为n-1的线性表(e1,…,ei-1,ei+1,…,en)。例如:线性表(4,9,15,21,28,30,30,42,51,62)删除第5个元素,则需将第6个元素到第10个元素依次向前移动一个位置,如图2.4所示。第2章线性表图2.4顺序表中删除元素第2章线性表算法描述:int

DelList(SeqList

*L,

int

i,

ElemType

*e)/*在顺序表L中删除第i个数据元素,并用指针参数e返回其值。i的合法取值为1≤i≤L.last+1*/{int

k;if((i<1)||(i>L->last+1)){printf(″删除位置不合法!″);return(ERROR);}第2章线性表*e=L->elem[i-1];

/*将删除的元素存放到e所指向的变量中*/for(k=i;i<=L->last;k++)L->elem[k-1]=L->elem[k];

/*将后面的元素依次前移*/L->last--;Return(OK);}第2章线性表【算法2.3线性表的删除运算】与插入运算类似,在顺序表上实现删除运算也必须移动

结点,这样才能反映出结点间的逻辑关系的变化。若i=L->

last+1,则移位语句L->elem[k-1]=L->elem[k]不执行,因为循环变量的初值大于终值,此时不需要移动元素,仅将表长度减1即可。显然,删除算法中移位语句L->elem[k-1]=L->elem[k]的执行频度也与删除位置i有关。第2章线性表在顺序表中插入或删除一个数据元素时,其时间主要耗费在移动数据元素上。对于插入算法而言,设Pi为在第i个元素之前插入元素的概率,并假设在任何位置上插入的概率相等,即

Pi=1/(n+1),i=1,2,…,n+1。设Eins为在长度为n的表中插入一元素所需移动元素的平均次数,则同理,设Qi为删除第i个元素的概率,并假设在任何位置上删除的概率相等,即Q

i=1/n,i=1,2,…,n。删除一个元素所需移动元素的平均次数Edel为第2章线性表例2-1有两个顺序表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中。第2章线性表void

merge(SeqList

*LA,{int

i,

j,

k,

l;i=0;

j=0;

k=0;SeqList

*LB,SeqList

*LC)while(i<=LA->last&&j<=LB->last)if(LA->elem[i]<=LB->elem[j]){LC->elem[k]=LA->elem[i];i++;

k++;}else{第2章线性表

LC->elem[k]=LB->elem[j];j++;k++;}while(i<=LA->last)/*当表LA比表LB长时,则将表LA余下的元素赋给表LC

*/{LC->elem[k]=LA->elem[i];i++;

k++;}while(j<=LB->last)/*当表LB比表LA长时,则将表LB余下的元素赋给表LC

*/{LC->elem[k]=LB->elem[j];j++;

k++;}LC->last=LA->last+LB->last;}第2章线性表【算法2.4线性表的合并运算】由上面的讨论可知,线性表顺序表示的优点是:(1)无需为表示结点间的逻辑关系而增加额外的存储空间(因为逻辑上相邻的元素其存储的物理位置也是相邻的);(2)可方便地随机存取表中的任一元素。其缺点是:(1)插入或删除运算不方便,除表尾的位置外,

在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低;(2)由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配,因此当表长变化较大时,难以确定合适的存储规模。若按可能达到的最大长度预先分配表空间,则可能造成一部分空间长期闲置而得不到充分利用;若事先对表长估计不足,则插入操作可能使表长超过预先分配的空间而造成溢出。第2章线性表2.3

线性表的链式存储2.3.1单链表图2.5单链表的结点结构第2章线性表单链表包括两个域:数据域用来存储结点的值;指针域用来存储数据元素的直接后继的地址(或位置)。链表正是通过每个结点的指针域将线性表的n个结点按其逻辑顺序链接在一起。由于链表的每个结点只有一个指针域,故将这种链表又称为单链表。由于单链表中每个结点的存储地址是存放在其前趋结点的指针域中的,而第一个结点无前趋,因而应设一个头指针H指向第一个结点。同时,由于表中最后一个结点没有直接后继,则指定线性表中最后一个结点的指针域为“空”(NULL)。这样对于整个链表的存取必须从头指针开始。第2章线性表例如:图2.6所示为线性表(A,B,C,D,E,F,G,H)的单链表存储结构,整个链表的存取需从头指针开始进行,依次顺着每个结点的指针域找到线性表的各个元素。图2.6单链表的示例图第2章线性表图2.7单链表的逻辑状态图2.8带头结点单链表图示第2章线性表单链表可以由头指针唯一确定。单链表的存储结构描述如下:typedef

struct

Node{

ElemType

data;struct

Node

*next;}Node,

*LinkList;/*结点类型定义*//*

LinkList为结构指针类型*/假设L是LinkList型的变量,则L是一个结构指针,即单链表的头指针,它指向表中第一个结点(对于带头结点的单链表,则指向单链表的头结点),若L=NULL(对于带头结点的单链表为L->

next=NULL),则表示单链表为一个空表,其长度为0。若不是空表,则可以通过头指针访问表中结点,找到要访问的所有结点的数据信息。对于带头结点的单链表L,p=L->next指向表中的第一个结点a1,即p->data=a1,而p->next->data=a2。其余依此类推。第2章线性表2.3.2单链表上的基本运算1.建立单链表图2.9头插法建立单链表图示第2章线性表用头插法建立单链表的算法:Linklist

CreateFromHead(){LinkList

L;Node

*s;char

c;int

flag=1;/*设置一个标志变量flag,初值为1,当输入“$”时,将flag置为建表结束*/0,L=(Linklist)malloc(sizeof(Node));L->next=NULL;While(flag){/*为头结点分配存储空间*/第2章线性表c=getchar();if(c!

=′$′){s=(Node*)malloc(sizeof(Node));/*为读入的字符分配存储空间*/s->data=c;s->next=L->next;L->next=s;}else

flag=0;}return

L;

=}第2章线性表【算法2.5用头插法建立单链表】2)尾插法建表图2.10尾插法建表图示第2章线性表该算法的实现与头插法建表的不同之处在于指针的变化,其具体实现如下:Linklist

CreateFromTail(){/*将新增的字符追加到链表的末尾*/LinkList

L;Node

*r,

*s;int

flag=1;/*设置一个标志,初值为1,当输入“$”时,flag为0,表结束*/建L=(Node

*)malloc(sizeof(Node));

/*为头结点分配存储空间*/L->next=NULL;r=L;

/*

r指针始终动态指向链表的当前表尾,以便于做尾插入,其初值指向头结点*/while(flag){第2章线性表c=getchar();if(c!

=′$′){s=(Node*)malloc(sizeof(Node));s->data=c;r->next=s;r=s;}else{flag=0;r->next=NULL;/*将最后一个结点的next链域置为空,表示链表的结束*/}}

/*while*/return

L;}

/*CreateFromTail*/第2章线性表【算法2.6尾插法建表】2.查找1)按序号查找在单链表中,由于每个结点的存储位置都放在其前一结

点的next域中,因而即使知道被访问结点的序号i,也不能像顺序表那样直接按序号i访问一维数组中的相应元素,实现随机存取,而只能从链表的头指针出发,顺链域next逐个结点往下搜索,直至搜索到第i个结点为止。第2章线性表算法描述:设带头结点的单链表的长度为n,要查找表中第i个结点,则需要从单链表的头指针L出发,从头结点(L->next)开始顺着链域扫描,用指针p指向当前扫描到的结点,初值指向头结点,用j做计数器,累计当前扫描过的结点数(初值为0),当j=i时,指针p所指的结点就是要找的第i个结点。第2章线性表Node

*Get

(LinkList

L,

int

i)/*在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n),则返回该结点的存储位置;*//*否则返回NULL

*/{

int

j;Node

*p;p=L;j=0;

/*从头结点开始扫描*/while

(p->next!=NULL&&j<i){

p=p->next;

/*

扫描下一结点*/j++;

/*

已扫描结点计数器*/}if(i==j)

return

p;/*找到了第i个结点*//*找不到,i≤0或i>n

*/else

return

NULL;}

/*

Get

*/第2章线性表【算法2.7在单链表L中查找第i个结点】2)按值查找算法描述:按值查找是指在单链表中查找是否有结点值等于e的结点,若有的话,则返回首次找到的其值为e的结点的存储位置,否则返回NULL。查找过程从单链表的头指针指向的头结点出发,顺着链逐个将结点的值和给定值e作比较。第2章线性表Node

*Locate(

LinkList

L,

ElemType

key)/*在带头结点的单链表L中查找其结点值等于key的结点,若找到则返回该结点的位置p;否则返回NULL

*/{

Node

*p;p=L->next;

/*从表中第一个结点比较*/while

(p![KG-*2]=NULL)if

(p->data![KG-*2]=key)p=p->next;else

break;/*找到结点key,退出循环*/return

p;}

/*

Locate

*/第2章线性表【算法2.8在单链表L中查找值等于key的结点】3.单链表插入操作算法描述:要在带头结点的单链表L中第i个位置插入一个数据元素e,需要首先在单链表中找到第i-1个结点并由指针pre指示,然后申请一个新的结点并由指针s指示,其数据域的值为e,并修改第i-1个结点的指针使其指向s,然后使s结点的指针域指向原第i个结点。插入结点的过程如图2.11所示。第2章线性表int

InsList(LinkList

L,

int

i,

ElemType

e){/*在带头结点的单链表L中第i个位置插入值为e的新结点*/Node

*pre,*s;int

k;pre=L;

k=0;while(pre![KG-*2]=NULL&&k<i-1)/*在第i个元素之前插入,则先找到第i-1个数据元素的存储位置,使指针pre指向它*/{pre=pre->next;k=k+1;}第2章线性表if(k!

=i-1)/*即while循环是因为pre=NULL或i<1而跳出的,所以一定是插入位置不合理所致*/{printf(″插入位置不合理!″);return

ERROR;}s=(Node*)malloc(sizeof(Node));

/*为e申请一个新的结点并由s指向它*//*将待插入结点的值e赋给s的数据域*//*完成插入操作*/s->data=e;s->next=pre->next;pre->next=s;return

OK;}第2章线性表【算法2.9单链表插入操作】图2.11在单链表第i个结点前插入一个结点的过程第2章线性表4.删除图2.12单链表的删除过程第2章线性表int

DelList(LinkList

L,

int

i,

ElemType

*e)/*在带头结点的单链表L中删除第i个元素,并将删除的元素保存到变量*e中*/{Node

*p,

*r;int

k;p=L;

k=0;while(p->next![KG-*2]=NULL&&k<i-1)/*寻找被删除结点i的前驱结点i-1使p指向它*/{p=p->next;k=k+1;〖ZK)〗}第2章线性表if(k!

=i-1)/*即while循环是因为p->next=NULL或i<1而跳出的*/{printf(″删除结点的位置i不合理!″);return

ERROR;}r=p->next;p->next=p->next->next;

/*

删除结点r

*/free(r);

/*

释放被删除的结点所占的内存空间*/return

OK;}第2章线性表【算法2.10单链表删除操作】说明:删除算法中的循环条件(p->next!=NULL

&&k<i-1)与前插算法中的循环条件(p!=NULL

&&

k<i-1)不同,因为前插时的插入位置有m+1个(m为当前单链表中数据元素的个数)。i=m+1是指在第m+1个位置前插入,即在单链表的末尾插入。而删除操作中删除的合法位置只有m个,若使用与前插操作相同的循环条件,则会出现指针指空的情况,使删除操作失败。第2章线性表例2-2编写一个算法,求单链表的长度。算法描述:可以采用“数”结点的方法来求出单链表的

长度,用指针p依次指向各个结点,从第一个元素开始“数”,一直“数”到最后一个结点(p->next=NULL)。int

ListLength(LinkList

L)/*本算法用来求带头结点的单链表L的长度*/{Node

*p;p=L->next;j=0;

/*用来存放单链表的长度*/while(p![KG-*2]=NULL){

p=p->next;j

++;}return

j;}/*

End

of

function

ListLength

*/第2章线性表【算法2.11求单链表的长度】例2-3如果以单链表表示集合,假设集合A用单链表LA表示,集合B用单链表LB表示,设计算法求两个集合的差,即A-B。算法思想:由集合运算的规则可知,集合的差A-B中包含所有属于集合A而不属于集合B的元素。具体做法是,对于集合A中的每个元素e,在集合B的链表LB中进行查找,若存在与e相同的元素,则从LA中将其删除。第2章线性表void

Difference

(LinkList

LA,

LinkList

LB){/*此算法求两个集合的差集*/Node

*pre;*p,*r;pre=LA;

p=LA->next;/*

p指向LA表中的某一结点,而pre始终指向p的前驱*/while(p!

=NULL){q=LB->next;/*依次扫描LB中的结点,看是否有与LA中*p结点的值相同的结点*/while

(q!=NULL&&q->data!=p->data)q=q->next;if

(q![KG-*2]=NULL){第2章线性表r=p;pre->next=p->next;p=p->next;free(r);}else{pre=p;

p=p->next;}}

/*

while(p!

=NULL)*/}第2章线性表【算法2.12用单链表求两个集合的差集】2.3.3循环链表循环链表(Circular

Linked

List)是单链表的另一种形式,它是一个首尾相接的链表。其特点是将单链表最后一个结点的指

针域由NULL改为指向头结点或线性表中的第一个结点,就得到了单链形式的循环链表,并称为循环单链表。类似地,还有多重链的循环链表。在循环单链表中,表中所有结点都被链在一个环上,多重循环链表则是将表中的结点链在多个环上。为了使某些操作实现起来方便,在循环单链表中也可设置一个头结点。这样,空循环链表仅由一个自成循环的头结点表示。第2章线性表图2.13带头结点循环单链表第2章线性表例2-4有两个带头结点的循环单链表LA、LB,编写一个算法,将两个循环单链表合并为一个循环单链表,其头指针为LA。算法思想:先找到两个链表的尾,并分别由指针p、q指向它们,然后将第一个链表的尾与第二个表的第一个结点链接起来,并修改第二个表的尾Q,使它的链域指向第一个表的头结点。第2章线性表LinkListmerge

--1(LinkList

LA,

LinkList

LB){/*此算法将两个链表的首尾连接起来*/Node

*p,

*q;p=LA;q=LB;while

(p->next!

=LA)p=p->next;/*找到表LA的表尾,用p指向它*/while

(q->next!

=LB)q=q->next;/*找到表LB的表尾,用q指向它*/q->next=LA;/*修改表LB的尾指针,使之指向表LA的头结点*/p->next=LB->next;/*修改表LA的尾指针,使之指向表LB中的第一个结点*/free(LB);return(LA);}第2章线性表【算法2.13循环单链表的合并算法(1)】采用上面的方法,需要遍历链表,找到表尾,其执行时间是O(n)。若在尾指针表示的单循环链表上实现,则只需要修改指针,无需遍历,其执行时间是O(1)。LinkList

merge

--2(LinkList

RA,

LinkList

RB){/*此算法将两个链表首尾连接起来*/Node

*p;p=RA->next;/*保存链表RA的头结点地址*/RA->next=RB->next->next;/*链表RB的开始结点链到链表RA的终端结点之后*/free(RB->next);/*释放链表RB的头结点*/RB->next=p;/*链表RA的头结点链到链表RB的终端结点之后*/return

RB;/*返回新循环链表的尾指针*/}第2章线性表【算法2.14循环单链表的合并算法(2)】2.3.4双向链表循环单链表的出现,虽然能够实现从任一结点出发沿着链能找到其前驱结点,但时间耗费是O(n)。如果希望从表中快速确定某一个结点的前驱,另一个解决方法就是在单链表的每个结点里再增加一个指向其前驱的指针域prior。这样形成的链表中就有两条方向不同的链,我们可称之为双(向)链表(DoubleLinked

List)。双链表的结构定义如下typedef

struct

DNode{

ElemType

data;struct

DNode

*prior,

*next;}DNode,

*DoubleList;第2章线性表图2.14双链表的结点结构第2章线性表由于在双向链表中既有前向链又有后向链,寻找任一个结点的直接前驱结点与直接后继结点变得非常方便。设指针p指向双链表中某一结点,则有下式成立:p->prior->next

=

p

=

p->next->prior第2章线性表图2.15双向循环链表图示第2章线性表1.双向链表的前插操作图2.16双向链表插入操作第2章线性表int

DlinkIns(DoubleList

L,

int

i,

ElemType

e){DNode

*s,

*p;.../*先检查待插入的位置i是否合法(实现方法同单链表的前插操作)*/.../*若位置i合法,则让指针p指向它*/s=(DNode*)malloc(sizeof(DNode));if

(s){s->data=e;s->prior=p->prior;

p->prior->next=s;s->next=p;

p->prior=s;return

TRUE;}else

return

FALSE;}第2章线性表【算法2.15双向链表的插入操作】2.双向链表的删除操作算法描述:图2.17双向链表的删除操作第2章线性表int

DlinkDel(DoubleList

L,

int

i,

ElemType

*e){DNode

*p;.../*首先检查待插入的位置i是否合法(实现方法同单链表的删除操作)

*/.../*若位置i合法,则让指针p指向它*/*e=p->data;p->prior->next=p->next;p->next->prior=p->prior;free(p);return

TRUE;}第2章线性表【算法2.16双向链表的删除操作】例2-5设一个循环双链表L=(a,b,c,d),编写一个算法,将链表转换为L=(b,a,c,d)。算法思想:本题实际上是交换表中前两个元素的次序。第2章线性表void

swap(DLinkList

L){DNode

*p,

*q,

*h;h=L->next;p=h->next;q=h->prior;h->next=p->next;p->next->prior=h;p->prior=q;p->next=h;L->next=p;}/*

h指向表中的第一个结点,即a

*//*

p指向b结点*//*保存a结点的前驱*//*

a结点的后继指向c结点*//*

c结点的前驱指向a结点*//*将b结点插入,作为表的第一个结点*//*将表的头结点的next域指向b结点*/第2章线性表【算法2.17交换循环双链表中前两个元素的次序】*2.3.5静态链表静态链表同样可以借助一维数组来描述:#define

Maxsize=链表可能达到的最大长度typedef

struct{ElemType

data;int

cursor;}Component,

StaticList[Maxsize];第2章线性表图2.18静态链表的插入和删除操作示例第2章线性表1.初始化所谓初始化操作,是指将这个静态单链表初始化为一个备用静态单链表。设space为静态单链表的名字,av为备用单链表的头指针,其算法如下:void

initial(StaticList

space,

int

*av){int

k;space[0]->cursor=0;

/*

设置已用静态单链表的头指针指向位置0

*/for(k=0;

k<Maxsize-1;

k++)

space[k]->cursor=k+1;

/*

连链*/space[Maxsize-1]=0;

/*

标记链尾*/*av=1;/*设置备用链表头指针初值*/}第2章线性表【算法2.18静态单链表初始化】2.分配结点int

getnode(int

*av)/*从备用链表摘下一个结点空间,分配给待插入静态链表中的元素*/{

int

i;i=*av;*av=space[*av].cur;return

i;}第2章线性表【算法2.19分配结点】3.结点回收void

freenode(int

*av,

int

k)/*将下标为k的空闲结点插入到备用链表*/{

space[k].cursor=*av;*av=k;}第2章线性表【算法2.20空闲结点回收】4.前插操作在已用静态单链表space的第i个数据元素之前插入一个数据元素x。算法描述:①先从备用单链表上取一个可用的结点;②将其插入到已用静态单链表第i个元素之前。第2章线性表void

insbefore(StaticList

space,

int

i,

int

*av){int

j,

k,

m;j=*av

;

/*

j为从备用表中取到的可用结点空间的下标*/av=space[av]->cursor;

/*

修改备用表的头指针*/space[j]->data=x;k=space[0]->cursor

;

/*

k

为已用静态单链表的第一个元素的下标值*/for([ZK(]m=1;

m<i-1;

m++)

/*

寻找第i

-1个元素的位置k

*/k=space[k]->cursor

;space[j]->cursor=space[k]->cursor;space[k]->cursor=j;}/*修改游标域,实现插入操作*/第2章线性表【算法2.21在已用静态单链表的第i个元素之前插入元素x】5.删除删除已用静态单链表中第i个元素。算法描述:①寻找第i-1个元素的位置,然后通过修改相应的游标域进行删除;②将被删除的结点空间链到可用静态单链表中,实现回收。第2章线性表void

delete(StaticList

space;

int

i;

int

*av

){int

j,

k,

m;k=space[0]->cursor;for(m=1,m<i-1;m++)

/*寻找第i-1个元素的位置k

*/k=space[k]->cursor

;j=space[k]->cursor

;space[k]->cursor=space[j]->cursor

;/*从已用表中删除第i个元素*/space[j]->cursor=*av;/*将第i个元素占据的空间回收,即将其链入备用表*/av=j

;

/*

置备用表头指针以新值*/}第2章线性表【算法2.22删除已用静态单链表中的第i个元素】2.3.6顺序表和链表的比较1.基于空间的考虑在链表中的每个结点,除了数据域外,还要额外设置指针(或光标)域,从存储密度来讲,这是不经济的。所谓存储密度(Storage

Density),是指结点数据本身所占的存储量和整个结点结构所占的存储量之比,即存储密度=结点数据本身所占的存储量/结点结构所占的存储总量第2章线性表一般地,存储密度越大,存储空间的利用率就越高。显然,顺序表的存储密度为1,而链表的存储密度小于1。例如单链表的结点的数据均为整数,指针所占空间和整型量相同,则单链表的存储密度为50%。因此若不考虑顺序表中的备用

结点空间,则顺序表的存储空间利用率为100%,而单链表的存储空间利用率为50%。由此可知,当线性表的长度变化不

大,易于事先确定其大小时,为了节约存储空间,宜采用顺序表作为存储结构。第2章线性表2.基于时间的考虑顺序表是由向量实现的,它是一种随机存取结构,对表中任一结点都可以在O(1)时间内直接地存取,而链表中的结点,需从头指针起顺着链找才能取得。因此,若线性表的操作主要是进行查找,很少做插入和删除时,宜采用顺序表做存储结构。在链表中的任何位置上进行插入和删除,都只需要修改指针。而在顺序表中进行插入和删除,平均要移动表中近一半的结点,尤其是当每个结点的信息量较大时,移动结点的时间开销就相当可观。因此,对于频繁进行插入和删除的线性表,宜采用链表

做存储结构。若表的插入和删除主要发生在表的首尾两端,则

宜采用尾指针表示的单循环链表。第2章线性表3.基于语言的考虑对于没有提供指针类型的高级语言,若要采用链表结构,则可以使用光标实现的静态链表。虽然静态链表在存储分配上有不足之处,但它和动态链表一样,具有插入和删除方便的特点。值得指出的是,即使是对那些具有指针类型的语言,静态链表也有其用武之地。特别是当线性表的长度不变,仅需改变结点之间的相对关系时,静态链表比动态链表可能更方便。第2章线性表2.4

一元多项式的表示及相加对于符号多项式的各种操作,实际上都可以利用线性表来处理。比较典型的是关于一元多项式的处理。在数学上,一个一元多项式Pn(x)可按升幂的形式写成:它实际上可以由n+1个系数唯一确定。因此,在计算机内,可以用一个线性表P来表示:第2章线性表假设Qm(x)是一个一元多项式,则它也可以用一个线性表Q来表示,即若假设m<n,则两个多项式相加的结果Rn(x)=Pn(x)+Qm(x),也可以用线性表R来表示:第2章线性表我们可以采用顺序存储结构来实现顺序表的方法,使得多项式的相加的算法定义十分简单,即p[0]存系数p0,p[1]存系数p1,…,p[n]存系数p-n,对应单元的内容相加即可。但是在通常的应用中,多项式的指数有时可能会很高并且变化很大。例如:若采用顺序存储,则需要20001个空间,而存储的有用数据只有三个,这无疑是一种浪费。若只存储非零系数项,则必须存储相应的指数信息才行。第2章线性表假设一元多项式Pn(x)=p1xe1+p2xe2+…+pmxem,其中pi是指数为ei的项的系数(且0≤e1≤e2≤…≤em=n),若只存非零系数,则多项式中每一项由两项构成(指数项和系数项),用线性表来表示,即采用这样的方法存储,在最坏情况下,即n+1个系数都不为零,则比只存储系数的方法多存储1倍的数据。但对于非零系数多的多项式则不宜采用这种表示。第2章线性表(1)用单链表存储多项式的结点结构如下:

struct

Polynode{int

coef;int

exp;Polynode

*next;}

Polynode,

*

Polylist;第2章线性表(2)通过键盘输入一组多项式的系数和指数,以输入系数0为结束标志,并约定建立多

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论