概述
由一系列 节点 通过指针连接在一起形成的线性结构,称为 链表
简单图示(C++)

特性
- 随机存储 —— 存储空间不一定连续:链表在内存中不是连续分布,分配机制取决于操作系统的内存管理,资源申请方面一般是先申请节点资源,通过指针把各个结点串成链表,资源释放方面也是以结点为单位
- 不定长:由于结构相比数组较为松散,以节点为单位来管理资源,因此链表长度灵活,增删方便
链表种类
- 单链表(一个指针,指向后一个结点)
- 双链表(两个指针,分别指向前一个结点和后一个结点)
- 循环链表(最后结点的指针指向开头结点)
- …
实现(C++)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| struct Node { int data; Node* next;
Node(int dat, Node* nxt = nullptr) : data(dat), next(nxt) {} };
class List { public: List() : virtualHead_(new Node(-1)), size_(0) {} ~List() { Node* cur = virtualHead_->next; while (cur != nullptr) { Node* tmp = cur; cur = cur->next; delete tmp; --size_; }
delete virtualHead_; }
void insert(int data, int index = 0) { if (index < 0 || index > size_) return; Node* preNode = indexToNode(index - 1); Node* newNode = new Node(data, preNode->next); preNode->next = newNode; ++size_; } void erase(int index) { if (index < 0 || index >= size_) return; Node* preNode = indexToNode(index - 1); Node* cur = indexToNode(index); if (cur == nullptr) return; preNode->next = cur->next; delete cur; --size_; } int data(int index) { Node* cur = indexToNode(index); if (cur) return cur->data; return -1; } int size() { return size_; }
private: Node* indexToNode(int index) { Node* node = virtualHead_; if (index < 0 || index > size_) return node; int cnt = 0; while (node != nullptr && cnt <= index) { node = node->next; ++cnt; } return node; }
private: Node* virtualHead_ = nullptr; int size_; };
|
复杂度分析(部分操作)
| 操作 |
时间复杂度 |
| 访问/查找元素 |
O(n) |
| 插入数据 |
O(1) |
| 删除数据 |
O(1) |
| 扩容 |
O(1) |
发散/随想/自身理解(?)
- 链表是线性的,在检索数据方面较慢,需要从头结点开始遍历找到目标结点; 但长度很灵活,增删方面比数组优
- 相比数组,需要额外存储指针结构
介绍链表不如说是介绍链表的节点结构,这个在我看来才是主角,链表是节点的一种组织形式
- 虚头节点,便于管理链表增删,视情况添加