初学者怎么理解链表

写在最前

阅读这篇文章需要什么知识储备:
结构体、指针的一些知识、malloc
涉及开发环境:CodeBlocks 纯C

1、什么是链表

首先聊聊数组,数组是同类型的一组数据的集合,但首先要定义数组的大小,比如:

	int num[10];

这就定义了一个int类型的数组num,里面可以存放10个int的数据,根据数组下标进行操作。

这时会出现一个问题,数组大小不够或者是浪费?这时就出现了一种新的数据存储方式,链表,链表的一个个节点是一个结构体,可以把结构体想象成一个一个,里面有一根线引出来,为了和下一个建立联系。因为这个点是一个单独的东西,所以各个点之间的地址不一定是连续的,而数组的地址一定是连续的,这个充分利用了内存资源,没有必要提前划分出一大块区域给数组用。这是链表的一大优势。

下面看结构体定义,举一个简单例子:

	typedef struct Node{
	    int year;
	    int month;
	    int day;
	    struct Node *next;
	}Node,*pNode;

Node相当于给这个结构体取了名字,同样的 *pNode 这个整体也是给结构体取了个名字,但注意,pNode就是一个指向结构体的指针了。结构体里面的那根线就是 *struct Node nextnext也是一个指向 struct Node 的指针,也就是指向结构体的指针。好,点 和 线 都有了,下面开始串起来。

2、第一个链表

直接上最难的串法:

#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
    int year;
    int month;
    int day;
    struct Node *next;
}Node,*pNode;

void create(pNode phead){
    pNode ptail = phead;//跟踪整个链表的最后一个节点
    while(1){
        pNode pnew = (pNode)malloc(sizeof(Node));//新申请一块内存,也就是一个新的“点”
        scanf("%d",&pnew->year);//这里输入数据
        if(pnew->year == -1)
            break;
        scanf("%d",&pnew->month);
        scanf("%d",&pnew->day);
        //开始串起来
        ptail->next = pnew;
        ptail = pnew;
        ptail->next = NULL;
    }
}

void traverse(pNode  phead){
    pNode p = phead->next;
    while(p!=NULL){
        printf("%d-%d-%d\n",p->year,p->month,p->day);
        p = p->next;
    }
}

int main()
{
    pNode phead = (pNode)malloc(sizeof(Node));
    phead->next = NULL;
    create(phead);
    traverse(phead);
    return 0;
}

我们约定链表的头节点不存储数据,只做标识作用,用来找到整个链表。
所以这个头节点一定不能改变,这里输入三个数代表年月日,如果年输入-1表示不继续创建节点。

每次准备输入新数据时,一定要重新malloc一块新的Node地址出来。

然后把数据输入进去,输入完成之后,这个点已经只做好了,因为ptail一直是最后一个点(先这个认为,后面会解释怎么做到的),所以链的时候只需ptail->next=pnew,即可,注意,这里还没完,说好的ptail是最后一个节点,所以这里还要ptail=pnew,这样ptail才真的是最后一个,那一开始的时候呢?ptail=phead,这里ptail也是最后一个节点啦!所以,这里的ptail始终指向的是最后一个节点。这里还需要注意!!patil->next=NULL;这一步操作是必须的,后面比如遍历的时候是根据当前节点是否是NULL来决定的,如果不手动把patilnext赋值NULL,会出现一个意想不到的错误!!这样整个流程其实已经走完了,phead始终没有变,只是后面多链了一些东西。

3、链表的其他操作

3.1增加链表(头插、尾插)

尾插:其实前面的创建链表的方式就是尾插,就是新的节点一直插在最后面。

头插:就是新的节点在最前面,这种插法简单,但是顺序反了,根据实际需要选择头插和尾插。

void create1(pNode phead){
    while(1){
        pNode pnew = (pNode)malloc(sizeof(Node));

        scanf("%d",&pnew->year);//这里输入数据
        if(pnew->year == -1)
            break;
        scanf("%d",&pnew->month);
        scanf("%d",&pnew->day);
        //开始串起来

        pnew->next = phead->next;
        phead->next = pnew;
    }
}

注意这里的插法不一样了,来的新节点应该插在phead的后面(因为phead不存储数据,所以它的下一个就是第一个节点了)。
首先pnew->next = phead->next
找到第一个然后赋值给pnew的下一个,这样pnew和大部队已经建立联系,但还没和phead联系上。再加上phead->next = pnew,注意这两步赋值操作顺序不能反,可以仔细思考如果反过来还能和它们建立联系吗?
这里需要注意的是生成phead的时候要手动phead->next = NULL,给末尾一个NULL的标识。

3.2删除链表

void delete(pNode phead){
    //简化操作,根据 年 来删除节点
    pNode p = phead->next;
    pNode pre = phead;
    int year;
    printf("输入年来删除\n");
    scanf("%d",&year);
    while(p != NULL){
        if(p->year == year){
            pre->next = p->next;
            break;
        }
        pre = p;
        p = p->next;
    }
}

这里需要注意的是,我们一直判断当前的这个节点的年份是不是我们想要删除的那个(删除条件)如果是就删除,可是找不到前面的节点了(哭!如果你说从头遍历,判断下一个是不是要删除的节点,那。。。很“聪明”!)
所以我们需要随时记录前一个节点是什么,所有有pre临时变量。当然这里要规范,因为有数据的是phead->next,所以肯定从它开始找,这时它的前一个是phead,所以pre赋初始值是phead,如果删除的是第一个就有作用了!!!
最后附上完整代码吧:

#include <stdio.h>
#include <stdlib.h>
typedef struct Node{
    int year;
    int month;
    int day;
    struct Node *next;
}Node,*pNode;

void create(pNode phead){
    pNode ptail = phead;//跟踪整个链表的最后一个节点
    while(1){
        pNode pnew = (pNode)malloc(sizeof(Node));//新申请一块内存,也就是一个新的“点”
        scanf("%d",&pnew->year);//这里输入数据
        if(pnew->year == -1)
            break;
        scanf("%d",&pnew->month);
        scanf("%d",&pnew->day);
        //开始串起来
        ptail->next = pnew;
        ptail = pnew;
        ptail->next = NULL;
    }
}

void create1(pNode phead){
    while(1){
        pNode pnew = (pNode)malloc(sizeof(Node));

        scanf("%d",&pnew->year);//这里输入数据
        if(pnew->year == -1)
            break;
        scanf("%d",&pnew->month);
        scanf("%d",&pnew->day);
        //开始串起来

        pnew->next = phead->next;
        phead->next = pnew;
    }
}

void traverse(pNode  phead){
    pNode p = phead->next;
    while(p!=NULL){
        printf("%d-%d-%d\n",p->year,p->month,p->day);
        p = p->next;
    }
}

void delete(pNode phead){
    //简化操作,根据 年 来删除节点
    pNode p = phead->next;
    pNode pre = phead;
    int year;
    printf("输入年来删除\n");
    scanf("%d",&year);
    while(p != NULL){
        if(p->year == year){
            pre->next = p->next;
            break;
        }
        pre = p;
        p = p->next;
    }
}

int main()
{
    pNode phead = (pNode)malloc(sizeof(Node));
    phead->next = NULL;
    create1(phead);
    traverse(phead);
    delete(phead);
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值