15-单链表的基本运算实现

1. 单链表的数据抽象类型

  对于单链表来说,它是线性表的一种存储结构,当我们把单链表的数据对象,数据关系,数据操作描述出来后,然后就可以考虑基本操作的实现了。

ADT ——线性表:

ADT List
{
    数据对象:
    D = {ai | ai∈ElemType, i=1,2,…,n, n≧0 } //ElemType为类型标识符
    数据关系:
    R = {<ai-1, ai> | ai-1, ai∈D, i=2,3,…,n }
    数据操作:
    (1)初始化线性表InitList(&L):构造一个空的线性表L
    (2)销毁线性表DestroyList(&L):释放线性表L占用的内存空间
    (3)判线性表是否为空表ListEmpty(L):若L为空表,则返回真,否则返回假
    (4)求线性表的长度ListLength(L):返回L中元素个数
    (5)输出线性表DispList(L):当线性表L不为空时,顺序显示L中各节点的值域
    (6)求线性表L中指定位置的某个数据元素GetElem(L,i,&e):用e返回L中第 i 个元素的值
    (7)查找元素LocateElem(L,e):返回线性表L中第1个与e相等的序号,找不到返回08)插入元素ListInsert(&L, i, &e):在线性表L中的第i个位置插入元素e;
    (9)删除元素ListDelete(&L, i, &e):在线性表L中删除第i个元素,有e返回删除的值;
}

2. 单链表的基本运算实现

1.初始化线性表InitList(L)

  InitList()运算用于建立一个空的单链表,即创建一个头节点,这个头结点一般不存储数据。

这里写图片描述

void InitList(LinkList *&L)
{   
    //创建头结点,并通指针L指向头结点的地址
    L=(LinkList *)malloc(sizeof(LinkList));
    L->next=NULL;
}

2.销毁线性表DestroyList(L)
  DestroyList()运算用于释放单链表L占用的内存空间。即逐一释放全部节点的空间。

这里写图片描述

   在开始时,定义两个指针,指针pre记录销毁的节点,指针p用于记录下一个将要销毁的节点。

这里写图片描述

  指针pre在释放完当前节点时,指针p不断的向后移动去指向下一个节点,当全部节点释放完毕时,p指向就会为NULL,pre就可以把指向的尾节点释放掉。

void DestroyList(LinkList *&L)
{
    //相当于图1中的初始操作
    LinkList *pre=L,*p=L->next;
    //后面所有的代码相当于图2中的操作
    while (p!=NULL)
    {
        //释放节点的空间
        free(pre);
        //移动到下一个要释放的节点位置
        pre=p;
        //p指向下一个节点
        p=pre->next;
    }
    //释放尾节点的空间
    free(pre);
}

3.判线性表是否为空表ListEmpty(L)

判断线性表是否为空,如下图所示:

这里写图片描述

  第一种情况线性表是在只有一个头结点的情况,且头结点的next指针域为NULL,没有存储有效数据,所以这时线性表为空。

  第二种情况线性表是在头结点的next指针域不为NULL,说明线性表中有多个节点,因此线性表就不为空。

  无论是对于第一种情况,还是第二种情况,我们只需判断头结点的next指针域即可。

bool ListEmpty(LinkList *L)
{
    //根据头结点next指针域来判断线性表是否为空
    return(L->next==NULL);
}

4.求线性表的长度ListLength(L)

  ListLength()运算用于返回线性表的长度,也就是节点的个数。

这里写图片描述

  在计算线性表的长度时,定义一个指针p指向链表的头结点位置,再定义一个变量n记录节点的个数。

这里写图片描述

  然后p往后移动,只要p指向的节点的next指针域不为NULL,说明后面还有节点,那么n就加1,当p的next指针域为空时,就可以返回线性表的长度n了。

int ListLength(LinkList *L)
{
    //n记录节点的个数
    int n=0;
    //指针p用于记录节点
    LinkList *p=L;
    //只要next指针域不为NULL,说明还有节点
    while (p->next!=NULL)
    {
        n++;
        //指针p移动到下一个节点
        p=p->next;
    }
    //最后返回线性表的长度
    return n;
}

5.输出线性表DispList(L)

DispList()运算是用于输出链表中的每个节点的数据域的值。

void DispList(LinkList *L)
{
    //指针p直接指向第一个节点
    LinkList *p=L->next;
    //只要p不为NULL,说明还有节点
    while (p!=NULL)
    {
        //输出节点的数据域
        printf("%d ",p->data);
        //移动到下一个节点
        p=p->next;
    }
    printf("\n");
}

6.求线性表L中指定位置的某个数据元素GetElem(L,i,&e)

  GetElem()运算实现思路:在单链表L中从头开始找到第i个节点,若存在第i个数据节点,则将其data域值赋给变量e。

这里写图片描述

在开始时,j用于记录查找到第几个节点,然后指针p指向链表开头位置。

