当前位置: 首页 > article >正文

(数据结构)双向链表

(数据结构)带头双向循环链表

前言

  • 前面链表部分,咱们着重讲解了不带头单向不循环链表,简称单链表。那么链表其实也分很多种类适用于各种各样的场景。通过单链表的学习,其实我们已经大致了解了链表的绝大多数的内容,所以接下来我通过再为大家讲解一个带头双向循环链表,那么剩下的链表的种类大家就可以各自组合,各自书写了。
  • 链表种类:
    在这里插入图片描述
  • 两种代表链表:
  • 在这里插入图片描述
    1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
    2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了,后面我们代码实现了就知道了。

整体的实现代码如下:(头文件)

 #include<stdio.h>
 #include<stdlib.h>
 #include<assert.h>
 
 //对int类型取别名是为了后续方便更改数据类型
 typedef int LTDateType;
 //链表节点的结构
 typedef struct ListNode
 {
     struct ListNode *prev;
     struct ListNode *next;
     LTDateType date;
 }LTNode;
 
 //动态申请一个节点
 LTNode *BuyLTNode(LTDateType x);
 //对链表进行初始化
 LTNode *LTInit();
 //打印链表
 void LTPrint(LTNode* phead);
 
 //尾部插入数据
 void LTpushBack(LTNode *phead,LTDateType x);
 //尾部删除数据
 void LTpopBack(LTNode* phead);
 
 //头部插入数据
 void LTpushFront(LTNode *phead,LTDateType x);
 //头部删除数据
 void LTpopFront(LTNode* phead);
 
 //计算链表中有效数据的个数
 int LTSize(LTNode* phead);
 
 //查找数据值为x的节点
 LTNode *LTFind(LTNode *phead,LTDateType x);
 //在pos节点前插入数据为x的节点
 void LTInsert(LTNode* pos, LTDateType x);
 //删除pos位置处的节点
 void LTErase(LTNode* pos);
 
 //销毁链表
 void LTDestroy(LTNode* phead);
  • 函数实现文件:
 #include "List.h"
 
 LTNode *BuyLTNode(LTDateType x)
 {
     LTNode *node= (LTNode*)malloc(sizeof (LTNode));
     if(node==NULL)
     {
         perror("malloc fail");
         exit(-1);
     }
     node->date=x;
     node->next=NULL;
     node->prev=NULL;
 
     return node;
 }
 
 LTNode *LTInit()
 {
     LTNode *phead= BuyLTNode(-1);
     phead->prev=phead;
     phead->next=phead;
 
     return phead;
 }
 
 void LTPrint(LTNode* phead)
 {
     assert(phead);
 
     printf("phead<->");
     LTNode *cur=phead->next;
     while(cur!=phead)
     {
         printf("%d<->",cur->date);
         cur=cur->next;
     }
     printf("phead\n");
 }
 
 void LTpushBack(LTNode *phead,LTDateType x)
 {
     //判断phead是否为空指针
     assert(phead);
     //尾节点如下,无需再去遍历寻找尾节点
     LTNode *tail=phead->prev;
     //创建一个新节点
     LTNode *newnode=BuyLTNode(x);
 
     //改变指针指向,将尾节点与新节点相连
     newnode->prev=tail;
     tail->next=newnode;
 
     newnode->next=phead;
     phead->prev=newnode;
 
 }
 
 void LTpopBack(LTNode* phead)
 {
     assert(phead);
 
     if (phead->next == phead)
         exit(-1);
     else
     {
         LTNode *tail=phead->prev;
         phead->prev=tail->prev;
         phead->prev->next=phead;
         free(tail);
     }
 }
 
 void LTpushFront(LTNode *phead,LTDateType x)
 {
     assert(phead);
 /*  LTNode *newnode= BuyLTNode(x);
 
     newnode->next=phead->next;
     phead->next->prev=newnode;
 
     phead->next=newnode;
     newnode->prev=phead;
 */
     LTNode *newnode= BuyLTNode(x);
     LTNode *first=phead->next;
 
     phead->next=newnode;
     newnode->prev=phead;
     newnode->next=first;
     first->prev=newnode;
 
 }
 
 void LTpopFront(LTNode* phead)
 {
     assert(phead);
 
     if (phead->next == phead)
         exit(-1);
     else
     {
         /* LTNode *first=phead->next;
         phead->next=first->next;
         phead->next->prev=phead;
         free(first);*/
         LTNode *first=phead->next;
         LTNode *second=first->next;
         free(first);
         phead->next=second;
         second->prev=phead;
     }
 
 }
 
 int LTSize(LTNode* phead)
 {
     assert(phead);
     int size=0;
     LTNode *cur=phead->next;
     while(cur!=phead)
     {
         size++;
         cur=cur->next;
     }
     return size;
 }
 
 LTNode *LTFind(LTNode *phead,LTDateType x)
 {
     assert(phead);
 
     LTNode *cur=phead->next;
     while(cur!=phead)
     {
         if(cur->date==x)
             return cur;
         cur=cur->next;
     }
 
     return NULL;
 
 }
 
 void LTInsert(LTNode* pos, LTDateType x)
 {
     assert(pos);
 
     LTNode *posPrev=pos->prev;
     LTNode *newnode= BuyLTNode(x);
 
     posPrev->next=newnode;
     newnode->prev=posPrev;
     newnode->next=pos;
     pos->prev=newnode;
 
 }
 
 void LTErase(LTNode* pos)
 {
     assert(pos);
 
 /*    pos->prev->next=pos->next;
     pos->next->prev=pos->prev;
 
     free(pos);*/
     LTNode *posPrev=pos->prev;
     LTNode *posNext=pos->next;
 
     free(pos);
 
     posPrev->next=posNext;
     posNext->prev=posPrev;
 
 }
 
 void LTDestroy(LTNode* phead)
 {
     assert(phead);
 
     LTNode *cur=phead->next;
     while(cur!=phead)
     {
         LTNode *next=cur->next;
         free(cur);
 
         cur=next;
     }
 
     free(phead);
 
 }
 
  • 测试文件:(这里的测试文件,其实就是大家调用写好的函数,看功能是否正确,大家可以自行更改,这里只是一个示例)
 #include "List.h"
 
 void test1()
 {
     LTNode *plist=LTInit();
     LTpushBack(plist,1);
     LTpushBack(plist,2);
     LTpushBack(plist,3);
     LTpushBack(plist,4);
     LTPrint(plist);
     int c= LTSize(plist);
     printf("%d\n",c);
     LTNode *node= LTFind(plist,3);
     LTInsert(node,6);
     LTErase(node);
     LTPrint(plist);
     LTDestroy(plist);
     plist=NULL;
 }
 
 int main()
 {
     test1();
 
     return 0;
 }
 

