第二章 线 性 表(顺序表、单链表、静态链表、循环链表、双向链表)

来源:互联网 发布:暗黑破坏神2数据库 编辑:程序博客网 时间:2024/05/10 06:35

1. 线性表:是由n(n≥0)个数据元素组成的有限序列(数据元素的非空有限集合)

若将线性表记为(a1,...,ai-1,ai,ai+1,...,an),则表中ai-1领先于aiai领先于ai+1,ai-1ai的直接前驱元素,ai+1ai的直接后继。当i=1,2,...,n-1时,ai有且仅有一个直接后继,当i=2,3,...,n时,ai有且仅有一个直接前驱。

线性表元素的个数nn>=0)定义为线性表的长度,当n=0时,称为空表。

在较复杂的线性表中,一个数据元素可以有若干个数据项组成。

2. 抽象数据类型线性表的定义:

ADT {    数据对象:D = {ai|ai∈Element,i = 1,2,3,...,n, n>=0};    数据关系:R = {<ai-1,ai>| ai-1,ai∈D,i = 2,3,...,n};    基本操作:        InitList(&L);//构造一个空的线性表        DestroyList(&L);//L已存在;销毁线性表L;        ClearList(&L);//L已存在;把L重置为空表;        ListEmpty(L);//若L为空表,返回TRUE;否则返回FALSE;        ListLength(L);//返回L中元素的个数;        GetElem(L,i,&e);//返回第i个元素赋给e;        LocateElem(L,e,compare());//返回L中第一个与e满足函数compare()的元素的位序;        PriorElem(L,cur_e,&pre_e);//返回cur_e的前驱元素赋给pre_e;        NextElement(L,cur_e,&next_e);//返回cur_e的后继元素赋给next_e;        ListInsert(&L,i,e);//在L中第i个位置插入元素e;L长度加1;        ListDelete(&L,i,e);//删除L中第i个元素,并用e返回其值;L长度减1;        ListTraverse(L,visit());//依次对L中每个元素调用函数visit();} ADT List
<span style="font-family:'宋体';font-size: 10.5pt;"></span><span style="font-family:'宋体';font-size: 10.5pt;"></span>

2.0 求两个线性表LaLb并集

//扩大La,将存在于Lb中且不存在与LA中的元素依次插入LA中(取LB中元素在LA中查访)void union(List &La, List Lb) {    La_len = ListLength(La);    Lb_len = ListLength(Lb);    for(int i = 1; i<=Lb_len; i++) {        GetElem(Lb, i, e);        if(!LocateElem(La, e, equal)) ListInsert(La, ++La_len, e);    }} //union

2.1 合并两个数据元素按值非递减排列的线性表LaLb,输出Lc

void MergeList (List La, List Lb, List &Lc) {    InitList(Lc);    int i = j = k = 0;    La_len = ListLength(La);    Lb_len = ListLength(Lb);    while((i<=La_len)&&(j<=Lb_len)) {        GetElem(La, i, ai);        GetElem(Lb, j, bi);        if(ai < bi) { ListInsert(Lc, ++k, ai); ++i; }        else {ListInsert(Lc, ++k, bi); ++j;}    }    while(i<=La_len) {  GetElem(La, i++, ai);   ListInsert(Lc, ++k, ai); }    while(i<=Lb_len) {  GetElem(Lb, j++, bi);ListInsert(Lc, ++k, bi); }} //MergeList

 

顺序表(线性表的顺序存储结构)

1、线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

2、存储器中每个单元都有自己的编号,这个编号称为地址。

3、顺序表的三个属性:

Ø 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置。

Ø 线性表的最大存储容量:数组长度MaxSize。

Ø 线性表的当前长度:length.

//顺序表:顺序存储结构的线性表(动态分配的一维数组)typedef struct {     ElemType data[MAXSIZE];     int      length;}SqList;Status Init_SqList(SqList *L);//初始化ElemType GetElem_SqList(SqList L, int i) ;//返回元素Status Insert_SqList(SqList *L, int i, ElemType e);//插入元素ElemType Delete_SqList(SqList *L, int i);//删除指定位置元素并返回值int Search_SqList(SqList L, ElemType e, Status(*compare)(ElemType, ElemType));//查找并返回符合比较条件的元素位置,否则返回-1void Merge_SqList(SqList La, SqList Lb, SqList *Lc);//合并两个顺序表

4、顺序表的存取数据时间复杂度为O(1),插入删除元素时间复杂度为O(n)。

5、顺序存储结构的优缺点:

Ø 优点:1、无须为表示表中元素之间的逻辑关系而增加额外的存储空间。

     2、可以快速低存取表中任一位置的元素。

Ø 缺点:1、插入和删除操作需要移动大量元素。

     2、当线性表长度变化较大时,难以确定存储空间的容量

 3、造成存储空间的“碎片”

6、顺序表上的基本运算(初始化、插入、删除、查找、合并)

//初始化Status Init_SqList(SqList *L) {//     L->data = (ElemType*)malloc(MAXSIZE*sizeof(ElemType));//     if(!L->data)   exit(OVERFLOW);     L->length = 0;     return OK;}//返回元素ElemType GetElem_SqList(SqList L, int i) {    if(i>=0 && i<L.length)   return L.data[i];    return -1;  //i号元素不存在}//插入元素Status Insert_SqList(SqList *L, int i, ElemType e) {     if(L->length == MAXSIZE || i<0 || i>L->length) return ERROR;//顺序表存储空间已满,或指定插入位置无效     int j;     for(j=L->length; j>i; j--)        L->data[j] = L->data[j-1];  //从i到length-1号元素后移     L->data[j] = e;     ++L->length;     return OK;}//删除指定位置元素并返回值ElemType Delete_SqList(SqList *L, int i) {     if(i<0 || i>L->length-1) return ERROR;     ElemType e = L->data[i];     int j;     for(j=i; j<L->length-1; j++)        L->data[j] = L->data[j+1];  //从i+1到length-1号元素前移     --L->length;     return e;}//查找并返回符合比较条件的元素位置,否则返回-1int Search_SqList(SqList L, ElemType e, Status(*compare)(ElemType, ElemType)) {     int i = 0;     ElemType* p = L.data;     while(i<L.length && !(*compare)(*p++, e)) ++i;     if(i<L.length) return i;     else return -1;}//合并两个顺序表,并按增序排列void Merge_SqList(SqList La, SqList Lb, SqList *Lc) {     ElemType* pa = La.data;    //pa指向La第一个元素     ElemType* pb = Lb.data;     int len = La.length + Lb.length;     Lc->length = len < MAXSIZE ? len : MAXSIZE;     ElemType* pc = Lc->data;     ElemType* pa_last = La.data + La.length-1;     //pa_last指向La最后一个元素     ElemType* pb_last = Lb.data + Lb.length-1;     ElemType* pc_last = Lc->data + Lc->length-1;     while(pa<=pa_last && pb<=pb_last && pc<=pc_last) {          if(*pa<=*pb) *pc++ = *pa++;          else *pc++ = *pb++;     }     while(pa<=pa_last && pc<=pc_last) *pc++ = *pa++;   //Lb中元素已全部添加到Lc     while(pb<=pb_last && pc<=pc_last) *pc++ = *pb++;   //La中元素已全部添加到Lc}

 

单链表

1、链表:为了表示每个数据元素ai与其直接后继数据元素ai+1之间的逻辑关系,对数据元素ai来说,除了其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置)。

