【数据结构】速速收藏,一文带你参透双向链表各接口实现_使用双向链表实现数据接收的方法(1)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

void LPopBack(LNode* phead)
{
    if ((phead == NULL) || (phead->next == phead))    //排除为空的情况
    {
        return;
    }
    LNode* tail = phead->prev;    //找到尾
    LNode* tailprev = tail->prev;    //找到尾的前驱
    tailprev->next = phead;    //尾前驱节点的后继指针指向头
    phead->prev = tail->prev;  //头的前驱指针指向尾的前驱
    free(tail);
    tail = NULL;
}
  • 测试尾删接口功能实现:

Ⅵ.双向链表头插:
  • 执行操作前应当对传入指针进行判断,防止传入空指针
  • 在改变指向前,应当首先保存哨兵节点的后继节点,因为当我们插入新节点后链表结构将会发生改变,再想要找到该节点将变得麻烦
  • 进行插入操作时,使哨兵节点的后继指针指向新节点,再使新节点的前驱指针指向哨兵节点,接着使用同样的操作使新节点与哨兵节点的原后继节点互指
void LPushFront(LNode* phead, LDataType x)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* newnode = BuyListNode(x);
    LNode* next = phead->next;    //保存哨兵节点的后继节点
    phead->next = newnode;    //哨兵点的后继指针指向新节点
    newnode->prev = phead;    //新节点的前驱指针指向哨兵节点
    newnode->next = next;    //新节点的后继指针指向哨兵节点的原后继节点
    next->prev = newnode;    //哨兵节点的原后继节点前驱指针指向新节点
}
  • 测试头插接口功能实现:

Ⅶ.双向链表头删:
  • 首先**进行非空判断,并排除链表为空(只含有哨兵节点)**的情况。
  • 开始进行头删操作前找到并保存头节点,防止改变指向后链表结构发生变化难以找到原本的头节点,以便于最后进行释放
  • 并且应当找到并保存原头节点的后继节点,防止防止改变指向后链表结构发生变化而难以找到
  • 具体操作便是使哨兵节点与头节点的后继节点跳过头节点互指,再将要删除的头节点释放并置空即可。
void LPopFront(LNode* phead)
{
    if ((phead == NULL) || (phead->next == phead))    //排除为空的情况
    {
        return;
    }
    LNode* next = phead->next;    //保存头节点,便于释放
    LNode* nextNext = next->next;    //保存头节点的后继节点
    phead->next = nextNext;
    nextNext->prev = phead;
    free(next);
    next = NULL;
}
  • 测试头删接口功能实现:

Ⅷ.双向链表查找:
  • 执行操作前需进行非空判断,防止对空指针进行操作。
  • 查找逻辑很简单,遍历整个链表,直至链表完整循环一遍,若比对存在匹配元素返回该节点,否则返回空。
LNode* LFind(LNode* phead, LDataType x)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* cur = phead->next;
    while (cur != phead)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}
  • 测试查找接口功能实现:

Ⅸ.双向链表给定节点前插:
  • 执行操作前需进行非空判断,防止对空指针进行操作。
  • 执行逻辑很简单,将数据存入通过动态申请的来的新节点中后,找到目标节点,使目标节点的前驱节点与新节点互指,再使新节点与目标节点互指接即可。
void LInsert(LNode* pos, LDataType x)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->prev;
    LNode* newnode = BuyListNode(x);
    posPrev->next = newnode;
    newnode->prev = posPrev;
    newnode->next = pos;
    pos->prev = newnode;
}
  • 测试前插接口功能实现:

Ⅹ.双向链表给定节点后插:
  • 执行操作前需进行非空判断,防止对空指针进行操作。
  • 执行逻辑与前插高度类似,不同的是使目标节点的后继节点与新节点互指,再使新节点与目标节点互指
void LInsertBack(LNode* pos, LDataType x)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->next;
    LNode* newnode = BuyListNode(x);
    posPrev->prev = newnode;
    newnode->next = posPrev;
    newnode->prev = pos;
    pos->next = newnode;
}
  • 测试后插接口功能实现:

