C语言实现链表

一.链表介绍

  1.链表结构,支持动态增加节点,释放节点,比较适合存储动态数据的应用场景,而且链表的空间是存储在堆上面的,可以动态分配,释放。

从效率上来讲,数组的空间是连续的,查询、读取数据数组占优势;链表的优势在于节点可以动态增加、动态删除,删除支持任意位置的节点删除。

2.链表特点:

(1)数组的空间是连续的,可以直接通过[]下标访问。

(2)链表的节点是不连续的,需要通过每个节点的指针,来找到上一个节点或者下一个节点的地址。

     链表的每个节点就是一个结构体变量,节点里有一个或者两个指针,可以保存上一个节点和下一个节点的地址,方便遍历链表,删除、插入节点时定位位置。

二.单向链表的创建与使用

1.实现的功能如下:

初始化链表头

插入节点的函数(链表任意位置插入,链表尾插入)

删除节点的函数(链表任意位置删除、链表尾删除)

遍历链表,输出链表里的所有信息

2.代码实现

#include <stdio.h>

#include <stdlib.h>

//定义链表节点的结构体

struct app

{

    int a;

    struct app *next; //能保存结构体的地址

};

struct app *list_head=NULL;  //链表的头指针

void list_print(struct app *head);

struct app *list_HeadInit(struct app *head);

void list_add(int a,struct app *head);

void list_del(int a,struct app *head);

/*

函数功能: 初始化链表头--给链表头申请空间

*/

struct app *list_HeadInit(struct app *head)

{

    if(head==NULL) //没有空间

    {

        //1. 申请链表头空间

        head=malloc(sizeof(struct app));

        //2. 初始化链表头

        head->next=NULL;

    }

    return head;

}

/*

函数功能: 在链表尾插入数据

int a  插入的数据值

struct app *head  链表头

*/

void list_add(int a,struct app *head)

{

    struct app *new_p=NULL;

    struct app *next_p=head;

    struct app *tmp_p; //保存上一个节点的地址

    //1.申请空间并给空间成员赋值

    new_p=malloc(sizeof(struct app));

    new_p->a=a;

    new_p->next=NULL;

    //2. 找到链表尾

    while(next_p!=NULL)

    {

        tmp_p=next_p;

        next_p=next_p->next; //指针指向下一个节点

    }

    //3. 插入新节点--链接结尾

    tmp_p->next=new_p;

}

/*

函数功能: 遍历输出链接里的所有数据

*/

void list_print(struct app *head)

{

    struct app *next_p=head;

    int cnt=0;

    if(head!=NULL)

    {

        while(next_p->next!=NULL)

        {

            next_p=next_p->next;

            printf("链表节点[%d]:a=%d\n",cnt++,next_p->a);

        }

    }

}

/*

函数功能:链表的删除--按照指定的数据删除

*/

void list_del(int a,struct app *head)

{

    struct app *next_p=head;

    struct app *tmp_p; //保存上一个节点的地址

    //1. 找到要删除的链表

    if(next_p!=NULL)

    {

        while(next_p->next!=NULL)

        {

            tmp_p=next_p; //保存上一个节点的地址

            next_p=next_p->next; //获取有效节点的地址

            if(next_p->a==a) //判断是否需要删除

            {

                tmp_p->next=next_p->next;

                free(next_p);

            }

        }

    }

}

int main()

{

    //1. 初始化链表头

    list_head=list_HeadInit(list_head);

    //2. 在链表尾插入数据

    list_add(10,list_head);

    list_add(11,list_head);

    list_add(12,list_head);

    list_add(13,list_head);

    //3. 删除节点

    list_del(11,list_head);

    //4. 输出链接节点里的数据

    list_print(list_head);

    return 0;

}

三.单向循环链表

   和上面二中单向链表区别就是尾结点指向了头结点而不是NULL。

#include <stdio.h>

#include <stdlib.h>

//定义链表节点的结构体

struct app

{

    int a;

    struct app *next; //能保存结构体的地址

};

struct app *list_head=NULL;  //链表的头指针

void list_print(struct app *head);

struct app *list_HeadInit(struct app *head);

void list_add(int a,struct app *head);

void list_del(int a,struct app *head);

/*

函数功能: 初始化链表头--给链表头申请空间

*/

struct app *list_HeadInit(struct app *head)

{

    if(head==NULL) //没有空间

    {

        //1. 申请链表头空间

        head=malloc(sizeof(struct app));

        //2. 初始化链表头

        head->next=head;

    }

    return head;

}

/*

函数功能: 在链表尾插入数据

int a  插入的数据值

struct app *head  链表头

*/

void list_add(int a,struct app *head)

{

    struct app *new_p=NULL;

    struct app *next_p=head;

    struct app *tmp_p; //保存上一个节点的地址

    //1.申请空间并给空间成员赋值

    new_p=malloc(sizeof(struct app));

    new_p->a=a;

    new_p->next=head;

    //2. 找到链表尾

    if(head!=NULL)

    {

        if(next_p->next==head)  //表示第一次插入节点

        {

            next_p->next=new_p;

            //head ----<节点1>---head

        }

        else

        {

            while(next_p->next!=head)

            {

                next_p=next_p->next; //指针指向下一个节点

            }

            //3. 插入新节点--链接结尾

            next_p->next=new_p;

        }   

    }

}

