链表
链表的特点是通过指针连接其元素,不需要在内存中连续存储。它可以分成:
- 单向链表:每个节点包含一个数据字段和一个指向链表中下一个节点的指针。
- 双向链表:每个节点不仅包含指向下一个节点的指针,还包含一个指向前一个节点的指针。
- 循环链表:单向或双向链表的变体,其中尾部节点的下一个指针指向头部节点,形成一个环。
他的特点是插入和删除的效率高,为O(1)。但是不支持随机访问,必须以O(n)的代价遍历。在大多数情况下,这不是一个非常有用的数据结构,数组往往是一个更好的选择。
侵入式链表
值得一提的是链表的侵入式实现方式。链表的链接指针(例如next
和prev
指针)被直接嵌入到数据元素中。这意味着数据元素自身包含了链接到其他元素的信息。光看文字描述可能有点迷惑,看一下代码,这是我们熟悉的非侵入式的链表:
侵入式链表在Linux内核被广泛使用,大概是下面这个样子(定义在types.h):
可以理解为,侵入式链表由数据管理指针,非侵入式链表由链表管理指针。侵入式链表的设计能够减少内存碎片,提高缓存命中率。
算法题
在做Leetcode的时候,我发现双指针和链表反转几乎是所有链表算法题的基础。
链表反转对于单链表来说确实比较烦人,可以把模板背下来:
// 将从[start, end)范围内的链表反转
ListNode *reverse(ListNode *prev, ListNode *start, ListNode *end) {
ListNode *curr = start;
while (curr != end) {
ListNode *next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
- 206. 反转链表 - 力扣(LeetCode): 如果要反转整条链表,就是
reverse(nullptr, head, nullptr)