第二章:2线性表---单链表表示和实现

前言:

  为避免在使用线性表顺序存储结构的时,需插入和删除需大量移动元素的弊端。

  本节讨论线性表的另外一种表示方法---链式存储结构:

    由于它不要求逻辑上相邻的元素在物理位置上相邻,因此它对元素的插入和删除没有顺序结构所具有的弊端,但是也失去了顺序表随机存取的优点。

目录:

1.线性表的链式表示和实现

  1.1线性链表

    单链表(指针型线性链表)

    静态链表

  1.2循环链表

  1.3双向链表

正文:

1.线性表的链式表示和实现

  1.1线性链表

    线性链表的特点:

      是用一组任意的存储单元存储线性表的数据元素(存储单元可以连续,也可以不连续)。因此,为表示 数据元素 ai 和其直接后继元素 a(i+1)之间逻辑关系,对ai来说,除存储其本身的信息外,还需存储一个指向其直接后继的信息(即直接后继的存储位置)。这两部分信息组成数据元素ai的存储映像,称为结点。它包括两个域:其中存储数据元素信息的域称为数据域,存储后继元素位置的域称为指针域。

      指针域中存储的信息称作指针或链。n个结点链结成一个链表,即为线性表

        (a1, a2, ...., an)

      的链式存储结构。又由于此链表的每个节点中只包含一个指针域,故又称线性链表或单链表。

    链表的机构描述:

      整个链表的存取必须从头指针开始进行,头指针指示链表中的第一个结点的存储位置。同时,由于最后一个数据元素没有直接后继,则线性链表中最后一个结点的指针为 “空” (NULL),如下图所示:

    

    单链表可以由头指针唯一确定,在C语言中可用 “结构指针” 来描述。

    线性表的单链表存储结构:

    typedef struct LNode{

      ElemType   data;

      struct  LNode   *next;

    }LNode, *LinkList;

    假设L是LinkList型的变量,则L为单链表的头指针,它指向表中的第一个结点。若L为空(L=NULL),则所表示的单链表为“空”,表长度为 0;

    有时候,我们在第一个结点前面,附加一个结点,称之为头结点。头结点的数据域可以不存储信息,也可存储链表长度等信息,头结点的指针域存储指向第一个结点的之指针。如下图:a中 头结点指向第一个结点,   b中头结点的指针域为NULL

      

     在单链表结点的创建和删除的时候,我们会用到C语言中的两个标准函数 malloc 和free。

    假设 P 和 q 是LinkList 型的变量,则执行 p=(LinkList)malloc(sizeof(LNode))的作用是由系统生成一个LNode型的结点,同时将该结点的起始位置复制给指针变量 p ;反之,执行 free(q) 的作用是由系统回收一个LNode型的结点,回收后的空间可以备作再次生成结点时用。

     每个单链表占用的空间都不需要预先分配,而是应需求通过 malloc 即时生成。

    代码实现:

#include<stdio.h>
#include<stdlib.h>

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值是函数结果状态码
typedef int Status;

typedef int ElemType;
typedef struct LNode{
    ElemType data;                //单链表中结点的数据域
    struct LNode *next;            //结点的指针域
}LNode,*LinkList;


//为单链表L,创建n个元素
void CreateList_L(LinkList L,int n){
    //初始化头结点
    L=(LinkList)malloc(sizeof(LNode));                        //生成头结点
    L->next=NULL;
    for(int i=0;i<n;i++){
        LNode *q=(LinkList)malloc(sizeof(LNode));            //创建新结点
        
        scanf("%d",&q->data);                                    //输入元素
        q->next=L->next;                                    //插入到表头
        L->next=q;
    }    
}

//在单链表中,取得第i个位置的值必须从头开始找起,因为每个结点元素的位置信息只能通过其直接前继结点确定
//获取L 中第i个元素的值,并用 e 返回。
Status GetElem_L(LinkList &L,int i,ElemType &e){
    //此处L为带有头结点的单链表
    LNode * q;
    q=L->next;                    //p指向第一个结点
    int j=1;
    while(q&&j<i){
        //移动指针的错误写法:q++; 因为在链式存储结构中存储地址不一定是连续的
        q=q->next;
        j++;
    }    
    if(j!=i)
        return ERROR;
    e=q->data;
    return OK;
}//时间复杂度为 O(n)