这里写图片描述

  指针p移动到第i个节点的位置时,此时j = i,并判断p是否为NULL,如果不为NULL,说明找到了第i个节点,那么就可以把第i个节点的数据域赋值给e,然后返回。

bool GetElem(LinkList *L,int i,ElemType &e)
{
    // j 用于记录查找到第几个节点
    int j=0;
    //指针p指向链表开头位置
    LinkList *p=L;
    //指针p移动到第i个节点的位置,j不能大于i
    while (j<i && p!=NULL)
    {
        //同时j要记录移动到第几个节点
        j++;
        //移动指针p
        p=p->next;
    }
    //如果p == NULL说明没有找到第i个节点,直接返回false
    if (p==NULL)
        return false;
    else
    {
        //否则就找到了第i个节点,同时把数据域的值赋值给e,返回true说明查找成功
        e=p->data;
        return true;
    }
}

7.按元素值查找LocateElem(L,e)

  LocateElem()运算的实现思路:从头开始找第1个值域与e相等的节点,若存在这样的节点,则返回位置,否则返回0。

这里写图片描述

  在开始时,先定义一个指针p指向链表的第一个有效节点,i = 1表示记录第一个有效节点的位置

这里写图片描述

  判断指针p的指针域是否为空,同时判断p的数据域是否为要查找的元素,如果没查找到指针p就移动到下一个节点。

int LocateElem(LinkList *L,ElemType e)
{
    int i=1;
    //同样还是要定义一个指针p指向第一个有效节点
    LinkList *p=L->next;
    //判断指针p的指针域是否为空,同时判断p的数据域是否为要查找的元素,不是p就往后移动
    while (p!=NULL && p->data!=e)
    {
        p=p->next;
        i++;
    }
    //如果p == NULL,说明没找到要查找的元素
    if (p==NULL)
        return (0);
    else
        //否则说明就找到了要查找的元素,返回其位置
        return (i);
}

8.插入数据元素ListInsert(&L,i,e)

  ListInsert()运算的实现思路:先在单链表L中找到第i-1个节点 p,若存在这样的节点,将值为e的节点s插入到其后。

这里写图片描述

在开始时,先定义指针p指向链表的开始位置,依次往后移动到要插入节点的位置

这里写图片描述

  这里要注意一点,因为我们要在第i个节点插入新节点,那么我们就需要找到第i-1个节点,在这个位置插入新节点,也就是说指针p要移动到第i-1个节点的位置。

这里写图片描述

  当找到第i-1个节点时(指针p移动到第i-1个节点),给新节点分配空间,然后指针s指向新节点,并给新节点的数据域赋值,然后新节点的next指针域指向第i个节点的地址,第i-1个节点的next指针域指向新节点,完成插入操作。

bool ListInsert(LinkList *&L,int i,ElemType e){
    //j用于记录i-1个节点
    int j=0;
    //定义指针p指向链表的开始位置,指针s用于指向要插入的新节点地址
    LinkList *p=L,*s;
    //指针p移动到i-1个节点的位置
    while (j<i-1 && p!=NULL){
        j++;
        p=p->next;
    }
    //如果p为NULL说明没找到第i-1个位置,超过了链表的长度
    if (p==NULL)
        return false;
    else{
        //指针s指向新节点
        s=(LinkList *)malloc(sizeof(LinkList));
        //新节点数据域赋值
        s->data=e;
        //新节点指向第i个节点
        s->next=p->next;
        //然后第i-1个节点的next指针域指向新节点
        p->next=s;
        return true;
    }
}

9.删除数据元素ListDelete(&L,i,&e)

  ListDelete()运算的实现思路:先找到第i-1个节点p,若存在这样的节点,且也存在后继节点,则删除该后继节点。

这里写图片描述

  跟插入数据元素一样,在开始时,先定义指针p指向链表的开始位置,依次往后移动到要删除节点的位置

这里写图片描述

  需要先找到删除位置,也就是找到第i-1个节点的位置,指针p指向第i-1个节点。

这里写图片描述

  在删除第i个节点时,指针q记录下第i个节点的数据域,再把指针p的next指针域指向第i+1个节点,同时把第i个节点的空间释放掉。

bool ListDelete(LinkList *&L,int i,ElemType &e){
    int j=0;
    //定义指针p指向链表的开始位置,指针q用于记录要删除的节点
    LinkList *p=L,*q;
    //移动指针p,直到指向第i-1个节点位置
    while (j<i-1 && p!=NULL) {
        j++;
        p=p->next;
    }
    //p为null说明没找到第i-1个节点,链表长度不足
    if (p==NULL)
        return false;
    else{
        //找到第i-1个节点,并让指针q记录第i个节点
        q=p->next;
        //注意:当找到第i-1个节点时,需要判断第i个节点是否存在
        if (q==NULL)
            return false;
            //记录下要删除的节点的数据域
        e=q->data;
        //把指针p指向第i+1个节点
        p->next=q->next;
        //释放第i个节点的空间
        free(q);
        return true;
    }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值