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

十二,数据结构-链表

定义

链表是最简单的基于节点的数据结构。节点表示散步在内存中的互相连接的数据,在链表中,每个节点都表示其中的一个元素。这个元素中除了信息内容之外,还有指向下一个/前一个节点的内存地址信息(指针形式)。

实现

这里先考虑单向链表的实现。首先是链表节点的实现:

class Node {
public:int data;unique_ptr<Node> nextNode;Node(int val) : data(val), nextNode(nullptr) {}
};

其实只使用上述的节点类即可以创建链表,但还需要一个简单的方法告知链表的开始位置,所以再创建一个LinkedList类,链表尾节点的下一节点指针为空,即nullptr。

class LinkedList {
private:unique_ptr<Node> firstNode;public:LinkedList(Node* node) : firstNode{ node } {}
};

链表的读取

数组的读取时间复杂度为O(1),而上述链表由于只记录了链表的第一个位置,因此只能遍历整个链表以读取指定位置的节点,其时间复杂度为O(N)。

	int read(int index) const {if (index < 0) {throw out_of_range("Index must be non-negative.");}Node* current = firstNode.get();int count = 0;while (current) {if (count == index) {return current->data;}current = current->nextNode.get();++count;}throw out_of_range("Index exceeds list length.");}

链表的查找

和读取过程类似,链表的查找也是从第一个节点开始遍历所有节点,并检查节点的值是否就是要查找的值。

	int indexOf(int value) const {Node* current = firstNode.get();int count = 0;while (current) {if (current->data == value) {return count;}current = current->nextNode.get();++count;}throw out_of_range("Index exceeds list length.");}

链表的插入

特定情况下,链表插入速度优于数组。当需要在数组头插入数据时由于需要右移所有元素,时间复杂度是O(N),但在链表头插入时间复杂度为O(1)。理论上在链表的任何位置插入一个节点,时间复杂度为O(1),但插入前还有个查找的过程,因此当需要在链表尾插入数据时,则其时间复杂度为O(N+1)。

	void insert(int index, int val) {if (index < 0) {throw out_of_range("Index must be non-negative.");}auto newNode = make_unique<Node>(val);if (index == 0) {newNode->nextNode = move(firstNode);firstNode = move(newNode);return;}Node* current = firstNode.get();int count = 0;while (current && count < index - 1) {current = current->nextNode.get();count++;}if (!current) {throw out_of_range("Index exceeds list length.");}newNode->nextNode = move(current->nextNode);current->nextNode = move(newNode);}

链表的删除

和插入一样,链表的删除也有其优势,即删除表头节点时,只需要让链表的firstNode指向第二个节点。而删除最后一个节点时,其步骤也只需要一步,但因为需要先遍历到最后一个节点,其时间复杂度仍然为O(N)。

	void removeAt(int index) {if (index < 0) {throw out_of_range("Index must be non-negative.");}if (!firstNode) {throw out_of_range("List is empty.");}if (index == 0) {firstNode = move(firstNode->nextNode);return;}Node* current = firstNode.get();int count = 0;while (current->nextNode && count < index - 1) {current = current->nextNode.get();count++;}if (!current->nextNode || count != index - 1) {throw out_of_range("Index exceeds list length.");}current->nextNode = move(current->nextNode->nextNode);}

如果设置为双链表的话,则在链表头或则链表尾进行删除和插入操作,都只需要花O(1)时间,和单链表不同的是,双链表节点需要额外存储前一个节点的内存地址的指针,同时双链表还要知道最后一个节点。双链表实现如下:

class DNode {
public:int data;unique_ptr<DNode> next;DNode* prev;DNode(int val) : data(val), next(nullptr), prev(nullptr) {}
};class DoublyLinkedList {
private:unique_ptr<DNode> head;DNode* tail;public:DoublyLinkedList() : head(nullptr), tail(nullptr) {}void append(int val) {auto newNode = make_unique<DNode>(val);if (!head) {tail = newNode.get();head = move(newNode);return;}newNode->prev = tail;tail->next = move(newNode);tail = tail->next.get();}void insert(int index, int val) {if (index < 0) throw out_of_range("Index must be non-negative.");auto newNode = make_unique<DNode>(val);if (index == 0) {newNode->next = move(head);if (newNode->next) newNode->next->prev = newNode.get();head = move(newNode);if (!tail) tail = head.get();return;}DNode* current = head.get();int count = 0;while (current && count < index - 1) {current = current->next.get();count++;}if (!current) throw out_of_range("Index exceeds list length.");newNode->next = move(current->next);if (newNode->next) newNode->next->prev = newNode.get();newNode->prev = current;current->next = move(newNode);if (!current->next->next) tail = current->next.get();}void removeAt(int index) {if (index < 0 || !head) throw out_of_range("Invalid index or empty list.");if (index == 0) {head = move(head->next);if (head) head->prev = nullptr;else tail = nullptr;return;}DNode* current = head.get();int count = 0;while (current && count < index) {current = current->next.get();count++;}if (!current) throw out_of_range("Index exceeds list length.");DNode* prevNode = current->prev;if (current->next) {current->next->prev = prevNode;prevNode->next = move(current->next);}else {prevNode->next = nullptr;tail = prevNode;}}
};

前文中提到的抽象数据结构队列也可以用双链表实现,不仅满足FIFO,同时其效率为O(1)。这里就不再赘述其实现。

http://www.lryc.cn/news/626791.html

相关文章:

  • Linux用30秒部署Nginx+Tomcat+Mysql+Jdk1.8环境
  • 学习嵌入式的第二十二天——数据结构——双向链表
  • 为6G和超快光谱铺路,《Nature Communications》发布新型太赫兹光芯片,实现多通道信号操纵
  • AI 效应: GPT-6,“用户真正想要的是记忆”
  • 书籍推荐|《Computational Methods for Rational Drug Design》574页
  • React响应式链路
  • CAMEL-Task1-CAMEL环境配置及你的第一个Agent
  • uniapp学习【上手篇】
  • CF每日4题(1500-1700)
  • 基于单片机水质检测系统/污水监测系统/水情监测
  • HTTP的协议
  • Git Commit 提交信息标准格式
  • GIT总结一键式命令清单(顺序执行)
  • 分布式唯一 ID 生成方案
  • C++高频知识点(三十)
  • [Mysql数据库] 用户管理选择题
  • macos 多个版本的jdk
  • 如何在高并发下,保证共享数据的一致性
  • 如何制作免费的比特币冷钱包
  • 自我探索之旅:哲学人格测试H5案例赏析
  • YT8512C拓展寄存器配置方式
  • 机器学习数学基础与商业实践指南:从统计显著性到预测能力的认知升级
  • 设计模式的一些笔记
  • 对抗式域适应 (Adversarial Domain Adaptation)
  • 零基础学Java第二十一讲---异常(1)
  • 卸载win10/win11系统里导致磁盘故障的补丁
  • CorrectNav——基于VLM构建带“自我纠正飞轮”的VLN:通过视觉输入和语言指令预测导航动作,且从动作和感知层面生成自我修正数据
  • 有关SWD 仿真和PA.15, PB3, PB4的冲突问题
  • 基于STM32单片机的温湿度采集循迹避障APP小车
  • 关于uniappx注意点1 - 鸿蒙app