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

简单的双向循环链表实现与使用指南

CnLinkedList:双向循环链表实现与使用指南

概述

CnLinkedList是一个轻量级的双向循环链表实现,具有自包含节点结构的特点。每个节点既作为数据载体,又负责维护链表的连接关系,无需额外的链表管理对象。这种设计使得链表操作高效且内存使用紧凑,适合需要频繁插入、删除节点的场景。

类定义与实现

class CnLinkedList
{
public:CnLinkedList() {m_pcPrev = this;m_pcNext = this;}virtual ~CnLinkedList() {}inline void InsertPrev(CnLinkedList* pcLinkedList) {pcLinkedList->m_pcPrev = m_pcPrev;pcLinkedList->m_pcNext = this;m_pcPrev->m_pcNext = pcLinkedList;m_pcPrev = pcLinkedList;}inline CnLinkedList* RemovePrev() {CnLinkedList* pcPrev = m_pcPrev;if (this != pcPrev) {m_pcPrev = pcPrev->m_pcPrev;pcPrev->m_pcPrev->m_pcNext = this;}pcPrev->m_pcPrev = pcPrev;pcPrev->m_pcNext = pcPrev;return pcPrev;}inline CnLinkedList* GetPrev() const { return m_pcPrev; }inline void InsertNext(CnLinkedList* pcLinkedList) {pcLinkedList->m_pcPrev = this;pcLinkedList->m_pcNext = m_pcNext;m_pcNext->m_pcPrev = pcLinkedList;m_pcNext = pcLinkedList;}inline CnLinkedList* RemoveNext() {CnLinkedList* pcNext = m_pcNext;if (this != m_pcNext) {m_pcNext = pcNext->m_pcNext;pcNext->m_pcNext->m_pcPrev = this;}pcNext->m_pcPrev = pcNext;pcNext->m_pcNext = pcNext;return pcNext;}inline CnLinkedList* GetNext() const { return m_pcNext; }void RemoveFromLinkedList() {// 无论链表长度如何,都更新前后节点的指针关系m_pcPrev->m_pcNext = m_pcNext;m_pcNext->m_pcPrev = m_pcPrev;// 将当前节点恢复为自循环状态m_pcPrev = this;m_pcNext = this;}u32 LinkedListSize() const {u32 dwSize = 1;CnLinkedList* pcTemp = m_pcNext;while (this != pcTemp) {dwSize++;pcTemp = pcTemp->m_pcNext;}return dwSize;}private:CnLinkedList* m_pcPrev;CnLinkedList* m_pcNext;
};

核心功能解析

1. 构造函数

CnLinkedList() {m_pcPrev = this;m_pcNext = this;
}

初始化时,节点的m_pcPrevm_pcNext均指向自身,形成一个单个节点的循环链表。

2. 插入操作

  • InsertPrev: 在当前节点前插入新节点

    void InsertPrev(CnLinkedList* pcLinkedList)
    
  • InsertNext: 在当前节点后插入新节点

    void InsertNext(CnLinkedList* pcLinkedList)
    

插入操作通过调整前后节点的指针关系来完成,时间复杂度为O(1)。

3. 删除操作

  • RemovePrev: 移除当前节点的前一个节点并返回

    CnLinkedList* RemovePrev()
    
  • RemoveNext: 移除当前节点的后一个节点并返回

    CnLinkedList* RemoveNext()
    
  • RemoveFromLinkedList: 当前节点主动从链表中脱离

    void RemoveFromLinkedList()
    

删除操作会将被移除的节点重置为自循环状态,避免野指针问题,时间复杂度为O(1)。

4. 辅助方法