ⅩⅠ.双向链表删除给定节点:
  • 执行操作前需进行非空判断,防止操作空指针。
void LErase(LNode* pos)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->prev;
    LNode* posNext = pos->next;
    posPrev->next = posNext;
    posNext->prev = posPrev;
    free(pos);
    pos = NULL;
}
  • 测试删除接口功能实现:

ⅩⅡ.双向链表销毁:
  • 哨兵节点为空,即表示链表内没有有效数据节点,则无需进行销毁、释放与置空操作
  • 含有有效节点则遍历所有节点,将每一个节点均进行释放,特别注意所有数据节点释放完毕之后,不要忘记释放哨兵节点
void LDestroy(LNode* phead)
{
    if(phead==NULL)
    {
        return;
    }
    LNode* cur = phead->next;
    while (cur != phead);
    {
        LNode* next = cur->next;
        free(cur);
        cur = next;
    }
    free(phead);
    phead = NULL;
}

🍄三、完整接口实现代码🍄:

1.List.h:

#pragma once

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

//自定数据类型LDataType:
typedef int LDataType;

//双向链表节点结构:
typedef struct ListNode
{
	LDataType data;
	struct LNode* next;
	struct LNode* prev;
}LNode;

LNode* LInit();    //初始化双向循环链表
void LPrint(LNode* phead);    //打印双向循环链表
LNode* BuyListNode(LDataType x);//双向循环链表新节点申请
void LPushBack(LNode* phead, LDataType x);    //双向循环链表尾插
void LPopBack(LNode* phead);    //双向循环链表尾删
void LPushFront(LNode* phead, LDataType x);    //双向循环链表头插
void LPopFront(LNode* phead);    //双向循环链表头删
LNode* LFind(LNode* phead, LDataType x);    //双向循环链表查找
void LInsertFront(LNode* pos, LDataType x);    //双向循环链表给定节点前插
void LInsertBack(LNode* pos, LDataType x);    //双向循环链表给定节点后插
void LErase(LNode* pos);    //双向循环链表给定节点删除
void LDestroy(LNode* phead);    //双向循环链表的销毁

2.List.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include"List.h"

//初始化双向循环链表初始化:
LNode* LInit()
{
	//哨兵位头节点:
	LNode* phead = (LNode*)malloc(sizeof(LNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

//打印双向循环链表
void LPrint(LNode* phead)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* cur = phead->next;
    while (cur != phead)
    {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

//双向循环链表申请新节点:
LNode* BuyListNode(LDataType x)
{
    LNode* newnode = (LNode*)malloc(sizeof(LNode));
    newnode->data = x;
    newnode->next = NULL;
    newnode->prev = NULL;
    return newnode;
}

//双向循环链表尾插:
void LPushBack(LNode* phead, LDataType x)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* tail = phead->prev;    //找到尾节点
    LNode* newnode = BuyListNode(x);
    //新尾互指:
    tail->next = newnode;
    newnode->prev = tail;
    //新头互指:
    phead->prev = newnode;
    newnode->next = phead;
}

//双向循环链表尾删:
void LPopBack(LNode* phead)
{
    if ((phead == NULL) || (phead->next == phead))    //排除为空的情况
    {
        return;
    }
    LNode* tail = phead->prev;    //找到尾
    LNode* tailprev = tail->prev;    //找到尾的前驱
    tailprev->next = phead;    //尾前驱节点的后继指针指向头
    phead->prev = tail->prev;  //头的前驱指针指向尾的前驱
    free(tail);
    tail = NULL;
}

//双向循环链表头插:
void LPushFront(LNode* phead, LDataType x)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* newnode = BuyListNode(x);
    LNode* next = phead->next;    //保存哨兵节点的后继节点
    phead->next = newnode;    //哨兵点的后继指针指向新节点
    newnode->prev = phead;    //新节点的前驱指针指向哨兵节点
    newnode->next = next;    //新节点的后继指针指向哨兵节点的原后继节点
    next->prev = newnode;    //哨兵节点的原后继节点前驱指针指向新节点
}

//双向循环链表头删:
void LPopFront(LNode* phead)
{
    if ((phead == NULL) || (phead->next == phead))    //排除为空的情况
    {
        return;
    }
    LNode* next = phead->next;    //保存头节点,便于释放
    LNode* nextNext = next->next;    //保存头节点的后继节点
    phead->next = nextNext;
    nextNext->prev = phead;
    free(next);
    next = NULL;
}

//双向循环链表查找:
LNode* LFind(LNode* phead, LDataType x)
{
    if (phead == NULL)
    {
        return;
    }
    LNode* cur = phead->next;
    while (cur != phead)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}

//双向循环链表给定节点前插:
void LInsert(LNode* pos, LDataType x)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->prev;
    LNode* newnode = BuyListNode(x);
    posPrev->next = newnode;
    newnode->prev = posPrev;
    newnode->next = pos;
    pos->prev = newnode;
}

//双向循环链表给定节点前插:
void LInsertFront(LNode* pos, LDataType x)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->prev;
    LNode* newnode = BuyListNode(x);
    posPrev->next = newnode;
    newnode->prev = posPrev;
    newnode->next = pos;
    pos->prev = newnode;
}