我们把存储数据元素的信息域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成数据元素ai的存储映像,成为结点(Node)。

n个结点(ai的存储映像)链结成一个链表,即为线性表(a1a2...an)的链式存储结构,因为此链表的每个结点中只包含一个指针域,所以叫单链表。

//线性表的单链表存储结构(线性链表)typedef struct LNode {     ElemType          data;     struct LNode     *next;}LNode, *LinkList;void Creat_LinkList(LinkList *L, int n);//创建以L为指向头结点指针的单链表int Length_LinkList(LinkList L);ElemType GetElem_LinkList(LinkList L, int i);//返回指定位置元素Status Insert_LinkList(LinkList *L, int i, ElemType e);//插入元素Status Delete_LinkList(LinkList *L, int i);//删除元素Status Clear_LinkList(LinkList *L);//单链表的整表删除


2、头指针:链表中第一个结点的存储位置叫做头指针。

Ø 头指针是指链表指向第一个结点的指针,若连标有头结点,则是指向头结点的指针。

Ø 头指针具有标识作用,所以常以头指针冠以链表的名字。

Ø 无论链表是否为空,头指针均不为空,头指针是链表的必要元素。

3、头结点:有时为了更加方便地对链表进行操作,会在单链表的第一个结点前附设一个结点,成为头结点。其数据域可以不存储任何信息,也可以存储如线性表的长度等附加信息;指针域为指向第一个结点的指针。