函数逐个讲解:

  1. 第一个动态申请链表节点:
 
 LTNode *BuyLTNode(LTDateType x)
 {
     LTNode *node= (LTNode*)malloc(sizeof (LTNode));
     if(node==NULL)
     {
         perror("malloc fail");
         exit(-1);
     }
     node->date=x;
     node->next=NULL;
     node->prev=NULL;
 
     return node;
 }
  • 这个函数没什么好说的,就是用malloc和sizeof来申请节点,如果申请成功就赋值,失败则exit。
  1. 初始化函数:
LTNode *LTInit()
{
    LTNode *phead= BuyLTNode(-1);
    phead->prev=phead;
    phead->next=phead;

    return phead;
}
  • 在这里插入图片描述
  1. 打印函数:

    void LTPrint(LTNode* phead)
    {
        assert(phead);
    
        printf("phead<->");
        LTNode *cur=phead->next;
        while(cur!=phead)
        {
            printf("%d<->",cur->date);
            cur=cur->next;
        }
        printf("phead\n");
    }
    

在这里插入图片描述
4. 尾部插入函数:

void LTpushBack(LTNode *phead,LTDateType x)
{
    //判断phead是否为空指针
    assert(phead);
    //尾节点如下,无需再去遍历寻找尾节点
    LTNode *tail=phead->prev;
    //创建一个新节点
    LTNode *newnode=BuyLTNode(x);

    //改变指针指向,将尾节点与新节点相连
    newnode->prev=tail;
    tail->next=newnode;

    newnode->next=phead;
    phead->prev=newnode;

}

在这里插入图片描述
5. 尾部删除函数

