链表基本操作

一、工程文件的创建
1、main
在根目录下新建一个名为main的文件夹,里面包含Makefile文件和src源码文件夹。

其中Makefile如下所示代码编写:

include ../scripts/Makefile
 
all : $(Objs)
 
clean : 
    rm -rf $(Objs)
在src里面新建一个main.c,里面写上包括但不限于,链表创建,链表释放,链表插入,链表删除,链表遍历,链表逆序的功能。

代码等测试的时候一个个贴出来。

2、link
在根目录下面新建一个名为link的文件夹,里面包含Makefile文件和src源码文件夹。

其中Makefile和main中的一样。

在src里面新建一个link.c,代码为链表基本操作的定义,源码如下:

#include <stdio.h>
#include <stdlib.h>
#include "../../include/myhead.h"
 
/* 函数名:Create_link
 * 参数:链表(结构体)指针,
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将头指针指向NULL,创建一个空的链表
 */
void Create_link(Link *head)
{
    *head = NULL;                   //将空链表指向NULL,防止野指针
}
 
/* 函数名:Insert_node_head
 * 参数:链表的头指针,新节点的指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将新节点的指针指向原来的头节点,将头指针指向新节点
 */
void Insert_node_head(Link * head,Link new_node)
{
    new_node->next = *head;         //新节点指向越来的头指针
    *head = new_node;               //头指针指向新节点
}
 
/* 函数名:Insert_node_tail()
 * 参数:链表的头指针,新节点的指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:
 * 概述:将链表的最后一个节点的指针指向新节点,新节点的指针指向NULL,如果链表为空,则让头指针指向它,它的指针指向NULL
 */
void Insert_node_tail(Link  * head,Link  new_node)
{
    Link p;                         //定义一个结构体指针,用来移动链表,保护头指针
 
    p = *head;
 
    if (p == NULL)                  //如果链表是空链表。则将新节点作为头节点
    {
        new_node->next = NULL;      //新节点的指针指向NULL
        *head = new_node;           //头指针指向新节点
    }
    else                            //否则找到链表的尾节点
    {
        while (p->next != NULL)     //判断是否到尾节点
        {
            p = p->next;            //节点向后移动
        }
 
        new_node->next = NULL;      //移动到尾节点之后,让新节点指向NULL
        p->next = new_node;         //原来的尾节点指向新节点
    }
 
}
 
/* 函数名:Insert_node()
 * 参数:头指针,要插入链表的数据
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将一个数据插入链表,如果链表为空,则将它作为头节点,否则,升序插入
 */
void Insert_node(Link * head,int num)
{
    Link new_node,p;                //定义一个新节点指针和一个结构体指针(移动链表,保护头指针)
 
    new_node = (Link)malloc(sizeof(Node));
    new_node->num = num;            //为新节点分配内存,把数据存入新节点
 
    p = *head;
 
    if (*head == NULL)              //如果是空链表,把新节点作为链表
    {
        *head = new_node;           //头指针指向新节点
        new_node->next = NULL;      //新节点指向NULL
    }
    else                            //不是空链表
    {
        if (num < (*head)->num)     //数据比头节点数据还小,将新节点作为新的头节点
        {
            *head = new_node;       //头指针指向新节点
            new_node->next = p;     //新节点指向原来的头节点
            return;                 //插入结束,返回
        }
        while (p->next != NULL)     //p还没有移动到尾节点
        {
            if (p->next->num > num) //如果下一个节点的数比要插入的数大
            {
                new_node->next = p->next;
                p->next = new_node; //就插入,新节点指向下一个节点,这个节点指向新节点;
 
                return;             //插入结束,返回
            }
 
            p = p->next;            //没有返回,就将链表节点向后移
        }
        
        p->next = new_node;         //单节点和不是单节点的最后一个数据都比较过了,不满足插在之前的条件
        new_node->next =NULL;       //这个节点指向新节点,新节点指向NULL
    }
}
 
/* 函数:Delete_node()
 * 参数:头指针,具体数值
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将指定数值的值从链表删除
 */