Ø 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义。

Ø 有了头结点,队在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了。

Ø 头结点不一定是链表必须要素。

4、单链表的存取数据时间复杂度为O(1),插入删除元素时间复杂度为O(n)。

//创建以L为指向头结点指针的单链表,尾插法void Creat_LinkList(LinkList *L, int n) {     int i;     LinkList p, q;     *L = (LinkList)malloc(sizeof(LNode));  //先建立一个带头结点的单链表,即初始化头结点     (*L)->next = NULL;    //数据域无效,指针域为空     if(!(*L)) exit(OVERFLOW);     p = *L;    //p为指向尾部的结点     for(i=0; i<n; i++) {          q = (LinkList)malloc(sizeof(LNode));  //生成新结点          scanf("%d", &q->data);          p->next = q;      //将表尾终端结点的指针指向新结点          p = q;            //将当前的新结点定义为表尾终端结点     }     p->next = NULL;        //表示当前链表结束,很重要}//返回链表长度int Length_LinkList(LinkList L) {    int len = 0;    LinkList p = L->next;    while(p) {        ++len;        p = p->next;    }    return len;}//返回指定位置元素ElemType GetElem_LinkList(LinkList L, int i) {     LinkList p = L->next;     int j = 0;     while(p&&j<i) {        //寻找第i个结点          p = p->next;          ++j;     }     if(p && j<=i)  return p->data;     return ERROR;}//插入元素Status Insert_LinkList(LinkList *L, int i, ElemType e) {     int j = 0;     LinkList p, q;     p = *L;     while(p && j<i) {         //寻找第i-1个结点          p = p->next;          ++j;     }     if(!p || j>i)  return ERROR;           //第i个结点不存在     q = (LinkList)malloc(sizeof(LNode));   //生成新结点     q->data = e;     q->next = p->next;     p->next = q;     return OK;}//删除并返回元素ElemType Delete_LinkList(LinkList *L, int i) {    int j = 0;    LinkList p, q;    ElemType e;    p = *L;    while(p->next && j<i) { //遍历寻找第i-1个结点        p = p->next;        ++j;    }    if(p->next && j<=i) {   //第i个结点不存在        q = p->next;        p->next = q->next;        e = q->data;        free(q);        return e;    }    return ERROR;}//将L重置为空表,不删除头结点Status Clear_LinkList(LinkList *L) {    LinkList p, q;    p = (*L)->next;     //p指向第一个结点    while(p) {          //没到表尾        q = p->next;        free(p);        p = q;    }    (*L)->next = NULL;  //头结点指针域为空    return OK;}


静态链表

1、用数组描述的链表叫做静态链表(游标实现法)。

对于一些没有指针的语言(如BasicFortran等早期的高级编程语言),无法实现单链表结构。

