C语言之双链表的基本操作

目录

1、编译环境和工具

2、双链表的介绍

3、双链表的实现

3.1、数据节点的定义

3.2、基本操作的Function定义

3.3、基本操作Function的实现

4、makefile的编写

5、总结


1、编译环境和工具


        我是在MAC os下写的程序,使用的是GCC进行编译的,因为vim中编写代码有点费劲,所以使用VScode作为编辑器,编写makefile来链接多个文件,最终生成一个可执行程序。

2、双链表的介绍


首先我们来讲一讲为何有了单链表,还要弄个双链表呢?在学习单链表的时候,我们都知道单链表比起数组来说,它的优势在于对增删数据节点方面非常有效率;但是在查找数据方面就显得比较笨了,比如你在查找到某个数据节点后,你想访问它的前面一个数据节点,这时候你需要再次遍历一遍单链表才可以,当然你也可以用双指针来弄这个(这个咱们暂时不考虑)! 为了提升咱们链表在查询数据方面的效率,所以就为它多加了一个指针域,这样就这可以提高链表在某些数据操作的效率。好啦!下面咱们进入正题!

3、双链表的实现


3.1、数据节点的定义

         双链表的数据节点的定义就比单链表的数据节点的定义多了一个指针域而已,多的这个指针域用来存放当前数据节点的前驱(也就是当前节点的前面一个节点的地址,记住,指针就是存放地址的!),好啦,我们看一下代码吧!

typedef int ElemType;
typedef int Status;

//双链表的数据结构定义
typedef struct DulNode{
    ElemType data;              //数据域
    struct DulNode *pre;        //前驱指针
    struct DulNode *next;       //后继指针
}DulNode;

typedef struct DulNode *DulLinkList;    //定义结点指针