void Delete_node(Link * head,int num)
{
    Link p,q;                       //定义两个结构体指针,保护链表,和将删除节点的链表连接在一起
    int flag = 1;
 
    if (*head == NULL)              //如果链表是空链表,无法删除,直接返回
    {
        printf("Link is empty!\n");
        
        return;
    }
    else                            //如果不是空链表
    {
 
        while (*head != NULL && (*head)->num == num) 
                                    //如果要删除的值和头指针一样,则将头指针后移,首先链表(头节点)不能为空
        {
            p = *head;              //将头节点存入p
            
            *head = (*head)->next;  //将头指针指向下一个节点
            
            free(p);                //将头节点释放
 
            flag = 0;               //清0删除开关量
        }
 
        p = q = *head;              //把最新的头节点给p,q
        
        while (p != NULL)           //p不指向NULL
        {
           if (p->num != num)       //P指向的数据不等于要删除的数据
           {
               q = p;               //将q指向p
               p = p->next;         //p指向下一个下一个节点
           }
           else                     //p指向的数据等于要删除的数据
           {
               q->next = p->next;   //将q指向p的下一个
               free(p);             //将p指向的空间释放
               p = q->next;         //将下一个节点给p,用以继续比较
               flag = 0;            //清0删除开关量
 
           }
        }
 
        if (flag)                   //删除开关量为1
        {
            printf("No such num!\n");
        }                           //输出链表里没有那个数
    }
}
/* 函数名:Display()
 * 参数:链表的头指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:由头指针指向的头节点开始,将链表所有数据输出
 */
void Display(Link head)
{
    Link p;
 
    p = head;                       //将头指针赋值给别人,保护头指针
    if (head == NULL)               //链表为空
    {
        printf("Link is empty!\n"); //输出链表为空
    }
 
    while (p != NULL)               //当p指针不指向空
    {
        printf("num = %d\n",p->num);//将p指向的数据输出
        p = p->next;                //p指向下一个节点
    }
}
 
/* 函数名:Release()
 * 参数:链表的头指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:释放整个链表
 */
void Release(Link * head)       
{
    Link p;                         //保护头节点
 
    while (*head != NULL)           //头节点不指向空
    {
        p = *head;                  //存一下头节点
        *head = (*head)->next;      //头节点后移
        free(p);                    //释放p指向的节点
    }
}
 
/* 函数名:Reverse_link()
 * 参数:结构体指针,指向节点的结构体
 * 返回值:结构体指针
 * 调用函数:Reverse_link()
 * 被调函数:main()
 * 概述:递归调用,将头指针指向尾节点,让下一个节点指向自己,让自己指向NULL
 */
Link Reverse_link(Link node)
{
    Link head;                      //定义一个指针用以存放新的头节点
 
    if( node == NULL || node->next == NULL)
    {
        return node;                //如果节点为空或者节点指向空,就返回这个节点
    }
    else
    {
        head = Reverse_link(node->next);//将返回的节点作为头指针
        node->next->next = node;    //下一个节点指向自己
        node->next = NULL;          //自己指向空
        return head;                //返回头指针
    }
}
写好备用。

3、include
在根目录下新建一个include文件夹,里面新建一个myhead.h文件,写上链表结构体的定义,转命名,和函数的声明。

源码如下:

struct node                 // 创建链表节点
{                           
    int num;                //成员1为数据
    struct node * next;     //成员2为和自己同类型的指针
};
 
typedef struct node Node;   //将链表节点重命名为Node
typedef struct node * Link; //将链表节点指针重命名为Link
//typedef Node * Link;
 
/* 函数名:Create_link
 * 参数:链表(结构体)指针,
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将头指针指向NULL,创建一个空的链表
 */
void Create_link(Link *head);
 
/* 函数名:Insert_node_head
 * 参数:链表的头指针,新节点的指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将新节点的指针指向原来的头节点,将头指针指向新节点
 */
void Insert_node_head(Link * head,Link new_node);
 
