数据结构复习笔记(二)

第二章 线性表

线性结构是一个数据元素的有序(次序)集

线性结构的基本特征:

1.集合中必存在唯一的一个“第一元素”;

2.集合中必存在唯一的一个“最后元素”;

3.除最后元素之外,均有唯一的后继;

4.除第一元素之外,均有唯一前驱。

2.1线性表的类型定义

抽象数据类型线性表的定义如下:

ADT List{

数据对象:

D = {ai | ai∈ElemSeti=1,2,.....nn0}

{n为线性表的表长;

n=0时的线性表为空表。}

数据关系:

R1 = {|ai-1,ai∈Di=2,.......n}

{设线性表为(a1,a2,.....ai,.....an),

iai在线性表中的位序。} 

基本操作:

{结构初始化}


InitList&L

操作结果:构造一个空的线性表L


{销毁结构}


DestroyList&L

初始条件:线性表L已存在。

操作结果:销毁线性表L


{引用型操作}


ListEmptyL

初始条件:线性表L已存在。

操作结果:若L为空表,则返回TRUE,否则FALSE

 

ListLengthL

初始条件:线性表L已存在。

操作结果:返回线性表L中元素的个数。

 

PriorElemLcur_e&pre_e

初始条件:线性表L已存在。

操作结果:若cur_eL的元素,但不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义。

 

NextElemLcur_e&next_e

初始条件:线性表L已存在。

操作结果:若cur_eL的元素,但不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义。

  

GetElemLi&e

初始条件:线性表L已存在,1≦i≦LengthListL)。

操作结果:用e返回L中的第i个元素的值。

 

LocateElemLecompare())

初始条件:线性表L已存在,compare()是元素判定函数。

操作结果:返回L中第一个与e满足关系compare()的元素的位序,若这样的元素不存在,则返回值为0

 

ListTraverseLvisit()

初始条件:线性表L已存在。

操作结果:依次对L的每个元素调用函数visit()。一旦visit()失败,则操作失败。

 

{加工型操作}

ClearList&L

初始条件:线性表L已存在。

操作结果:将L重置为空表。

 

PutElemLi&e

初始条件:线性表L已存在,1≦i≦LengthListL)。

操作结果:L中第i个元素赋值同e的值。

 

ListInsert&Lie

初始条件:线性表L已存在,1≦i≦LengthListL+1

操作结果:在L的第i个元素之前插入新的元素eL的长度增1

  

ListDelete&Lie

初始条件:线性表L已存在且非空,1≦i≦LengthListL)。

操作结果:删除L的第i个元素,并用e返回其值,L的长度减1


}ADT List

 

2.2线性表类型的实现——顺序映象

用一组地址连续的存储单元依次存放线性表中的数据元素

a1

a2

....

ai-1

ai

....

an

 

 

以“存储位置相邻”表示有序对

即:LOC(ai)=LOC(ai-1)+C

一个数据元素所占存储量↑

所有数据元素的存储位置均取决于第一个数据元素的存储位置

LOCai)=LOCa1+i-1*C

             ↑基地址

顺序映象的C语言描述

#define LIST_INIT_SIZE 80

//线性表存储空间的初始分配量

#define LISTINCREMENT 10

//线性表存储空间的分配增量

Typedef struct{

ElemType * elem;//存储空间基址

int length;//当前长度

int listsize;//当前分配的存储容量

//(以sizeofElemType)为单位)

}SqList;//俗称顺序表

线性表的初始化操作

Status InitList_Sq(Sqlist &L){

//构造一个空的线性表

L.elem = (ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType));

if(!L.elem)exit(OVERFLOW);

L.length = 0;

L.listsize = LIST_INIT_SIZE;

return OK;

}//InitList_Sq 

 

线性表操作

LocateElem(L,e,compare())的实现:

int LocateElem_Sq(SqList L,ElemType e,Status(*compare)(ElemType,ElemType)){

i = 1;//i的初值为第一元素的位序

p = L.elem;//p的初值为第一元素的存储位置

while(i<=L.length && !(*compare)(*p++,e))

++i;

if(i <= L.length) return i;

else return 0;

}//LocateElem_Sq;

此算法的时间复杂度为O(ListLength(L))

 

线性表操作

ListInsert(&L,i,e)的实现:

Status ListInsert_Sq(SqList &L,int pos,ElemType e){

if(pos < 1|| pos > L.length + 1)return ERROR;//插入位置不合法

if(L.length >= L.listsize){//当前存储空间已满,增加分配

newbase = (ElemType *)realloc(L.elem,(L.listsize + LISTINCREMENT) * sizeof(ElemType));

if(!newbase)exit(OVERFLOW);//存储分配失败

L.elem = newbase;//新基址

L.listsize += LISTINCREMENT;//增加存储容量

}

q = &(L.elem[pos-1]);//q指示插入位置

for(p = &(L.elem[L.length-1]);p >=q;-- p)

*(p+1) = *p;//插入位置及之后的元素右移

*q = e;//插入e

++L.length;//表长增1

return OK;

}//ListInsert_Sq;

此算法时间复杂度为O(ListLength(L))

 

 

线性表操作

ListDelete(&L,i,&e)的实现:

Status ListDelete_Sq(SqList &L, int pos, ElemType &e){

if((pos < 1)||(pos > L.length)) return ERROR;//删除位置不合法

p = &(L.elem[pos-1]);//p为被删除元素的位置

e = *p;//被删除元素的值赋给e

q = L.elem + L.length - 1;//表尾元素的位置

for(++p;p <= q;++p)

*(p-1) = *p;//被删除元素之后的元素左移

--L.length;//表长减1

return OK;

}//ListDelete_Sq;

此算法的时间复杂度为:O(ListLength(L))

 

2.3线性表类型的实现——链式映象

一、单链表

用一组地址任意的存储单元存放线性表中的数据元素

以元素(数据元素的映象)+指针(指示后继元素存储位置的)=结点(表示数据元素)

以“结点的序列”表示线性表——称作链表

以线性表中第一个数据元素的存储地址作为线性表的地址,称作线性表的头指针

二、结点和单链表的C语言描述

Typedef struct LNode{

ElemType data;//数据域

Struct Lnode *next;//指针域

}LNode, *LinkList;

三、单链表操作的实现

线性表的操作GetElem(L,i,&e)

在链表中的实现:

基本操作位:使指针p始终指向线性表中第j个数据元素

Status GetElem L(LinkList L,int pos,ElemType &e){

p = L->next; j = 1;//初始化,p指向第一个结点,j为计数器

while(p && jnext; ++j; }//p指针向后查找,直到p指向第pos个元素或p为空

if(!p || j>pos)

return ERROR;//pos个元素不存在

E = p->data;//取第pos个元素

return OK

}//GetElem_L

此算法的时间复杂度为:O(ListLength(L))

 

线性表的操作ListInsert(&L,i,e)在链表中的实现:

基本操作为:找到线性表中第i-1个结点,修改其指向后继的指针

Status ListInsert(LinkList &L, int pos, ElemType e){

p = L;j = 0;

while(p && j < pos-1)

{ p=p->next;++jl}//寻找第pos-1个结点

if(!p||j > pos - 1

return ERROR;//pos小于1或者大于表长

s = (LinkList)malloc(sizeof(LNode));//生成新结点

s->data = e;s->next = p->next;//插入L

p->next = s;

return OK;

}//ListInsert_L

此算法的时间复杂度为O(ListLength(L))

 

线性表的操作ListDelete(&L,i,&e)在链表中的实现:

基本操作为:找到线性表中第i-1个结点,修改其指向后继的指针

Status ListDelete(LinkList &L, int pos, ElemType &e){

p = L;j = 0;

while(p->next && j

{p = p->next;++j;}//寻找第pos个结点,并令p指向其前驱

if(!(p->next)||j > pos-1)

return ERROR;//删除位置不合理

q = p->next;p->next = q->next;//删除并释放结点

e = q->data; free(q);

return OK;

}//ListDelete_L;

此算法的时间复杂度为O(ListLength(L))

 

初始化链表:

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;//插入到表头。

}

}//CreateList_L

此算法的时间复杂度为:O(ListLength(L))

 

求两个集合的并:

Void union(List &La,List Lb){

La _len = ListLength(La);

Lb_len = ListLength(Lb);

for(i = 1;i <= Lb_len;i ++){

GetElem(Lb,i,e);

if(!LocateElem(La,e,equal()))

ListInsert(La,++La_len,e)

}

}//union

上述算法的时间复杂度:

控制结构:for循环

基本操作:LocateElem(La,e,equal())

当以顺序映象实现抽象数据类型线性表时间复杂度为:O(La_len*Lb_len);

当以链式映象实现抽象数据类型线性表时间复杂度为:O(La_len*Lb_len);

 

有序合并:

void purge(List &La,List Lb){

InitList(LA);

La_len = ListLength(La);

Lb_len = ListLength(Lb);

for(i = 1;i <= Lb_len;i ++){

GetElem(Lb,i,e);

if(!equal(en,e)){

ListInsert(La,++La_len,e);

en = e;

}

}

}//purge

