### 链表基本概念
- **Linked List(链表)**:一种线性数据结构,由一系列节点组成。
- **Node(节点)**:链表的基本单元,包含数据和指向下一个节点的指针。
- **Head Node(头节点)**:链表的第一个节点,通常用于访问整个链表。
- **Tail Node(尾节点)**:链表的最后一个节点,其指针通常指向空(`null` 或 `None`)。
- **Next Pointer(下一个指针)**:节点中用于指向下一个节点的指针。
- **Previous Pointer(前一个指针)**:在双向链表中,节点指向前一个节点的指针。
- **Singly Linked List(单链表)**:每个节点只有一个指向下一个节点的指针的链表。
- **Doubly Linked List(双向链表)**:每个节点有指向前一个节点和下一个节点两个指针的链表。
- **Circular Linked List(循环链表)**:尾节点的指针指向头节点,形成一个环的链表。
### 链表操作
- **Insertion(插入)**:在链表中添加新节点的操作。
- **Insert at the Beginning(在头部插入)**:将新节点插入到链表的头部。
- **Insert at the End(在尾部插入)**:将新节点插入到链表的尾部。
- **Insert in the Middle(在中间插入)**:在链表的指定位置插入新节点。
- **Deletion(删除)**:从链表中移除节点的操作。
- **Delete the Head(删除头节点)**:移除链表的第一个节点。
- **Delete the Tail(删除尾节点)**:移除链表的最后一个节点。
- **Delete a Specific Node(删除特定节点)**:移除链表中指定值或位置的节点。
- **Search(搜索)**:在链表中查找具有特定值的节点。
- **Traversal(遍历)**:按顺序访问链表中的每个节点。
- **Reverse(反转)**:将链表中节点的顺序颠倒。
- **Merge(合并)**:将两个或多个链表合并为一个。
- **Split(拆分)**:将一个链表分成两个或多个子链表。
### 解题相关概念
- **Pointer Manipulation(指针操作)**:通过操作指针来改变链表的结构或访问节点。
- **Dummy Node(虚拟节点)**:一个额外的节点,通常用于简化链表操作,尤其是在头部插入或删除时。
- **Fast and Slow Pointers(快慢指针)**:使用两个指针,一个移动速度快,一个移动速度慢,用于解决如查找中间节点、检测环等问题。
- **Cycle Detection(环检测)**:判断链表中是否存在环。
- **Intersection(相交)**:判断两个链表是否相交,并找到相交节点。
- **Sorting(排序)**:对链表中的节点按值进行排序。
### 代码实现相关
- **Function(函数)**:实现特定链表操作的代码块。
- **Parameter(参数)**:传递给函数的变量,如链表头节点、要插入的值等。
- **Return Value(返回值)**:函数执行后返回的结果,可能是新的链表头节点、找到的节点等。
- **Variable(变量)**:用于存储中间结果或临时数据的标识符。
- **Loop(循环)**:如 `while` 循环、`for` 循环,用于遍历链表。
- **Condition(条件)**:如 `if - else` 语句,用于控制程序流程,例如判断指针是否为空。
- **Null(空)**:表示指针不指向任何节点,通常用于判断链表的结束或空链表。
160 相交链表:遍历,统计是否出现过
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// 先把 A 链表的所有结点都访问一遍
unordered_set<ListNode*> visited;
ListNode* temp = headA;
while (temp != nullptr) {
visited.insert(temp);
temp = temp->next;
}
temp = headB;
while (temp != nullptr) {
if (visited.count(temp)) {
return temp;
}
visited.insert(temp);
temp = temp->next;
}
return nullptr;
}
};
206 翻转链表:属于链表的基本操作之一
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
234 回文链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
vector<int> vals;
while (head != nullptr) {
vals.push_back(head->val);
head = head->next;
}
for (int i = 0, j = (int)vals.size() - 1; i < j; ++i, --j) {
if(vals[i] != vals[j]) {
return false;
}
}
return true;
}
};
141 环形链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head == NULL || head->next == NULL) return false;
ListNode* fast = head->next;
ListNode* slow = head;
while (slow != fast) {
if (fast == NULL || fast->next == NULL) return false;
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};
142: 环形链表找起点:相遇之后复位再出发
a + n(b + c) = 2(a + b)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while (true) {
if (fast == nullptr || fast->next == nullptr) return nullptr;
fast = fast->next->next;
slow = slow->next;
if (fast == slow) break;
}
fast = head;
while (slow != fast) {
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
21: 合并两个有序链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
// 建立头节点
ListNode* preHead = new ListNode(-1);
// 建立 prev
ListNode* prev = preHead;
// 添加更小的元素进来
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
prev->next = l1;
l1 = l1->next;
} else {
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};
2.两数相加
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 巧用哑节点
ListNode* dummy = new ListNode(0, head);
ListNode* second = dummy;
ListNode* fast = head;
for (int i = 0; i < n; ++i) {
fast = fast->next;
}
while (fast!=nullptr) {
second = second->next;
fast = fast->next;
}
second->next = second->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
25 K 个一组翻转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
// head 和 tail 是实际上需要被翻转的链表的头节点和尾节点
pair<ListNode*, ListNode*> myRev(ListNode* head, ListNode* tail) {
ListNode* prev = tail->next;
ListNode* p = head;
while (prev != tail) {
ListNode* nex = p->next;
p->next = prev;
prev = p;
p = nex;
}
return {tail, head};
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* hair = new ListNode(0, head);
ListNode* pre = hair;
while (head) {
// 移动 tail k 步,到待翻转链表的尾部
ListNode* tail = pre;
for (int i = 0; i < k; i++) {
tail = tail->next;
if (!tail) {
// 不足以翻转,则结束
return hair->next;
}
}
// 储存下一个节点,pre 已经有了
ListNode* nex = tail->next;
// 翻转
pair<ListNode*, ListNode*> result = myRev(head, tail);
// 取头和尾
head = result.first;
tail = result.second;
// 链接
pre->next = head;
tail->next = nex;
// 移动
pre = tail;
head = tail->next;
}
return hair->next;
}
};
随机链表复制
class Solution {
public:
Node* copyRandomList(Node* head) {
if (head == nullptr) {
return nullptr;
}
// 第一步:复制每个节点,并将新节点插入到原节点和下一个原节点之间
for (Node* node = head; node != nullptr; node = node->next->next) {
Node* nodeNew = new Node(node->val);
nodeNew->next = node->next;
node->next = nodeNew;
}
// 第二步:为新节点的 random 指针赋值
for (Node* node = head; node != nullptr; node = node->next->next) {
Node* nodeNew = node->next;
nodeNew->random = (node->random != nullptr) ? node->random->next : nullptr;
}
// 第三步:将交织的链表拆分为两个链表
Node* headNew = head->next;
for (Node* node = head; node != nullptr; node = node->next) {
Node* nodeNew = node->next;
node->next = node->next->next;
nodeNew->next = (nodeNew->next != nullptr) ? nodeNew->next->next : nullptr;
}
return headNew;
}
};
排序链表
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (!head) return nullptr;
// 创建 multiset 并将链表中的值插入其中
multiset<int> set;
ListNode* ptr = head;
while (ptr) {
set.insert(ptr->val);
ptr = ptr->next;
}
// 用排序后的值重建链表
ptr = head;
for (auto it = set.begin(); it != set.end(); it++) {
ptr->val = *it;
ptr = ptr->next;
}
return head;
}
};
146 LRU 缓存
# 定义一个存储数据的类
class DLinkedNode:
def __init__(self, key = 0, value = 0):
# 为什么要 key 和 value
# 只有一个可以不?
self.key = key
self.value = value
# 之前前驱和后继的两个指针
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
# 初始化容量
self.capacity = capacity
self.size = 0
# 初始化头尾
self.head = DLinkedNode()
self.tail = DLinkedNode()
# 初始化两者的关系
self.head.next = self.tail
self.tail.prev = self.head
# 一个字典储存现在的数据
self.cache = dict()
def get(self, key: int) -> int:
if key not in self.cache:
return -1
# 取值
node = self.cache[key]
# 把读取过的节点移动到头部
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.cache:
# 生成新的节点
node = DLinkedNode(key, value)
# 赋值
self.cache[key] = node
# 把新加入的数据直接放到头部有
self.addToHead(node)
# 修改大小
self.size += 1
if self.size > self.capacity:
removed = self.removeTail();
self.cache.pop(removed.key)
self.size -= 1
else:
# 取值
node = self.cache[key]
# 赋值
node.value = value
# 把读取过的节点移动到头部
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def moveToHead(self, node):
# 先把节点摘掉
self.removeNode(node)
self.addToHead(node)
def removeTail(self) -> DLinkedNode:
node = self.tail.prev
self.removeNode(node)
return node
# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)