/*

函数功能: 遍历输出链接里的所有数据

*/

void list_print(struct app *head)

{

    struct app *next_p=head;

    int cnt=0;

    if(head!=NULL)

    {

        printf("头地址: %#x\n",next_p); //头

        printf("第一个节点地址:%#x\n",next_p->next); //下一个节点地址

        printf("第二个节点地址:%#x\n",next_p->next->next); //下下一个节点地址

        printf("第三个节点地址:%#x\n",next_p->next->next->next);

        printf("第四个节点地址:%#x\n",next_p->next->next->next->next);

    

        while(next_p->next!=head)

        {

            next_p=next_p->next;

            printf("链表节点[%d]:a=%d\n",cnt++,next_p->a);

        }

    }

}

/*

函数功能:链表的删除--按照指定的数据删除

*/

void list_del(int a,struct app *head)

{

    struct app *next_p=head;

    struct app *tmp_p; //保存上一个节点的地址

    //1. 找到要删除的链表

    if(next_p!=NULL)

    {

        while(next_p->next!=head)

        {

            tmp_p=next_p; //保存上一个节点的地址

            next_p=next_p->next; //获取有效节点的地址

            if(next_p->a==a) //判断是否需要删除

            {

                tmp_p->next=next_p->next;

                free(next_p);

            }

        }

    }

}   

int main()

{

    //1. 初始化链表头

    list_head=list_HeadInit(list_head);

    //2. 在链表尾插入数据

    list_add(10,list_head);

    list_add(11,list_head);

    list_add(12,list_head);

    list_add(13,list_head);

    //3. 删除节点

    list_del(11,list_head);

    //4. 输出链接节点里的数据

    list_print(list_head);

    return 0;

}

四.创建双向链表循环,实现插入、删除、遍历

双向链表在每个节点里新增加了一个指针,用于保存上一个节点的地址,现在的节点里一个用两个指针,一个保存上一个节点的地址,一个保存下一个节点的地址。

#include <stdio.h>

#include <stdlib.h>

//定义链表节点的结构体

struct app

{

    int a;

    struct app *next; //下一个节点地址

    struct app *prev; //前一个节点地址

};

//全局变量声明区域

struct app *list_head=NULL;  //链表的头指针

//函数声明

struct app *List_HeadInit(struct app *head);

void list_add(int a,struct app *head);

void list_print(struct app *head);

void list_del(int a,struct app *head);

/*

函数功能: 创建链表头

*/

struct app *List_HeadInit(struct app *head)

{

    if(head==NULL)

    {

        head=malloc(sizeof(struct app));

        head->a=0;

        head->next=head;

        head->prev=head;

    }

    return head;

}

/*

函数功能: 添加数据-链表尾添加数据

*/

void list_add(int a,struct app *head)

{

    struct app *next_p=head;

    struct app *new_p=NULL;

    /*1. 申请新的节点*/

    new_p=malloc(sizeof(struct app));

    new_p->a=a;

    new_p->next=head;

    /*2. 完成新节点的添加*/

    //遍历链表

    while(next_p->next!=head)

    {

        next_p=next_p->next;

    }

    //添加新节点

    new_p->prev=next_p;

    next_p->next=new_p;

    //修改链表头的上一个节点地址

    head->prev=new_p;

}

/*

函数功能: 输出链表里的所有数据

*/

void list_print(struct app *head)

{

    struct app *next_p=head;

    int cnt=0;

    /*1. 顺向遍历*/

    while(next_p->next!=head)

    {

        next_p=next_p->next;

        printf("节点[%d]:%d\n",cnt++,next_p->a);

    }

    /*2. 反向遍历*/

    next_p=head;

    while(next_p->prev!=head)

    {

        next_p=next_p->prev;

        printf("节点[%d]:%d\n",cnt--,next_p->a);

    }

}

/*

函数功能: 删除链表里的指定节点

*/

void list_del(int a,struct app *head)

{

    struct app *next_p=head;

    struct app *tmp_p=NULL;

    while(next_p->next!=head)

    {

        tmp_p=next_p; //保存上一个节点的地址

        next_p=next_p->next;

        if(next_p->a==a)

        {

            //方式1

            tmp_p->next=tmp_p->next->next;

            tmp_p->next->prev=tmp_p;

            //方式2

            //tmp_p->next=next_p->next;

            //next_p->next->prev=tmp_p;

            

            //printf("%d\n",tmp_p->a); //11

            //printf("%d\n",tmp_p->next->a); //13

            //printf("%d\n",next_p->next->prev->a); //11

            free(next_p);

            break;

        }

    }

}

int main()

{

    /*1. 初始化链表*/

    list_head=List_HeadInit(list_head);

    /*2. 添加链表节点*/

    list_add(10,list_head);

    list_add(11,list_head);

    list_add(12,list_head);

    list_add(13,list_head);

    list_add(14,list_head);

    /*3.删除指定节点*/

    list_del(12,list_head);

    /*4. 遍历输出所有节点信息*/

    list_print(list_head);

    return 0;

}

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AI+程序员在路上

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

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

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

打赏作者

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

抵扣说明:

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

余额充值