/* 函数名NULL* 参数:链表的头指针,新节点的指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:
 * 概述:将链表的最后一个节点的指针指向新节点,新节点的指针指向NULL,如果链表为空,则让头指针指向它,它的指针指向NULL
 */
void Insert_node_tail(Link  * head,Link  new_node);
 
/* 函数:Delete_node()
 * 参数:头指针,具体数值
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将指定数值的值从链表删除
 */
void Delete_node(Link * head,int num);
 
/* 函数名:Display()
 * 参数:链表的头指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:由头指针指向的头节点开始,将链表所有数据输出
 */
void Display(Link head);
 
/* 函数名:Release()
 * 参数:链表的头指针
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:释放整个链表
 */
void Release(Link * head);
 
/* 函数名:Reverse_link()
 * 参数:结构体指针,指向节点的结构体
 * 返回值:结构体指针
 * 调用函数:Reverse_link()
 * 被调函数:main()
 * 概述:递归调用,将头指针指向尾节点,让下一个节点指向自己,让自己指向NULL
 */
Link Reverse_link(Link node);
 
 
/* 函数名:Insert_node()
 * 参数:头指针,要插入链表的数据
 * 返回值:无
 * 调用函数:无
 * 被调函数:main()
 * 概述:将一个数据插入链表,如果链表为空,则将它作为头节点,否则,升序插入
 */
void Insert_node(Link * head,int num);
4、scripts
在根目录下新建一个scripts文件夹,里面新建一个Makefile文件,源码如下所示:

CC := gcc
CFLAGS := -Wall -O3
Libs = -lpthread
Target := base_link
Source := $(wildcard src/*.c)
Objs := $(patsubst %.c,%.o,$(Source))
Modules += main link
AllObjs := $(addsuffix /src/*.o,$(Modules))
 
留着备用。

5、Makefile
在根目录下新建一个Makefile文件,源码如下:

 

include scripts/Makefile
 
modules_make = $(MAKE) -C $(1);
modules_clean = $(MAKE) clean -C $(1);
 
.PHONY: all mm mc clean
 
all: $(Target)
 
mm:
    @ $(foreach n,$(Modules),$(call modules_make,$(n)))
mc:
    @ $(foreach n,$(Modules),$(call modules_clean,$(n)))
 
$(Target) : mm
    $(CC) $(CFLAGS) -o $(Target) $(AllObjs) $(Libs)
    @ echo $(Target) make done!
 
clean : mc
    rm -rf $(Target)
    @ echo clean done!
 
 
 
 
 
 

以上五步完成之后,工程文件就建立好了。

就可以在根目录下输入make命令(Linux环境下)。

执行效果如下:

其中base_link为可执行文件。

二、链表的基本操作
具体如何实现link.c都有注释。

1、链表的创建与遍历。
链表创建之后我们无法的知是否创建,可以通过遍历链表来操作。

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:生成链表并遍历
 */
int main()
{
    Link head;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    Display(head);          // 遍历链表
 
}
执行效果如下:

成功创建了一个新链表。

2、链表的创建与节点插入
(1)、头插
我们把数据插在链表前面,再通过遍历显示。

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,头插进十个数据,并遍历链表
 */
int main()
{
    Link head;
    Link new_node;
 
    int i;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    for (i = 0;i < 10; i++)
    {
 
        new_node = (Link)malloc(sizeof(Node));
 
        new_node->num = (i + 1) / 2;
 
        Insert_node_head(&head,new_node);
    }
 
 
    Display(head);
 
    return 0;
}
执行效果如下:

成功的创建链表,头插,遍历。

(2)、尾插
同样的把数据插在尾部,并且遍历显示。

main()源码如下:

 

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,尾插进十个数据,并遍历链表
 */
int main()
{
    Link head;
    Link new_node;
 
    int i;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    for (i = 0;i < 10; i++)
    {
 
        new_node = (Link)malloc(sizeof(Node));
 
        new_node->num = (i + 1) / 2;
 
        Insert_node_tail(&head,new_node);
 
    }
 
 
    Display(head);
 
    return 0;
}
执行效果如下:

成功的创建链表,尾插,遍历。

(3)插入(升序)
把数据插入到指定要求的地方,这里是升序插入。

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,插入你输入的10个数据,每次输入都遍历链表
 */
int main()
{
    Link head;
    Link new_node;
 
    int i;
    int num;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    Display(head);
 
    for (i = 0;i < 10; i++)
    {
 
        printf("please enter num to be add!");
        scanf("%d",&num);
 
        Insert_node(&head,num);
 
        Display(head);
    }
 
    return 0;
}
执行效果如下:

成功的创建链表,插入数据,遍历链表,对链表的插入有,插入空链表,插入链表头,插入链表尾,插入链表中四种,执行效果如预期。

3、链表的释放
为了不造成内存泄漏,链表使用完成之后一定要释放。

对空链表的释放毫无意义,甚至会报错。所以我们就新建一个头插的链表来进行释放,同时通过遍历来判断是否释放。

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,头插进数个节点,遍历,释放链表,再遍历
 */
int main()
{
    Link head;
    Link new_node;
 
    int i;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    for (i = 0;i < 10; i++)
    {
 
        new_node = (Link)malloc(sizeof(Node));
 
        new_node->num = (i + 1) / 2;
 
        Insert_node_head(&head,new_node);
 
    }
 
 
    Display(head);
 
    Release(&head);
 
    Display(head);
 
    return 0;
}
执行效果如下:

最后可见,链表被释放为空链表。

4、链表的删除
链表删除有很多种情况,我写的是删除指定元素。那么就有以下几种情况。

(1)、删除空链表里面的数据

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,遍历,删除全部链表数据
 */
int main()
{
    Link head;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    Display(head);
 
    printf("please input a number to be deleted!\n");
    scanf("%d",&num);
 
    Delete_node(&head,num);
 
    return 0;
}
 

执行效果如下:

(2)、删除链表的全部数据

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,插入数据,遍历,删光数据,遍历
 */
int main()
{
    Link head;
 
    int num;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
 
 
    printf("please enter num to be add!");
    scanf("%d",&num);
 
    Insert_node(&head,num);
 
    Display(head);
 
 
    printf("please input a number to be deleted!\n");
    scanf("%d",&num);
 
    Delete_node(&head,num);
 
    Display(head);
 
    return 0;
}
执行效果如下:

(3)、删除尾部数据(和删除中间数据一起演示,同时删除多个数据)

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,插入十个数据,遍历,删除部分,遍历
 */
int main()
{
    Link head;
 
    int i;
    int num;
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    for (i = 0;i < 10; i++)
    {
 
        printf("please enter num to be add!");
        scanf("%d",&num);
 
        Insert_node(&head,num);
 
    }
 
    Display(head);
 
    printf("please input a number to be deleted!\n");
    scanf("%d",&num);
 
    Delete_node(&head,num);
 
    Display(head);
 
    return 0;
}
执行效果如下图所示:

5、链表的逆序
我这边逆序是用的递归的方法,用起来比较简单。

main()源码如下:

#include <stdio.h>
#include <stdlib.h>         //调用malloc函数,返回值为void
#include "../../include/myhead.h"
 
/* 函数名:main()
 * 参数:无
 * 返回值:整型(无意义)
 * 调用函数:Create_link(),Insert_node_head(),Display()
 * 被调函数:无
 * 概述:调用函数新建一个链表,头插10数据,遍历,逆序,遍历。
 */
int main()
{
    Link head;
    Link new_node;
 
    int i;
 
 
    Create_link(&head);     // 创建链表, 将地址传过去,带回地址
 
    for (i = 0;i < 10; i++)
    {
 
        new_node = (Link)malloc(sizeof(Node));
 
        new_node->num = (i + 1) / 2;
 
        Insert_node_head(&head,new_node);
 
    }
 
    Display(head);
    
    head = Reverse_link(head);
 
    Display(head);
 
    return 0;
}
执行效果如下图:

链表的基本操作大致就是这些。
--------------------- 
作者:不会水的鸟 
来源:优快云 
原文:https://blog.youkuaiyun.com/weixin_42425796/article/details/86741819 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值