3.2、基本操作的Function定义

         链表的基操无非就拿老几样,首先你需要创建一个双链表吧!当然也是有头插法和尾插法这两种方式,这里我就使用尾插法啦!至于头插法怎么实现,这个请参照单链表中的头插法进行自我实现。(单链表头插法

链表的创建、删除指定元素、查找指定元素、在特定位置插入元素、以及遍历整个链表这都是最基本的操作啦!代码如下:

//双链表的创建
void CreateDulList(DulLinkList *L, int n);
//双链表的遍历
void DisPlayList(DulLinkList L);
//双链表插入结点,在指定位置插入结点
Status InsertNode(DulLinkList *L, int n, ElemType data);
//双链表删除节点,删除指定位置的节点,并返回删除节点的data
Status DeleteNode(DulLinkList *L, int n, ElemType *data);
//双链表中查找某一元素是否存在
Status FindNode(DulLinkList L, ElemType data);
//返回链表长度
int GetListLength(DulLinkList L);

3.3、基本操作Function的实现

         首先我们来说一下双链表的创建吧!这里我是有创建头结点的哦!head->pre指向NULL,head->next指向首节点,head->data用来存储链表的长度,当然你也可以把你的head->pre指向为节点,再把为节点的next指向head,这样就构成了一个双向的循环链表。双链表的创建基本与单链表一致,值得注意的地方就是在head节点创建的时候,要将head->pre=NULL,head->next = NULL,避免野指针的出现!

//双链表的创建
void CreateDulList(DulLinkList *L, int n)
{
    DulLinkList me, ptr;
    int i;
    (*L) = (DulLinkList)malloc(sizeof(DulNode));    //为头结点申请空间
    (*L)->next = NULL;
    (*L)->pre = NULL;
    (*L)->data = n;
    ptr = (*L);
    for(i = 0; i < n; i++)
    {
        me = (DulLinkList)malloc(sizeof(DulNode));
        me->data = rand()%100 + 1;    //节点数据域
        me->next = NULL;
        me->pre = NULL;

        ptr->next = me;
        me->pre = ptr;
        ptr = ptr->next;
    }

}

其次就是如何遍历双链表了,其实你怎么遍历单链表的,这里你就怎么遍历双链表就好啦!

//双链表的遍历
void DisPlayList(DulLinkList L)
{
    DulLinkList me;
    me = L->next;
    for(;me != NULL; me = me->next)
    {
        printf("%d ",me->data);
    }
    printf("\n");
}

在指定位置插入新节点,这里需要注意一下,首先你需要判断一下插入的位置是否合法!当插入位置合法的时候,你需要考虑是否是在首节点前面进行插入,若是在首节点前面插入,则需要单独处理一下(至于为何单独处理,这是因为我个人写的代码有些不合理!),其次就是正常位置的插入啦!好的,下面我们来看看代码吧!

//双链表插入结点,在指定位置插入结点
Status InsertNode(DulLinkList *L, int n, ElemType data)
{
    DulLinkList me,ptr,prev;
    me = (DulLinkList)malloc(sizeof(DulNode));
    me->data = data;
    me->pre = NULL;
    me->next = NULL;

    ptr = (*L)->next;
    if(n < 1 || n > (*L)->data)
    {
        printf("insert location is error! please check your insert location.\n");
        return ERROR;
    }
        
    else if(n == 1)      //插入表头
    {
        me->next = ptr;
        ptr->pre = me;
        (*L)->next = me;
        me->pre = (*L);
        ((*L)->data)++;
        return OK;
    }
    else{
        for(int i = 0; i < n; i++)
        {
            prev = ptr;
            ptr = ptr->next;
        }

        me->next = ptr;
        ptr->pre = me;
        prev->next = me;
        me->pre = prev;

        ((*L)->data)++;
        return OK;
    }
}

删除指定位置的节点,与在指定位置插入节点其实有着异曲同工之妙!好啦,废话多讲,我们来看看代码!(记住!记住!记住!重要的话讲三遍!那就是删除的节点,你可不要不释放内存哦!不然内存泄漏了,那可是个大问题哈!)

//双链表删除节点,删除指定位置的节点,并返回删除节点的data
Status DeleteNode(DulLinkList *L, int n, ElemType *data)
{
    DulLinkList me,ptr;
    me = (*L);
    if(n < 1 || n > (me->data))
    {
        printf("delete location is error! please check your delete location.\n");
        return ERROR;
    }
    else if(n == 1)
    {
        ptr = me->next;
        me->next = ptr->next;
        ptr->next->pre = me;
        *data = ptr->data;
        free(ptr);
        ((*L)->data)--;
        return OK;
    }
    else{
        me = me->next;
        for(int i = 1; i < n; i++)
        {
            ptr = me;
            me = me->next;
        }

        ptr->next = me->next;
        me->next->pre = ptr;
        (*data) = me->data;
        free(me);
        ((*L)->data)--;
        return OK;
    }
}

接下来就是如何在双链表中搜索数据节点是否在链表中啦!这个就和单链表的一样的,所以我就不说啥啦!直接贴代码!你看就行啦!

//双链表中查找某一元素是否存在
Status FindNode(DulLinkList L, ElemType data)
{
    DulLinkList me;
    int Location = 1;
    me = L->next;
    for(; me != NULL; me = me->next, Location++)
    {
        if(data == me->data)
            break;
        else
            continue;
    }
    if(Location < L->data)
        return Location;
    else
        return ERROR;
}

好啦!文章就到此结束啦!你看完是不是有想吐槽博主的欲望呢!看看这个博主,动不动就贴代码,啥都不讲!不是俺不讲哈!是真的没啥好讲的,单链表你要是搞懂了,双链表当然你也就搞懂啦!(想啥呢哈哈哈哈!)自行体会吧!

4、makefile的编写

文章开头我有讲过哦!我为啥要用makefile呢!因为vscode人家不会自动帮你把所用的文件链接起来,它只会弄单个文件跑一跑哦!(骗你的,其实他也可以弄多个文件进行编译,只是你需要手动的添加到配置文件当中而已,至于怎么搞,网上一大把教程,自行百度,要是百度都不想百度了,那我劝你善良哈!),这里我自己最近也在开始刻意的练习写makefile文件,可能写的不太好哈!初级makefile的水平哈!不过,我觉得makefile挺有意思的,以后要把stm32的开发环境在linux上搭建,makefile的编写是必不可少的,所以,一切的一切都是有原因的!好啦,看看俺的makefile长什么样子吧!我这个makefile啊,你用linux、mac os或许能直接用,但是windows估计不行哦!


#OBJS=main.o double_list.o
CC=gcc
CFLAGS+=-c -Wall -g

TOPDIR := $(PWD)
OBJDIR := $(TOPDIR)/build
BINDIR := $(TOPDIR)/bin
SRCDIR := $(TOPDIR)/src

sfStack: $(OBJDIR)/main.o $(OBJDIR)/double_list.o
	$(CC) $^ -o $@

$(OBJDIR)/%.o:$(SRCDIR)/%.c
	$(CC) $^ $(CFLAGS) -o $@

run:
	./sfStack

clean:
	$(RM) $(OBJDIR)/*.o sfStack -r

help:
	@echo "********************************* help *******************************"
	@echo "*                                                                    *"
	@echo "*                             option description                     *"
	@echo "*                                                                    *"
	@echo "********************************* help *******************************"


clear:
	clear

5、总结

     这篇文章有点水,大家少喝点就行啦!对啦,要是有编写makefile的大神,小弟我提个问题哈!就是如何把编译生成好的可执行文件放到自己指定的文件夹中呢?欢迎大佬在评论区留言!同时欢迎各位访客大佬点点赞哦!(就是菜啊!一个又菜有爱玩的人!) 对啦,附上源码链接,有需要的请自提哈源码链接

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值