上述算法的时间复杂度:

控制结构:for循环

基本操作:GetElem(Lb,i,e);

当以顺序映象实现抽象数据类型线性表时间复杂度为:O(Lb_len);

当以链式映象实现抽象数据类型线性表时间复杂度为:O(La_len*Lb_len);

 

 

归并两个线性表:

Void MergeList(List La,List Lb,List &Lc){

InitList(Lc);

i = j = 1;k = 0;

La_len = ListLength(La);

Lb_len = ListLength(Lb);

while((i <= La_len)&&(j <= Lb_len)){

GetElem(La,i,ai);GetElem(Lb,j,bj);

if(ai <= bj){

ListInsert(Lc,++k,ai);

++i;

}

else{

ListInsert(Lc,++k,bj);

++j;

}

}

Whilei <= La_len{

GetElem(La,i++,ai);ListInsert(Lc,++k,ai);

}

Whilej <= Lb_len{

GetElem(Lb,j++,bj);ListInsert(Lc,++k,bj);

}

}

上述算法的时间复杂度为:

控制结构:三个并列的while循环

基本操作:ListInsert(Lc,++k,e)

当以顺序映象实现抽象数据类型线性表时间复杂度为:O(La_len+Lb_len);

当以链式映象实现抽象数据类型线性表时间复杂度为:O(La_len+Lb_len)*(La_len+Lb_len);

 

用上述定义的单链表实现线性表的操作时,

存在的问题:

1.单链表的表长是一个隐含的值;

2.在单链表的最后一个元素最后插入元素时,需遍历整个链表;

3.在链表中,元素的“位序”概念淡化,结点的“位置”概念强化。

改进链表的设置:

1.增加“表长”、“表尾指针”和“当前位置的指针”三个数据域;

2.将基本操作由“位序”改变为“指针”

 

 

四、一个带头结点的线性表类型

Typedef struct LNode{//结点类型

ElemType data;

struct LNode *next;

}*Link,*Position;

 

Status MakeNode(Link &p,ElemType e);

//分配由p指向的值为e的结点,并返回OK

//若分配失败,则返回ERROR

void FreeNode(Link &p);

//释放p所指结点

 

Typedef struct{

Link head,tail;//指向头结点和最后一个结点

int len;//指示链表长度

Link current;

//指向当前访问的结点的指针

//初始位置指向头结点

}LinkList;

 

链表的基本操作:

{结构初始化和销毁结构}

Status InitList(LinkList &L);

//构造一个空的线性链表L

//头指针、尾指针和当前指针均指向头结点,表长为零

 

Status DestroyList(LinkList &L);

//销毁线性链表LL不再存在

 

{引用型操作}

Status ListEmpty(LinkList L);//判表空

Int ListLength(LinkList L);//求表长

Status Prior(LinkList L);//改变当前指针指向其前驱

Status Next(LinkList L);//改变当前指针指向其后继

ElemType GetCurElem(LinkList L);//返回当前指针所指数据元素

Status LocatePos(LinkList L,int i);//改变当前指针指向第i个结点

Status LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType));

//若存在与e满足函数compare()判定后关系的元素,则移动当前指针

//指向第1个满足条件的元素,并返回OK;否则返回ERROR

Status ListTraverse(LinkList L,Status(*visit()));

//依次对L的每个元素调用函数visit()

 

{加工型操作}

Status ClearList(LinkList &L);//重置为空表

Status SetCurElem(LinkList &L,ElemType e);//更新当前指针所指数据元素

Status Append(LinkList &L,Link s);//一串结点链接在最后一个结点之后

Status InsAfter(LinkList &L,ElemType e);//将元素e插入到当前指针之后

Status DelAfter(LinkList &L,ElemType *e);//删除当前指针之后的结点

 

Status InsAfter(LinkList &L,ElemType e){

//当前指针在链表中,则将数据元素e插入在线性链表L

//当前指针所指结点之后,并返回OK;否则返回ERROR

if(!L.current)return ERROR;

if(!MakeNode(s,e))return ERROR;

S->next = L.current->next;

L.current->next = s;

return OK;

}//InsAfter

 

Status DelAfter(LinkList & L,ElemType &e){

//当前指针及其后继在链表中,则删除线性链表L中当前指针所指结点之后的结点

//并返回OK,否则返回ERROR

if(!(L.current&&L.current->next))return ERROR;

q = L.current->next;

L.current->next = q->next;

e = q->data;

FreeNode(q);

return OK;

}//DelAfter

 

{利用上述定义的线性链表可以完成线性表的其他操作}

 

例一:

Status ListInsert_L(LinkList L,int i ,ElemType e){

//在带头结点的单链线性表L的第i个元素之前插入元素e

if(!LocatePos(L,i-1))return ERROR;//i使不合法

if(InsAfter(L,e))return OK;//插入第i-1个结点之后

else return ERROR;

}//ListInsert_L

 

例二:

Void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc,int(*compare)(ElemType,ElemType)){

if(!InitList(Lc))return ERROR;//存储空间分配失败

LocatePos(La,0);LocatePos(Lb,0);//当前指针指向头结点

if(DelAfter(La,e)) a = e;

else a = MAXC;//MAXC为常量最大值

if(DelAfter(Lb,e)) b = e;

else b = MAXC;//ab为两表中当前比较元素

while(!(a = MAXC && b = MAXC)){//LaLb非空

.......

}

DestroyList(La);DestroyList(Lb);//销毁链表LaLb

return OK;

}//MergeList_L

 

if((*compare)(a,b) <= 0){//a <=b

InsAfter(Lc,a);

if(DelAfter(La,e1)) a = e1;

else a = MAXC;

}

else{//a > b

InsAfter(Lc,s);

if(DelAfter(Lb,e)) b = e1;

else b = MAXC;

}

 

五、其它形式的链表

1.双向链表

//-----线性表的双向链表存储结构----

Typedef sttuct DuLNode{

ElemType data;//数据域

struct DuLNode *prior;//指向前驱的指针域

struct DuLNode *next;//指向后继的指针域

}DuLNode,*DuLinkList;

 

2.循环链表

最后一个结点的指针域的指针又指回第一个结点的链表。

 

2.4一元多项式的表示

一元多项式

pn(x) = p0 + p1x + p2x2 +...+pnxn

在计算机中,可以用一个线性表来表示:

P = (p0,p1,.....,pn)

一般情况下的一元多项式可写成

Pn(x) = p1xe1 + p2xe2 +....+ pmxem

其中:pi是指数为ei的项的非零系数,

0 <= e1 < e2 <....< em = n

((p1,e1),(p2,e2),...(pm,em))

 

抽象数据类型一元多项式的定义如下:

 

ADT Polynomial{

数据对象:D = {ai | ai∈TeemSet, i = 1,2,3....,m, m≥0

TermSet中的每一个元素包含一个表示系数的实数和表示指数的整数}

数据关系:R1 = {i-1,ai> | ai-1,ai∈D,ai-1中的指数值i中的指数值,i = 2,....,n}

基本操作:

CreatPolyn(&P,m)

操作结果:输入m项的系数和指数,建立一个一元多项式P

DestroyPolyn(&P)

初始条件:一元多项式P已存在。

操作结果:销毁一元多项式P

PrintPolyn(P)

初始条件:一元多项式P已存在。

操作结果:打印输出一元多项式P

PolynLength(P)

初始条件:一元多项式P已存在。

操作结果:返回一元多项式P中的项数。

AddPolyn(&Pa&Pb)

初始条件:一元多项式PaPb已存在。

操作结果:完成多项式相加运算,即:Pa = Pa + Pb,并销毁一元多项式Pb

SubtractPolyn(&Pa&Pb)

初始条件:一元多项式PaPb已存在。

操作结果:完成多项式相减运算,即:Pa = Pa - Pb,并销毁一元多项式Pb

MultiplyPolyn(&Pa&Pb)

初始条件:一元多项式PaPb已存在。

操作结果:完成多项式相乘运算,即:Pa = Pa × Pb,并销毁一元多项式Pb

}ADT Polynomial

 

抽象数据类型Polynomial的实现

 

Typedef struct{//项的表示,多项式的项作为LinkList的数据元素

float coef;//系数

int expn;//指数

}term,ElemType;//两个类型名:term用于本ADTElemTypeLinkList的数据对象名

 

Typedef LinkList polynomial;//用带表头结点的有序链表表示多项式

 

int cmp(term a,term b);

//a的指数值<(=)(>)b的指数值,分别返回-10+1

 

Void CreatPolyn(polynomial &P,int m){

//输入m项的系数和指数,建立表示一元多项式的有序链表P

initList(P);

e.coef = 0.0; e.expn = -1;

setCurElem(P,e);//置头结点的数据元素

for(i = 1;i <= m; ++i){

//依次输入m个非零项

scanf(e.coef,e.expn);

if(!LocateElem(P,e,(*cmp)()))

//当前链表中不存在该指数项

InsAfter(P,e)

}

}//CreatPolyn

 

 

 

 


你可能感兴趣的:(数据结构(C语言))