单向链表(C语言)-头插法创建链表、尾插法创建链表、链表基础操作

链表就是很多个结构体通过地址相关联(至少我目前了解到的是这样),每个结构体里由两部分:一是数据,二是下一个结构体的地址,于是就可以通过一个结构体获取下一个结构的地址,然后通过这个地址访问它的数据,再获取下一个地址,往复。形成一种链状数据结构,如图:

1. 先看我最先接触到的一种写法(无头&&尾插法),看题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node{                //节点结构体
    int data;
    struct node *next; //记录下一节点地址
};
int main()
{
    int n,i;
    struct node *head,*p,*q;    //定义节点结构体类型的指针
                                //*head记录链表起点指针,*p记录当前指针,*q记录上一次位置指针
    while(scanf("%d",&n)!=-1)
    {
        for(i=0;i<n;i++)        //建立n个节点的链表
        {

            p=(struct node *)(malloc(sizeof(struct node))); //动态申请空间得到空间地址,并将之强
                                                            //制类型转换为节点结构体类型指针,赋给p

            scanf("%d",&p->data);    //其实是&(p->data),这里不能再用".",因为p是地址


            if(i==0)                 //第一次操作,先把第一个地址给head,q保存上一次指针(目前在head上)                  
                head=q=p;
            else                     //后面的操作就是把上一节点的next赋值为当前p(第一次先跳过)
                q->next=p;

            p->next=NULL;            //把当前指针的next赋值为空,其实是为尾部next为空做贡献
            q=p;                     //记录一下本次循环得到的指针,p马上要被覆盖


        }


        p=head;
        while(p!=NULL)        //可以直接while(p)
        {
            if(i==n-1) printf("%d\n",p->data);
            else printf("%d  ",p->data);
            p=p->next;
        }

    }
}

 

 如果还不是不能理解,跟着循环自己画一下链状图应该就差不多了,逻辑和我给出的第一张图一样。

可以发现这种写法"无头",因为head所指向的节点上也有数据信息,相当于普通的节点。"有头"就是head直接指向的节点上只有next。其次是"尾插",因为新的节点总是跟在原来节点的后面,在尾部插入)。

2. 有头&&头插法(感觉头插法不是很好理解,最好先分析一下代码,画张图,否则可能还是不懂,画完再看注释):

看代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node{
    int data;
    struct node *next;
};
int main()
{
    int n,i;
    struct node *head,*p,*q;
    while(scanf("%d",&n)!=-1)
    {
        head=(struct node *)(malloc(sizeof(struct node)));    //先申请了一个头节点,没有赋数据
        head->next=NULL;     //这个千万别忘了,否则最后一个节点不是NULL
        for(i=0;i<n;i++)
        {
            p=(struct node *)(malloc(sizeof(struct node)));    //申请新节点
            scanf("%d",&p->data);        //节点赋数据
            p->next=head->next;          //新节点把head的next放到自己后面(自己的next上),再让head指向自己
            head->next=p;                //就相当于新节点把head的连接的那一条链甩到自己身后,然后自己贴上去
                                         //如果理解我的意思的话不用背也应该知道该怎么写了
        }


        p=head->next;
        while(p!=NULL) 
        {
            if(i==n-1) printf("%d\n",p->data);
            else printf("%d  ",p->data);
            p=p->next;
        }
    }
}

看的出来每一次有新数据都是插在head和head指向的p中间(头插),最后head指向的是最后一次生成的p,形成了head->p3->p2->p1这样的结构,按照正向输出的话整张链表是倒序的。

3. 链表的基本操作:

输出:上面一直在用,就不说了。

增加:记录当前指针p和上一次指针q,找到位置时q->next=t; t->next=p;即可,但注意有头与无头时头部和尾部插入的处理。

删除:记录当前指针p和上一次指针q,找到时q->next=p->next即可,注意有头和无头时删除头节点的处理。

修改:记录当前指针p,找到修改即可。

查询:记录当前指针p,判断是否符合条件。

排序:

//1. 冒泡排序,交换链表单元上的值

p=q=head;
while(p)
{
    q=p->next;
    while(q)
    {
        if(q->num<p->num)
        {
            t=q->num;
            q->num=p->num;
            p->num=t;
        }
        q=q->next;
    }
    p=p->next;
}

//2. 有序插入

//本题要求降序排列

head=(struct stu *)malloc(sizeof(struct stu));
head->next=NULL;
for(i=0;i<n;i++)
{
    p=(struct stu *)malloc(sizeof(struct stu));
    scanf("%u %s %lf %lf %lf",&p->num,p->name,&p->classa,&p->classb,&p->classc);
    p->sum=(p->classa+p->classb+p->classc);
    p->aver=(p->sum)/3.0;
    if(i==0)
    {
         head->next=p;
         p->next=NULL;
         q=p;
    }
    else
    {
        tq=tp=head->next;
        while(tp!=NULL && (tp->sum)>(p->sum))    //找到第一个小于p的位置
        {
            tq=tp;
            tp=tp->next;
        }
        if(tp==head->next){
            t=head->next;
            head->next=p;
            p->next=t;
            q=t;
        }else if(tp==NULL){
            q->next=p;
            p->next=NULL;
            q=p;
        }else{
            t=tq->next;
            tq->next=p;
            p->next=t;
        }
    }
}

逆序:

方法一:新建链表,用头插法将原表数据复制到新链表。

方法二:对原链表进行操作,比较难理解,自己最好画图看一下。

#include <stdio.h>
#include <stdlib.h>
struct NUM{
    int data;
    struct NUM *next;
};
int main()
{
    int n,t,i;                                //用尾插法创建有头链表1-
    struct NUM *head,*p,*q;
    while(scanf("%d",&n)!=-1)
    {
        head=(struct NUM *)malloc(sizeof(struct NUM));
        head->next=NULL;
        for(i=0;i<n;i++)
        {
            scanf("%d",&t);
            p=(struct NUM *)malloc(sizeof(struct NUM));
            p->data=t;
            if(i==0) head->next=p;
            else q->next=p;
            p->next=NULL;
            q=p;
        }                                      //链表创建结束-1



        p=head->next;                          //链表逆序2-                

        while(p->next)
        {
            q=p->next;                         //取当前节点的下一个节点
            p->next=q->next;                   //当前节点直接连接到下一个节点的下一个节点
            q->next=head->next;                //于是就有一个节点被空出来,把这个节点的连接到当前节点
            head->next=q;                      //head指向被空出来的那个节点

        }                                       //链表逆序-2



        p=head->next;
        while(p)
        {
            printf("%d  ",p->data);
            p=p->next;
        }

    }
}

不大明白的话可以看下图:

简单的说一下,这种方法就是从第一个节点开始,让第一个节点直接连到第三个节点,抽出第二个节点,让它连到第一个节点,最后把它放在head后面。随着操作,第一个节点的在链表中的实际位置不断后移(代码中的p就一直是最初的第一个节点),继续用上述方法操作,直到最后一次操作完成后原来第一个节点(p)的next=NULL即它后面没有数的时候。

如果有问题的话,欢迎留言。

  • 14
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值