数据结构与算法:带头双向循环链表(c语言实现,带详细注释)

数据结构与算法:带头双向循环链表(c语言实现,带详细注释)

最实用链表,完美结构,具体实现如下所示:

//Dlist.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DLDataType;

typedef struct DListNode
{
    struct DListNode *pNext;
    struct DListNode *pPrev;
    DLDataType data;

}DLNode, *pDLNode;

pDLNode Built_ListNode(DLDataType value);
pDLNode Init_DList();
void DList_PushBack(pDLNode pHead, DLDataType value);
void DList_PushFront(pDLNode pHead, DLDataType value);
void DList_PopBack(pDLNode pHead);
void DList_PopFront(pDLNode pHead);
void DList_Replace(pDLNode pos, DLDataType value);
void DList_Insert(pDLNode pos, DLDataType value);
void DList_Erase(pDLNode pos);
pDLNode DList_Find(pDLNode pHead, DLDataType value);
void DList_Print(pDLNode pHead);
void DList_Destory(pDLNode pHead);
//Dlist.c
#include "Dlist.h"

// 双向链表节点结构体定义
typedef int DLDataType; // 假设数据类型为int
typedef struct DLNode
{
    DLDataType data;     // 数据域
    struct DLNode* pNext; // 指向下一个节点的指针
    struct DLNode* pPrev; // 指向前一个节点的指针
} DLNode, *pDLNode; // 定义节点和节点指针类型

// 创建一个新的双向链表节点并返回其指针
pDLNode Built_ListNode(DLDataType value)
{
    pDLNode newnode = (pDLNode)malloc(sizeof(DLNode)); // 分配内存来存储新节点
    newnode->data = value; // 初始化节点的数据
    newnode->pNext = NULL; // 将pNext指针初始化为NULL
    newnode->pPrev = NULL; // 将pPrev指针初始化为NULL
    return newnode; // 返回新节点的指针
}

// 初始化一个空的双向链表并返回虚拟头尾节点的指针
pDLNode Init_DList()
{
    pDLNode pHead = Built_ListNode(0); // 创建虚拟头尾节点,并将其数据值初始化为0
    pHead->pNext = pHead; // 将pNext指针指向自身,形成循环链表
    pHead->pPrev = pHead; // 将pPrev指针指向自身,形成循环链表
    return pHead; // 返回虚拟头尾节点的指针
}

// 在双向链表末尾添加一个新节点,其数据值为value
void DList_PushBack(pDLNode pHead, DLDataType value)
{
    DList_Insert(pHead->pPrev, value); // 调用DList_Insert在虚拟头尾节点之前插入新节点
}

// 在双向链表开头添加一个新节点,其数据值为value
void DList_PushFront(pDLNode pHead, DLDataType value)
{
    DList_Insert(pHead->pNext, value); // 调用DList_Insert在虚拟头尾节点之后插入新节点
}

// 从双向链表末尾移除最后一个节点
void DList_PopBack(pDLNode pHead)
{
    DList_Erase(pHead->pPrev); // 调用DList_Erase移除虚拟头尾节点之前的节点
}

// 从双向链表开头移除第一个节点
void DList_PopFront(pDLNode pHead)
{
    DList_Erase(pHead->pNext); // 调用DList_Erase移除虚拟头尾节点之后的节点
}

// 替换指定节点pos的数据值为value
void DList_Replace(pDLNode pos, DLDataType value)
{
    assert(pos); // 断言指定的节点pos不为空
    pDLNode newnode = Built_ListNode(value); // 创建一个新节点,数据值为value
    pDLNode pos_prev = pos->pPrev; // 获取指定节点pos的前一个节点
    pDLNode pos_next = pos->pNext; // 获取指定节点pos的后一个节点
    pos_prev->pNext = newnode; // 将前一个节点的pNext指针指向新节点
    newnode->pPrev = pos_prev; // 将新节点的pPrev指针指向前一个节点
    pos_next->pPrev = newnode; // 将后一个节点的pPrev指针指向新节点
    newnode->pNext = pos_next; // 将新节点的pNext指针指向后一个节点
}

