C语言链表——新手教程2

之前的文章里,我已经对链表和数组的区别做了较为详细的比较(具体连接:https://blog.csdn.net/Mr_J0304/article/details/79678873

下面实际操作一下,看看链表到底该怎么操作

链表是一种最简单常见的数据结构,常见操作包括建立链表,删除节点,删除链表,查找节点,置换节点,插入节点等,下面依次对这些操作进行处理

1.建立链表

如果是以C语言入门编程语言,对计算机的底层硬件结构有过多了解的话,其实是容易在内存地址这些概念上出现困惑的,这些困惑的具体表现就在指针上,而链表的建立恰恰离不开指针。

借用上一篇文章的概念:如果我们需要存放擎天柱的新款激光炮,而柜子里又放不下,这时候,我们需要向老妈口头申请一个放激光炮的空间(比如书架上),然后把上一个零件附带的纸条改为“下一个零件的位置在书架第二层”,最后把激光炮安置好,再继续向老妈申请下一个放零件用的空间

申请空间的时候,你只是向管理员(老妈/操作系统)提出了一个“需要一个激光炮那么大空间”的请求,管理员会根据实际情况,给你“随机”分配一个地址,为了记住这个地址,你需要一张小纸条(指针)记下来,防止防止错地方,导致最后好好的手办被老妈当成垃圾给扔了(放错地址后,数据会在后面的运算中被冲洗掉)

解释了这么多,我们来看具体的操作:

首先是定义链表的节点

struct ListNode{
    int data;//存放数据,即擎天柱的零件
    struct ListNode *next;//记录下一个存放地址的小纸条
};

然后开辟单个节点空间

struct ListNode *Create_Point()//创建一个节点
{
    struct ListNode *p;//准备一张记录空间地址的小纸条
    p=(struct ListNode*)malloc(sizeof(struct ListNode));//申请一个存放零件用的空间
    p->next=NULL;//记住,时刻要标记,新申请的零件是最后一个零件,后面没有新的存在,否则搜寻会出问题
    return p;//将地址返回
}

将单个节点空间组合为一个完整链表

struct ListNode *Create_List()//创建链表
{
    int number;//读取数据
    struct ListNode *head,*p1,*p2;//head头结点只作为开头,不存放数据,有利于后期代码书写的整洁性和易维护性,*p1,*p2用于开辟新的节点
    head=Create_Point();
    p2=head;
    scanf("%d",&number);
    while(number!=-1)//以-1作为停止录入数据的信号
    {
        p1=Create_Point();//正式开辟空间存放零件
        p1->data=number;
        p2->next=p1;//修改上一个零件所带的纸条,提示下一个零件的位置位于这个新开辟的空间里
        p2=p2->next;//因为零件在不停的增加,我们的”最后一个零件“所在的地点也是要不停更新为这个新开辟空间的
        scanf("%d",&number);//准备下一个要存放的零件
    }
    return head;//录入完成后,将这个零件的系列开头的纸条返回到自己手里,不然整个玩具都会迷失在8000平米的豪宅里
}

2.打印链表(查找链表中某个节点)

这两个任务其实非常相似,能够把链表完整打印一遍,就已经说明能够查找元素了

如果搞懂了创建链表,那打印是一件非常简单的事情了

void Print_List(struct ListNode *head)//打印以head作为开头的纸条标记的零件系列,稍作修改即可改为查找数据是否存在的函数
{
    struct ListNode *temp=head->next;//头节点没有存储数据,所以不需要打印,直接从下一个开始
    while(temp)
    {
        printf("%d\t",temp->data);
        temp=temp->next;//读取这个零件后,要及时去下一个零件的地点读取再下一个零件的位置
    }//这个循环的条件如果改为:temp->data!=number,即可变为搜索数据的存在
    printf("\n");
}

3.指定位置插入节点

这一步需要对链表的本质有所熟悉,我画一个图示意一下

按照图示,我们写出代码

void Insert_Point(struct ListNode *head, int number,int position)//在链表中第position个节点之后插入一个数据number
{
    struct ListNode *p1=head;
    struct ListNode *p2=head->next;//前后指针,作用继续看后面的操作
    for(int i=0;i<position;i++)//循环结束后,p1恰好位于要插入的节点,p2位于p1后的那个节点上
    {
        p1=p1->next;
        p2=p2->next;
    }
    //开始插入数据
    struct ListNode *temp=Create_Point();//先申请存放新数据的空间
    temp->data=number;
    p1->next=temp;
    temp->next=p2;//记住,要把p1上零件的纸条修改为新零件所在的空间地址,新零件的纸条修改为原来p2所在数据的地址,这样才能保证链表连贯,不至于断开
}

4.删除指定位置的节点

有了第三点作为参考,第四个任务显得轻松多了

void Cancel_Point(struct ListNode *head,int position)//删除位于第position个位置上的节点,这里不考虑position溢出的情况
{
    struct ListNode *p1=head;
    struct ListNode *p2=head->next;
    for(int i=1;i<position;i++)
    {
        p1=p1->next;
        p2=p2->next;
    }
    p1->next=p2->next;
    free(p2);
}

5.删除整个链表

这里会有不少人犯下经验性错误,只删除了头结点就认为完事了

但你想啊,你只不过是把零件链条的第一张纸条丢了,但是零件可依旧占据着整个空间呢,你可没有上报释放这些空间!

小数据倒无所谓,但是百万个数据可是相当吃内存的啊!所以节点要一个个释放

void Cancel_List(struct ListNode *head)
{
    struct ListNode *temp1=head,*temp2=head->next;
    while(temp2)
    {
        free(temp1);
        temp1=temp2;
        temp2=temp2->next;
    }
    free(temp1);
    printf("Have canceled");
}

6.交换任意两个位置的节点

这个是最难的一个操作

附上代码

void Exchange_Point(struct ListNode *head,int a,int b)//交换位于a,b位置上的两个节点
{
    struct ListNode *a1,*a2,*b1,*b2;
    a1=b1=head;
    a2=b2=head->next;
    for(int i=1;i<a;i++)
    {
        a1=a1->next;
        a2=a2->next;
    }
    for(int i=1;i<b;i++)
    {
        b1=b1->next;
        b2=b2->next;
    }
    a1->next=b2;
    struct ListNode *temp=a2->next;
    a2->next=b2->next;
    b2->next=temp;
    b1->next=a2;
}

鉴于语言描述略显苍白,我直接上图解释:

最后附上完整代码以供参考,代码没有处理诸如无效位置,溢出等问题,仅供新手向理解

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

struct ListNode{
    int data;//存放数据,即擎天柱的零件
    struct ListNode *next;//记录下一个存放地址的小纸条
};

struct ListNode *Create_Point()//创建一个节点
{
    struct ListNode *p;//准备一张记录空间地址的小纸条
    p=(struct ListNode*)malloc(sizeof(struct ListNode));//申请一个存放零件用的空间
    p->next=NULL;//记住,时刻要标记,新申请的零件是最后一个零件,后面没有新的存在,否则搜寻会出问题
    return p;//将地址返回
}

struct ListNode *Create_List()//创建链表
{
    int number;//读取数据
    struct ListNode *head,*p1,*p2;//head头结点只作为开头,不存放数据,有利于后期代码书写的整洁性和易维护性,*p1,*p2用于开辟新的节点
    head=Create_Point();
    p2=head;
    scanf("%d",&number);
    while(number!=-1)//以-1作为停止录入数据的信号
    {
        p1=Create_Point();//正式开辟空间存放零件
        p1->data=number;
        p2->next=p1;//修改上一个零件所带的纸条,提示下一个零件的位置位于这个新开辟的空间里
        p2=p2->next;//因为零件在不停的增加,我们的”最后一个零件“所在的地点也是要不停更新为这个新开辟空间的
        scanf("%d",&number);//准备下一个要存放的零件
    }
    return head;//录入完成后,将这个零件的系列开头的纸条返回到自己手里,不然整个玩具都会迷失在8000平米的豪宅里
}

void Print_List(struct ListNode *head)//打印以head作为开头的纸条标记的零件系列,稍作修改即可改为查找数据是否存在的函数
{
    struct ListNode *temp=head->next;//头节点没有存储数据,所以不需要打印,直接从下一个开始
    while(temp)
    {
        printf("%d\t",temp->data);
        temp=temp->next;//读取这个零件后,要及时去下一个零件的地点读取再下一个零件的位置
    }//这个循环的条件如果改为:temp->data!=number,即可变为搜索数据的存在
    printf("\n");
}

void Insert_Point(struct ListNode *head, int number,int position)//在链表中第position个节点之后插入一个数据number
{
    struct ListNode *p1=head;
    struct ListNode *p2=head->next;//前后指针,作用继续看后面的操作
    for(int i=0;i<position;i++)//循环结束后,p1恰好位于要插入的节点,p2位于p1后的那个节点上
    {
        p1=p1->next;
        p2=p2->next;
    }
    //开始插入数据
    struct ListNode *temp=Create_Point();//先申请存放新数据的空间
    temp->data=number;
    p1->next=temp;
    temp->next=p2;//记住,要把p1上零件的纸条修改为新零件所在的空间地址,新零件的纸条修改为原来p2所在数据的地址,这样才能保证链表连贯,不至于断开
}

void Cancel_Point(struct ListNode *head,int position)//删除位于第position个位置上的节点,这里不考虑position溢出的情况
{
    struct ListNode *p1=head;
    struct ListNode *p2=head->next;
    for(int i=1;i<position;i++)
    {
        p1=p1->next;
        p2=p2->next;
    }
    p1->next=p2->next;
    free(p2);
}

void Exchange_Point(struct ListNode *head,int a,int b)//交换位于a,b位置上的两个节点
{
    struct ListNode *a1,*a2,*b1,*b2;
    a1=b1=head;
    a2=b2=head->next;
    for(int i=1;i<a;i++)
    {
        a1=a1->next;
        a2=a2->next;
    }
    for(int i=1;i<b;i++)
    {
        b1=b1->next;
        b2=b2->next;
    }
    a1->next=b2;
    struct ListNode *temp=a2->next;
    a2->next=b2->next;
    b2->next=temp;
    b1->next=a2;
}

void Cancel_List(struct ListNode *head)
{
    struct ListNode *temp1=head,*temp2=head->next;
    while(temp2)
    {
        free(temp1);
        temp1=temp2;
        temp2=temp2->next;
    }
    free(temp1);
    printf("Have canceled");
}

int main()
{
    struct ListNode *head;
    head=Create_List();
    Print_List(head);
    Exchange_Point(head, 2, 5);
    Print_List(head);
    Insert_Point(head, 100, 2);
    Print_List(head);
    Cancel_Point(head, 3);
    Print_List(head);
    Cancel_List(head);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值