//插入元素,在L中第i个位置之前插入数据 e
Status ListInsert_L(LinkList &L,int i,ElemType e){
    //1.找到指向 第 i-1 处元素的指针
    LNode * q;
    q=L;                                //    q指向头结点
    int j=0;
    while(q&&j<(i-1)){
        q=q->next;                        //后移结点
        j++;
    }
    
    //正确j的可能值:0,(i-1),  
    if(!q||i<1)                        //1.q为NULL(i大于表长+1)  2.i不能小于1
        return ERROR;
    LNode * s;
    s=(LinkList)malloc(sizeof(LNode));    //生成新节点        
    s->data=e;                            //新结点数据域inti
    s->next=q->next; q->next=s;            //插入新结点    
    return OK;
}//时间复杂度为 O(n)


//删除元素,在L中删除位置为i的元素,并用将返回值存储在e中。
Status ListDelete_L(LinkList &L,int i,ElemType &e){
    //1.找到指向 第 i-1 处元素的指针
    LNode * q;
    q=L;                                //    q指向头结点
    int j=0;
    while(q&&j<(i-1)){
        q=q->next;                        //后移指针
        j++;
    }
    if(!(q->next)||i<1)                        //1.q->next不能为NULL(i位置不存在)  2.i不能小于1
        return ERROR;
    e=q->next->data;                    //将被删除的值保存在e中
    LNode * tem=q->next;                //保存 将被删除元素的坐在位置
    q->next=q->next->next;                //删除元素
    free(tem);                            //释放被删除元素占用的内存空间
    return OK;
}//时间复杂度为 O(n)


//合并两个非递减的单链表,使得合并后的单链表仍为非递减
Status MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){
    LNode *qa=La->next;                //指向La 表的第一个元素
    LNode *qb=Lb->next;                //指向Lb 表的第一个元素
    LNode *qc=Lc;
    while(qa&&qb){                        //如果都没有达到单链表的末尾
        if(qa->data<=qb->data){
            qc->next=qa;
            qa=qa->next;
        }else{
            qc->next=qb;                //增加Lc表的数据元素
            qb=qb->next;                    //Lb指针后移
        }
        qc=qc->next;                    //qc指向Lc的最后一个元素
    }
    //Lc的最后一个元素直接指向没有比较完的链表
    if(qa)
        qc->next=qa;
    if(qb)
        qc->next=qb;
    //释放头结点
    free(La);
    free(Lb);

    return OK;
}//链表进行合并时,不需要新建表的结点空间,而是将La 和Lb 中结点按新的关系连接起来

void printAllValues(LinkList &L){
    if(!L->next)
        printf("%s ","此表为空但链表");
    LNode *q;
    q=L;
    while(q->next){
        q=q->next;                        //指针后移
        printf("地址:%p",q);
        printf("值:%d ",q->data);
    }
}//时间复杂度为 O(n)


void main(int argc, char *argv[]){
    LNode *L;
    L=(LinkList)malloc(sizeof(LNode));    //生成头结点
    L->next=NULL;
    printf("%s ","执行插入操作...");
    ListInsert_L(L,1,9);
    ListInsert_L(L,1,7);
    ListInsert_L(L,1,4);
    ListInsert_L(L,1,2);
    printAllValues(L);

    ElemType e;
    
    GetElem_L(L,2,e);
    printf("获取第2个元素为:%d ",e);

    printf("%s ","执行删除操作...");
        
    if(ListDelete_L(L,3,e)==1)
        printf("%s ","删除成功");
    printAllValues(L);
    printf("删除的元素为:%d ",e);

    LNode *La;
    La=(LinkList)malloc(sizeof(LNode));    //生成头结点
    La->next=NULL;
    printf("%s ","插入La...");
    ListInsert_L(La,1,9);
    ListInsert_L(La,1,7);
    ListInsert_L(La,1,4);
    ListInsert_L(La,1,2);
    printAllValues(La);


    LNode *Lb;
    Lb=(LinkList)malloc(sizeof(LNode));    //生成头结点
    Lb->next=NULL;
    printf("%s ","插入Lb...");
    ListInsert_L(Lb,1,11);
    ListInsert_L(Lb,1,9);
    ListInsert_L(Lb,1,5);
    ListInsert_L(Lb,1,1);
    printAllValues(Lb);


    LNode *Lc;
    Lc=(LinkList)malloc(sizeof(LNode));    //生成头结点
    Lc->next=NULL;
    MergeList_L(La,Lb,Lc);
    printf("%s ","合并后的Lc...");
    printAllValues(Lc);
    
}

    运行结果:

      

原文地址:https://www.cnblogs.com/ahguSH/p/6173641.html