链表的理解与实现[数据结构]

一、链表的定义

n个节点离散分配,节点之间通过指针相连。除了首节点和尾节点之外,每个节点都只有一个前驱结点和一个后继节点。
如下图:
这里写图片描述
大家有没有发现,链表的结构很像一种交通工具,什么呢?
火车。(节点==车厢,指针==车厢间相连的绳索)

二、链表的实现

注意,这里实现的是链表中最简单的单链表。

1、创建链表

1.1 定义链表最基本的组成元素–节点

我们由上面链表的定义可以知道,链表的每个节点应该含有两部分:
1、每个节点本身存储的数据,我们假定data 为 int 类型;
2、当前节点指向下一个节点的指针。

好,我们根据以上两部分进行节点的定义:

/**
    NODE:节点类型
    PNODE:指向节点的指针
*/
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;
1.2创建链表

上面我们定义好了链表的节点,接下来我们就进入激动人心的一刻吧,创建属于你的链表!
上面讲了,链表的结构就像火车的结构,我们不妨以火车为例进行讲解吧!

1、创建火车头:

//PNODE是指向NODE节点的指针类型
PNODE pHead=(PNODE)malloc(sizeof(NODE));

完美!我们创造了一个火车头,并且使用pHead指向了这个火车头。
如下图:
这里写图片描述

2、创建火车尾部

    //PNODE是指向NODE节点的指针类型
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    //pTail是指向火车尾部的指针
    PNODE pTail=pHead;
    pHead->pNext=NULL;

由于现在只有一节车厢,所以pHead和pTail都指向这节车厢。
如下图:
这里写图片描述

3、添加车厢

//假设我们添加3节车厢
for(i=0;i<3;i++)
{
     printf("请输入第%d节车厢的数值\n",i+1);
     scanf("%d",&val);
     PNODE pNew=(PNODE)malloc(sizeof(NODE));
     pNew->data=val;
     pTail->pNext=pNew;
     pTail=pNew;
     pNew->pNext=NULL;
}

当i=0的时候,我们添加1节车厢,假设第1节车厢存储的值为1。
结构从
这里写图片描述

变为
这里写图片描述

上述pTail=pNew,使得pTail指向pNew指向的节点。

当i=1的时候,我们添加第2节车厢,假设第2节车厢存储的值为2。

这里写图片描述

当i=3的时候,同理添加第3节车厢。
如此,3节车厢就添加完了。

注意,pTail的作用是始终指向最后一节车厢,方便添加新车厢。
添加车厢的时候直接让pTail指向新的车厢,再让pTail指向新添加的车厢(最后一节车厢)。

总体代码:

//
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;
//创建链表
PNODE createList()
{
    int i,val,n;
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    PNODE pTail=pHead;
    pHead->pNext=NULL;
    printf("请输入链表长度:");
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        printf("请输入第%d个数值\n",i+1);
        scanf("%d",&val);
        PNODE pNew=(PNODE)malloc(sizeof(NODE));
        pNew->data=val;
        pTail->pNext=pNew;
        pTail=pNew;
        pNew->pNext=NULL;
    }
    //将指向火车头的指针返回
    return pHead;
}

2、遍历链表

2.1判断链表是否为空

我们要遍历一个链表,首先要判断链表是否为空。
链表空的话,我们就直接返回,不遍历。

/**
    返回1:链表为空
    返回0:链表不空
*/
//判断链表是否为空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
2.2遍历链表

如果链表不空的话,我们就进行如下遍历

PNODE p=pHead->pNext;
while(p!=NULL)
{
    printf("%d ",p->data);
    p=p->pNext;
}

我们定义一个指针p
PNODE p=pHead->pNext;使得p指向了第1节车厢
这里写图片描述

下面,只要p不为空(p!=NULL)
即p有所指向,那么就打印p所指向的车厢的里面的data值。
然后,p再指向下一节车厢(p=p->pNext)
这里写图片描述

循环上面步骤,直到p指向最后一节车厢
这里写图片描述

再循环,p指向第4节车厢。
唉?第4节车厢没了!
那么p==NULL了,退出while循环。至此,循环遍历完毕。

完整代码如下:

//判断链表是否为空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//遍历链表
void traverseList(PNODE pHead)
{
    if(isEmpty(pHead))
    {
        printf("链表为空\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        while(p!=NULL)
        {
            printf("%d ",p->data);
            p=p->pNext;
        }
        free(p);
    }
}

3、删除链表

假设p指向第1节车厢,q指向第2节车厢。
比如我们要删除第2节车厢。
如图:
这里写图片描述

即:
p->pNext=q->pNext;(p指向第1节车厢,q指向第2节车厢)
使第1节车厢的pNext指向第3节车厢,就删除了第2节车厢。

整体代码为:

//删除任意位置的元素
void deleteList(PNODE pHead,int pos)
{
    if(isEmpty(pHead))
    {
        printf("链表为空,不可删除\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        //注意这里的i=2(因为p指向的是要删除车厢的前一节车厢)
        int i=2;
        while(p!=NULL && i!=pos)
        {
            p=p->pNext;
            i++;
        }
        PNODE q=p->pNext;
        p->pNext=q->pNext;
        free(q);
    }
}

三、整体代码

#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
    int data;
    struct Node * pNext;
}NODE,*PNODE;
//创建链表
PNODE createList()
{
    int i,val,n;
    PNODE pHead=(PNODE)malloc(sizeof(NODE));
    PNODE pTail=pHead;
    pHead->pNext=NULL;
    printf("请输入链表长度:");
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        printf("请输入第%d个数值\n",i+1);
        scanf("%d",&val);
        PNODE pNew=(PNODE)malloc(sizeof(NODE));
        pNew->data=val;
        pTail->pNext=pNew;
        pTail=pNew;
        pNew->pNext=NULL;
    }
    return pHead;
}
//判断链表是否为空
int isEmpty(PNODE pHead)
{
    if(pHead->pNext==NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//遍历链表
void traverseList(PNODE pHead)
{
    if(isEmpty(pHead))
    {
        printf("链表为空\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        while(p!=NULL)
        {
            printf("%d ",p->data);
            p=p->pNext;
        }
        free(p);
    }
}
//删除任意位置的元素
void deleteList(PNODE pHead,int pos)
{
    if(isEmpty(pHead))
    {
        printf("链表为空,不可删除\n");
    }
    else
    {
        PNODE p=pHead->pNext;
        int i=2;
        while(p!=NULL && i!=pos)
        {
            p=p->pNext;
            i++;
        }
        PNODE q=p->pNext;
        p->pNext=q->pNext;
        free(q);
    }
}
int main()
{
    PNODE pHead=createList();
    bubbleSort(pHead);
    traverseList(pHead);
    return 0;
}

四、总结

从以上实现可以看出,链表的删除非常简便,但是查找很慢,很繁琐,需要从pHead一个个查起。

优点:插入删除简单,花费时间短。空间没有限制。
缺点:查询慢,比数组多一个pNext域,所占总空间大。

线性结构有数组和链表。数组和链表各有优缺点。没有哪一个最好。也就是没有最好,只有更好,看哪个更适合你的需求,就选择哪个结构。

本人初学,很多问题不甚明了。
如有错误或不当之处,敬请您不吝指正,
如果有问题需要探讨,烦请留言回复。

  • 13
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值