2、让数组的元素由两个数据域组成,即datacur

data:用来存放数据元素。

cur:相当于单链表中的next指针,存放该元素的后继在数组中的下标(cur称为游标)。

//线性表的静态单链表存储结构typedef struct{     ElemType   data;   //游标(指示器cur)代替指针指示节点在数组中的相对位置     int        cur;    //数组的第零分量可看成头结点,其指针域指示链表的第一个结点}component, StaticLinkList[MAXSIZE];//数组第一个元素的cur存放备用链表第一个结点的下标;最后一个元素的cur存放链表第一个元素的下标,相当于头结点//即备用链表以0为头结点,MAXSIZE-1为尾结点;链表以MAZSIZE-1为头结点,以0为尾结点void Init_StaticLinkList(StaticLinkList space);         //初始化int Length_StaticLinkList(StaticLinkList L);            //返回元素个数ElemType GetElem_StaticLinkList(StaticLinkList L, int i);   //返回指定元素Status Insert_StaticLinkList(StaticLinkList L, int i, ElemType e); //插入元素ElemType Delete_StaticLinkList(StaticLinkList L, int i);   //删除元素int Malloc_StaticLinkList(StaticLinkList space);        //分配存储空间void Free_StaticLinkList(StaticLinkList space, int k);  //释放内存//将一维数组space中各分量链成一个备用链表,space[0].cur为头指针, "0"表示空指针void Init_StaticLinkList(StaticLinkList space) {    int i;    for(i=0; i<MAXSIZE-1; i++) space[i].cur = i+1;    space[MAXSIZE-1].cur = 0;}//返回静态链表的长度(即元素个数)int Length_StaticLinkList(StaticLinkList L) {    int len = 0;    int i = L[MAXSIZE-1].cur;   //MAXSIZE-1为头结点,i为头指针    while(i) {        i = L[i].cur;        ++len;    }    return len;}//返回指定元素ElemType GetElem_StaticLinkList(StaticLinkList L, int i) {    int j = 0;    int p = L[MAXSIZE-1].cur;    while(p && j<i) {       //寻找第i个元素        p = L[p].cur;        ++j;    }    if(p && j<=i) return L[p].data;    return ERROR;}//插入元素Status Insert_StaticLinkList(StaticLinkList L, int i, ElemType e) {    int j, k, p;    j = 0;    p = MAXSIZE-1;    while(p && j<i) {       //寻找第i-1号结点        p = L[p].cur;        ++j;    }    if(!p || j>i) return ERROR;    k = Malloc_StaticLinkList(L);   //分配存储空间    L[k].data = e;    L[k].cur = L[p].cur;    L[p].cur = k;    return OK;}//删除并返回元素ElemType Delete_StaticLinkList(StaticLinkList L, int i) {    int j, p, k;    ElemType e;    j = 0;    p = MAXSIZE-1;    while(L[p].cur && j<i) {        //寻找第i号元素        p = L[p].cur;        ++j;    }    if(L[p].cur && j<=i) {        k = L[p].cur;        L[p].cur = L[k].cur;        e = L[k].data;        Free_StaticLinkList(L, k);  //释放内存        return e;    }    return ERROR;}//若备用链表非空,则返回分配的结点下标,否则返回0;int Malloc_StaticLinkList(StaticLinkList space) {     int i = space[0].cur;  //当前数组第一个元素cur存的值     if(space[0].cur)       //space[0].cur == 0则表明备用链表为空        space[0].cur = space[i].cur;     return i;              //返回第一个备用存储空间的下标}//将下标为k的空闲结点回收到备用链表void Free_StaticLinkList(StaticLinkList space, int k) {     space[k].cur = space[0].cur;     space[0].cur = k;}

循 环 链 表

1、将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked)。

2、空循环链表仅由一个自成循环的头结点表示。

双 向 链 表

1、双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

 


0 0
原创粉丝点击