void LTpopBack(LTNode* phead)
{
    assert(phead);

    if (phead->next == phead)
        exit(-1);
    else
    {
        LTNode *tail=phead->prev;
        phead->prev=tail->prev;
        phead->prev->next=phead;
        free(tail);
    }
}

在这里插入图片描述
6. 头部插入函数

void LTpushFront(LTNode *phead,LTDateType x)
{
    assert(phead);
/*  LTNode *newnode= BuyLTNode(x);

    newnode->next=phead->next;
    phead->next->prev=newnode;

    phead->next=newnode;
    newnode->prev=phead;
*/
    LTNode *newnode= BuyLTNode(x);
    LTNode *first=phead->next;

    phead->next=newnode;
    newnode->prev=phead;
    newnode->next=first;
    first->prev=newnode;

}

在这里插入图片描述
7. 头部删除函数

void LTpopFront(LTNode* phead)
{
    assert(phead);

    if (phead->next == phead)
        exit(-1);
    else
    {
        /* LTNode *first=phead->next;
        phead->next=first->next;
        phead->next->prev=phead;
        free(first);*/
        LTNode *first=phead->next;
        LTNode *second=first->next;
        free(first);
        phead->next=second;
        second->prev=phead;
    }

}

在这里插入图片描述
8. 计算有效节点个数

int LTSize(LTNode* phead)
{
    assert(phead);
    int size=0;
    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        size++;
        cur=cur->next;
    }
    return size;
}

在这里插入图片描述
9. 查找数据的值为x的节点

LTNode *LTFind(LTNode *phead,LTDateType x)
{
    assert(phead);

    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        if(cur->date==x)
            return cur;
        cur=cur->next;
    }

    return NULL;

}
  • 这里这个函数功能和上一个函数,也就是计算有效个数函数和打印函数实现逻辑类似,就不多说了。
  1. 在pos位置前插入值为x的节点
void LTInsert(LTNode* pos, LTDateType x)
{
    assert(pos);

    LTNode *posPrev=pos->prev;
    LTNode *newnode= BuyLTNode(x);

    posPrev->next=newnode;
    newnode->prev=posPrev;
    newnode->next=pos;
    pos->prev=newnode;

}

在这里插入图片描述

  • 这个函数也可以复用在尾插和头插函数里
  1. 删除pos位置的节点

void LTErase(LTNode* pos)
{
    assert(pos);

/*    pos->prev->next=pos->next;
    pos->next->prev=pos->prev;

    free(pos);*/
    LTNode *posPrev=pos->prev;
    LTNode *posNext=pos->next;

    free(pos);

    posPrev->next=posNext;
    posNext->prev=posPrev;

}

在这里插入图片描述

  • 这个函数也可以复用在尾删和头删里
  1. 链表销毁函数
void LTDestroy(LTNode* phead)
{
    assert(phead);

    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        LTNode *next=cur->next;
        free(cur);

        cur=next;
    }


}

在这里插入图片描述

  • 到此,链表的内容就全部讲完了。

http://www.kler.cn/a/573121.html

相关文章:

  • 自己的网页加一个搜索框,调用deepseek的API
  • 2025-03-04 学习记录--C/C++-PTA 习题5-3 使用函数计算两点间的距离
  • JetBrains学生申请
  • 最新Spring Security实战教程(一)初识Spring Security安全框架
  • 【Python · Pytorch】Conda介绍 DGL-cuda安装
  • 如何配置虚拟机连接finalshell并克隆
  • AI---DevOps常备工具(‌AI-Integrated DevOps Essential Tools)
  • NO.24十六届蓝桥杯备战|二维数组八道练习|杨辉三角|矩阵(C++)
  • 力扣-字符串
  • 设计模式|策略模式 Strategy Pattern 详解
  • EIF加载---虚拟物理地址加载
  • Gartner:数据安全平台DSP提升数据流转及使用安全
  • 【损失函数_模型结构与前向传播的数学建模】
  • 加油站小程序实战教程07城市管理
  • 【Proteus仿真】【51单片机】图书馆照明及环境控制系统
  • (50)[HGAME 2023 week2]before_main
  • 全网独家:zabbixV7版本容器服务器无法访问Postgres V17数据库的问题解决
  • UNIAPP前端配合thinkphp5后端通过高德API获取当前城市天气预报
  • 磐石云AXB小号平台——安全与隐私的守护者
  • C++第八节:继承