王道数据结构2.3-线性表的链式表示

单链表的定义

线性表的链式存储又称为单链表,单链表的结点结构如下所示:

data是数据域,用于存放数据,next为指针域,用于存放后继节点,因为有了next这个指针域,链表中的每一个结点才可以连接起来成为链表,但是同时因为next指针域只能指向后继节点,所以如果在单链表中寻找某个特定值,不可以直接进行寻找,而是需要从头结点开始依次遍历查找。

上图是带头结点的单链表,从上图可以发现,头结点是单链表的第一个元素结点,但是头结点的数据域可以不存放任何链表元素。这里注意需要对头结点和头指针进行区分:

不管链表是否带有头结点, 头指针始终指向链表的第一个结点,头结点是带头结点链表的第一个结点,用上图举例,上图就是带头结点单链表,第一个箭头就是头指针,它指向头结点,如果没有头结点,那么头指针就指向a1那个数据域。

头插法

头插法是指从一个空表开始,生成新节点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。如下图所示是用头插法构建单链表的方式。每次将s所指的结点插在最前端。第一步让s->next=L->next,将原来头结点的后继节点变成需要插入的结点的后继节点,第二步,让L->next=s,将s变成头结点的后继节点。

 从图示可以发现,采用头插法建立单链表,读入数据的顺序和生成的链表顺序是相反的,最先读入的数据是链表的最后一个元素,头插法建立单链表的算法如下:

LinkList CreatList(LinkList &L){
    //从表尾到表头逆向建立单链表L,每次均在头结点之后插入元素
    LNode *s;
    int x;
    L=(LinkList)malloc(sizeof(LNode));//创立头结点
    L->next=NULL;//初始为空链表
    scanf("%d",&x);//输入结点的值
    while(x!=9999){//输入9999表示结束
        s=(LNode*)nalloc(sizeof(LNode));//创建新的结点
        s->data=x;
        s->next=L->next;
        L->next=s;//将新的结点插入表中,L为指针
        scanf("%d",&x);
    }
    return i;
}

尾插法

尾插法如下图绿色所示,其中an是尾结点,am是需要插入的结点,从图中可以发现,尾插法每次插入结点都是将结点插入链表尾部,因此需要加入一个新的指针作为尾指针,始终指向尾结点。对比头插法和尾插法可以发现,尾插法因为每次读取的数据都放在最后,所以生成的链表顺序和读入元素的顺序一致。

 尾插法建立单链表的算法如下:

LinkList CreatList(LinkList &L){
    //从表头到表尾正向建立单链表L,每次均在表尾插入元素
    int x;//设元素类型为整型
    L=(LinkList)malloc(sizeof(LNode));
    LNode *s,*r=L;//r为表尾指针
    scanf("%d",&x);//输入结点的值
    while(x!=9999){
        s=(LNode*)malloc(sizeof(LNode));
        s->data=x;
        r->next=s;
        r=s;//r指向新的表尾结点
        scanf("%d",&x);
    }
    r->next=NULL;
    return r;
}

按序号查找结点值

在单链表中从第一个结点出发,顺指针next域逐个往下搜索,直到找到第i个结点为止,否则返回最后一个结点指针域NULL。

之前说了,因为链表当中每个结点只能指向他的后继节点,因此寻找链表中的某个值或者某个元素的位置,需要从表头依次遍历查找,这个算法的时间复杂度是O(n)

LNode *GetElem(LinkList L,int i){
    //本算法取出单链表L(带头结点)中第i个位置的结点指针
    int j=1;//计数,初始为1
    LNode *p=L->next;//头结点指针赋给P
    if(i==0)
        return L;//若i=0,则返回头结点
    if(i<1)
        return NULL;//若i无效,则返回NULL
    while(p&&j<i){//从第1个结点开始找,查找第i个结点
        p=p->next;
        j++;
    }
    return p;//返回第i个结点的指针,如果i大于表长,p=NULL,直接返回p即可
}

按值查找表结点

给定一个值,从单链表第一个结点开始不断往后,依次比较各节点元素的值和给定值,如果相等则返回该结点的指针,如果一直遍历到表尾还没有找到,则表示整个链表都没有这个结点,因此返回NULL,整个算法的时间复杂度同样为O(n)

LNode *LocateElem(LinkList L,ElemType e){
    //本算法查找单链表L(带头结点)中数据域值等于e的结点指针,否则返回NULL
    LNode *p=L->next;
    while(p!=NULL&&p->data!=e)//从第1个结点开始查找data域为e的结点
        p=p->next;
    return p;//找到后返回该结点的指针,否则返回NULL
}

插入结点

插入是将值为x的新结点插入到单链表的第i个位置上,先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后新结点。

算法首先调用按序号查找结点中的GetElem(L,i-1),查找第i-1个结点。假设返回的第i-1个结点为*p,然后令新结点*s的指针域指向*p的后继节点,再令结点*p的指针域指向新插入的结点*s,操作过程如下:

 删除结点操作

删除操作时将单链表的第i个结点删除。先检查删除位置的合法性,然后查找表中i-1个结点,即被删结点的前驱结点,再将其删除。假设结点*p为找到的被删节点的前驱结点,这时只需将p->next=q->next

实现删除结点的代码片段如下:

p=GetElem(L,i-1);//查找删除位置的前驱结点
q=p->next;//令q指向被删除结点
p->next=q->next;//将*q结点从链中“断开”
free(q);//释放结点的存储空间

 求链表长度

表长是不包括头结点的,因此带头结点和不带头结点的单链表求表长的操作会有些不同,同时对于不带头结点的单链表,当链表为空的时候需要单独处理。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

箬渊凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值