//双向循环链表给定节点后插:
void LInsertBack(LNode* pos, LDataType x)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->next;
    LNode* newnode = BuyListNode(x);
    posPrev->prev = newnode;
    newnode->next = posPrev;
    newnode->prev = pos;
    pos->next = newnode;
}

//双向循环链表给定节点删除:
void LErase(LNode* pos)
{
    if (pos == NULL)
    {
        return;
    }
    LNode* posPrev = pos->prev;
    LNode* posNext = pos->next;
    posPrev->next = posNext;
    posNext->prev = posPrev;
    free(pos);
    pos = NULL;
}

//双向循环链表销毁:
void LDestroy(LNode* phead)
{
    if(phead==NULL)
    {
        return;
    }
    LNode* cur = phead->next;
    while (cur != phead);
    {
        LNode* next = cur->next;
        free(cur);
        cur = next;
    }
    free(phead);
    phead = NULL;
}

3.test.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include"List.h"

void LTest()
{
	LNode* plist = LInit();
	//双向循环链表尾插:
	LPushBack(plist, 1);
	LPushBack(plist, 2);
	LPushBack(plist, 3);
	LPushBack(plist, 4);
	LPrint(plist);    //打印链表
	LNode* ret = LFind(plist, 3);
	LErase(ret);
	LPrint(plist);    //打印链表
}

int main()
{
	LTest();

	return 0;
}

//双向循环链表尾插与尾删:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist);    //打印链表
双向循环链表尾删:
//LPopBack(plist);
//LPopBack(plist);
//LPopBack(plist);
//LPrint(plist);    //打印链表

//双向循环链表头插与头删:
//LNode* plist = LInit();
//双向循环链表头插:
//LPushFront(plist, 1);
//LPushFront(plist, 2);
//LPushFront(plist, 3);
//LPushFront(plist, 4);
//LPrint(plist);    //打印链表
双向循环链表头删:
//LPopFront(plist);
//LPopFront(plist);
//LPopFront(plist);
//LPrint(plist);    //打印链表

//双向循环链表查找:
//LNode* plist = LInit();
//双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist);    //打印链表
//LNode* ret = LFind(plist, 3);

//双向循环链表给定位置前插:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist);    //打印链表


![img](https://img-blog.csdnimg.cn/img_convert/1373927d5bcab1a69d4c6da79ca1d020.png)
![img](https://img-blog.csdnimg.cn/img_convert/a02561634d3caefc56724ec3fd6cc8e4.png)
![img](https://img-blog.csdnimg.cn/img_convert/f0436fc51930506da21a30e1e2be5d3e.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

ack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist);    //打印链表
//LNode* ret = LFind(plist, 3);

//双向循环链表给定位置前插:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist);    //打印链表


[外链图片转存中...(img-5bwTiXDD-1715005506986)]
[外链图片转存中...(img-y9ojwGQO-1715005506986)]
[外链图片转存中...(img-8ixsDlBW-1715005506987)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值