// 在指定节点pos之后插入一个新节点,其数据值为value
void DList_Insert(pDLNode pos, DLDataType value)
{
    assert(pos); // 断言指定的节点pos不为空
    pDLNode pos_prev = pos->pPrev; // 获取指定节点pos的前一个节点
    pDLNode newnode = Built_ListNode(value); // 创建一个新节点,数据值为value
    pos_prev->pNext = newnode; // 将前一个节点的pNext指针指向新节点
    newnode->pPrev = pos_prev; // 将新节点的pPrev指针指向前一个节点
    newnode->pNext = pos; // 将新节点的pNext指针指向指定节点pos
    pos->pPrev = newnode; // 将指定节点pos的pPrev指针指向新节点
}

// 移除指定节点pos
void DList_Erase(pDLNode pos)
{
    assert(pos); // 断言指定的节点pos不为空
    pDLNode pos_prev = pos->pPrev; // 获取指定节点pos的前一个节点
    pDLNode pos_next = pos->pNext; // 获取指定节点pos的后一个节点
    pos_prev->pNext = pos_next; // 将前一个节点的pNext指针指向后一个节点
    pos_next->pPrev = pos_prev; // 将后一个节点的pPrev指针指向前一个节点
    free(pos); // 释放指定节点pos的内存
    pos = NULL; // 将指定节点pos指向NULL,防止悬挂指针
}

// 在双向链表中查找具有指定数据值的节点,并返回该节点的指针,若未找到返回NULL
pDLNode DList_Find(pDLNode pHead, DLDataType value)
{
    pDLNode cur = pHead->pNext; // 从第一个真实节点开始查找
    while (cur != pHead) // 遍历整个链表,直到回到虚拟头尾节点
    {
        if (cur->data == value) // 若找到具有指定数据值的节点
        {
            return cur; // 返回该节点的指针
        }
        cur = cur->pNext; // 继续向后遍历
    }
    return NULL; // 若未找到,返回NULL
}

// 打印双向链表中所有节点的数据值
void DList_Print(pDLNode pHead)
{
    pDLNode cur = pHead->pNext; // 从第一个真实节点开始打印
    while (cur !=

 pHead) // 遍历整个链表,直到回到虚拟头尾节点
    {
        printf("%d ", cur->data); // 打印当前节点的数据值
        cur = cur->pNext; // 继续向后遍历
    }
    printf("\n"); // 换行,输出完整链表后换行
}

// 释放双向链表的内存,包括所有节点
void DList_Destroy(pDLNode pHead)
{
    assert(pHead); // 断言虚拟头尾节点不为空
    pDLNode cur = pHead->pNext; // 从第一个真实节点开始释放
    while (cur != pHead) // 遍历整个链表,直到回到虚拟头尾节点
    {
        pDLNode cur_next = cur->pNext; // 保存当前节点的下一个节点
        free(cur); // 释放当前节点的内存
        cur = cur_next; // 继续向后遍历
    }
    free(pHead); // 释放虚拟头尾节点的内存
}

// 测试函数1:演示基本操作,如尾部和头部插入节点以及尾部和头部移除节点
void test01()
{
    pDLNode dlist = Init_DList(); // 初始化双向链表
    DList_PushBack(dlist, 1); // 尾部插入节点,数据值为1
    DList_PushBack(dlist, 2); // 尾部插入节点,数据值为2
    DList_PushBack(dlist, 3); // 尾部插入节点,数据值为3
    DList_PushBack(dlist, 4); // 尾部插入节点,数据值为4
    DList_Print(dlist); // 打印链表的数据值:1 2 3 4
    DList_PushFront(dlist, 0); // 头部插入节点,数据值为0
    DList_PushFront(dlist, -1); // 头部插入节点,数据值为-1
    DList_PushFront(dlist, -2); // 头部插入节点,数据值为-2
    DList_Print(dlist); // 打印链表的数据值:-2 -1 0 1 2 3 4
    DList_PopBack(dlist); // 尾部移除节点
    DList_Print(dlist); // 打印链表的数据值:-2 -1 0 1 2 3
    DList_PopFront(dlist); // 头部移除节点
    DList_Print(dlist); // 打印链表的数据值:-1 0 1 2 3
    DList_PopBack(dlist); // 尾部移除节点
    DList_Print(dlist); // 打印链表的数据值:-1 0 1 2
    DList_PopFront(dlist); // 头部移除节点
    DList_Print(dlist); // 打印链表的数据值:0 1 2
}

int main()
{
    test01(); // 调用测试函数1
    DList_Destroy(dlist); // 释放双向链表的内存
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值