  • GetPrev(): 获取前一个节点
  • GetNext(): 获取后一个节点
  • LinkedListSize(): 计算链表包含的节点总数,时间复杂度为O(n)

使用示例

1. 创建带数据的节点类

#include <iostream>
#include "CnLinkedList.h"// 创建一个带数据的链表节点
class DataNode : public CnLinkedList {
public:int value;DataNode(int val) : value(val) {}
};

2. 链表遍历函数

// 打印链表内容(从指定节点开始遍历)
void printList(CnLinkedList* startNode) {if (!startNode) return;CnLinkedList* current = startNode;DataNode* dataNode = static_cast<DataNode*>(current);std::cout << "链表内容: " << dataNode->value;current = current->GetNext();while (current != startNode) {dataNode = static_cast<DataNode*>(current);std::cout << " -> " << dataNode->value;current = current->GetNext();}std::cout << " (循环结束)" << std::endl;
}

3. 主要操作示例

int main() {// 1. 创建节点DataNode* node1 = new DataNode(10);DataNode* node2 = new DataNode(20);DataNode* node3 = new DataNode(30);DataNode* node4 = new DataNode(40);std::cout << "初始节点1的值: " << node1->value << ", 链表长度: " << node1->LinkedListSize() << std::endl;// 2. 插入节点node1->InsertNext(node2);  // 在node1后插入node2std::cout << "插入node2后,node1的链表长度: " << node1->LinkedListSize() << std::endl;printList(node1);node2->InsertNext(node3);  // 在node2后插入node3node1->InsertPrev(node4);  // 在node1前插入node4std::cout << "插入node3和node4后,链表长度: " << node1->LinkedListSize() << std::endl;printList(node1);// 3. 移除节点CnLinkedList* removed = node1->RemoveNext();  // 移除node1的下一个节点(node2)std::cout << "移除node2后,链表长度: " << node1->LinkedListSize() << std::endl;printList(node1);std::cout << "被移除的节点值: " << static_cast<DataNode*>(removed)->value << std::endl;// 4. 节点自移除node4->RemoveFromLinkedList();  // node4主动从链表中移除std::cout << "node4自移除后,链表长度: " << node1->LinkedListSize() << std::endl;printList(node1);// 5. 遍历链表(从node3开始)std::cout << "从node3开始遍历: ";printList(node3);// 清理内存delete node1;delete node2;delete node3;delete node4;return 0;
}

4. 预期输出

初始节点1的值: 10, 链表长度: 1
插入node2后,node1的链表长度: 2
链表内容: 10 -> 20 (循环结束)
插入node3和node4后,链表长度: 4
链表内容: 10 -> 20 -> 30 -> 40 (循环结束)
移除node2后,链表长度: 3
链表内容: 10 -> 30 -> 40 (循环结束)
被移除的节点值: 20
node4自移除后,链表长度: 2
链表内容: 10 -> 30 (循环结束)
从node3开始遍历: 链表内容: 30 -> 10 (循环结束)

适用场景

  • 需要频繁在任意位置插入/删除节点的场景(如游戏对象管理、事件队列)
  • 需支持双向遍历的链表结构
  • 内存受限环境(无额外链表管理对象,节点自包含)

使用建议

  1. 通过继承CnLinkedList实现具体的数据节点类
  2. 遍历链表时,可从任意节点出发,通过GetNext()循环访问,直到回到起点
  3. 节点销毁前建议调用RemoveFromLinkedList(),避免链表结构被破坏
  4. 对于大型链表,LinkedListSize()方法效率较低,可考虑维护一个单独的长度计数器

CnLinkedList以极简的代码提供了双向循环链表的核心功能,适合对性能和内存使用有要求的场景。

http://www.xdnf.cn/news/17686.html

相关文章:

  • Java数据库编程之【JDBC数据库例程】【自动生成报表】【六】
  • Gradient Descent for Logistic Regression|逻辑回归梯度下降
  • Qwen-OCR:开源OCR技术的演进与全面分析
  • 【数据结构】——顺序表链表(超详细解析!!!)
  • Flink运行时的实现细节
  • COAT: 压缩优化器状态和激活以实现内存高效的FP8训练
  • apache+虚拟主机
  • @(AJAX)
  • 使用Spring Boot对接欧州OCPP1.6充电桩:解决WebSocket连接自动断开问题
  • 日志管理--g3log
  • 前端项目一键换肤
  • IEEE 2025 | 重磅开源!SLAM框架用“法向量+LRU缓存”,将三维重建效率飙升72%!
  • 单例模式,动态代理,微服务原理
  • 操作系统1.6:虚拟机
  • 从原理到实践:一文掌握Kafka的消息生产与消费
  • 【bug 解决】串口输出字符乱码的问题
  • pdftk - macOS 上安装使用
  • 干货分享|如何从0到1掌握R语言数据分析
  • OpenAI传来捷报,刚刚夺金IOI,实现通用推理模型的跨越式突破
  • 如何实现PostgreSQL的高可用性,包括主流的复制方案、负载均衡方法以及故障转移流程?
  • 【接口自动化】-11-接口加密签名 全局设置封装
  • 容器安全扫描工具在海外云服务器环境的集成方法
  • Element用法---Loading 加载
  • npm、pnpm、yarn区别
  • 一周学会Matplotlib3 Python 数据可视化-绘制饼状图(Pie)
  • 前沿技术借鉴研讨-2025.8.12 (数据不平衡问题)
  • Web项目Excel文件处理:前端 vs. 后端,企业级如何选择?
  • 【3】Transformers快速入门:大语言模型LLM是啥?
  • 11-docker单机版的容器编排工具docker-compose基本使用
  • centos 7 如何安装 ZipArchive 扩展