书接上回:
leetcode hot100 链表(一)-CSDN博客
8.删除链表的倒数第N个结点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* curr=head;
int len=0;
while(curr){
curr=curr->next;
len++;
}
int pos=len-n;
if(pos==0){
ListNode* newHead=head->next;
return newHead;
}
curr=head;
while(--pos) curr=curr->next; //目标是把curr移动到要删除结点的前面
curr->next=curr->next->next;
return head;
}
};
9.两两交换链表中的结点
参考leetcode灵神题解:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummy=new ListNode(0,head); //dummy->val=0&&dummy->next=head;
ListNode* node0=dummy;
ListNode* node1=head;
while(node1&&node1->next){
ListNode* node2=node1->next;
ListNode* node3=node2->next;
node0->next=node2;
node2->next=node1;
node1->next=node3;
node0=node1;
node1=node3;
}
return dummy->next;
}
};
10.k个一组反转链表
和上题使用了相同的命名体系。
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* dummy=new ListNode(0,head);
ListNode* node0=dummy;
ListNode* node1=node0->next;
while(node1&&node1->next){
ListNode* node2=node1->next;
ListNode* cnt=node2;
for(int i=1;i<k;i++){
if(cnt==nullptr) return dummy->next;
cnt=cnt->next;
}
for(int i=1;i<k;i++){
node1->next=node2->next;
node2->next=node0->next; //node0->next始终指向当前链表的头部
node0->next=node2;
node2=node1->next;
}
node0=node1;
node1=node0->next;
}
return dummy->next;
}
};
11.随机链表的复制
哈希映射法建立新链表结点与原节点的映射关系。
class Solution {
public:
unordered_map<Node*,Node*> map; //存储原链表节点到新链表节点的映射
Node* copyRandomList(Node* head) {
if(!head) return nullptr;
//检查当前节点是否已经在哈希表中(即是否已经被复制过)
//如果节点未被复制过,则创建一个新节点,值与原节点相同,并将原节点和新节点的映射存入哈希表
if(!map.count(head)){
Node* newHead=new Node(head->val);
map[head]=newHead;
//递归复制next指针指向的链表部分和random指针指向的节点
newHead->next=copyRandomList(head->next);
newHead->random=copyRandomList(head->random);
}
return map[head]; //返回头结点在新链表中的映射
}
};
12.排序链表
类似归并排序方法,先二分找到中点(通过快慢指针法),再对左右两边分别排序,最后合并两部分。
class Solution {
// 链表的中间结点(快慢指针)
ListNode* middleNode(ListNode* head) {
ListNode* pre=head;
ListNode* slow=head;
ListNode* fast=head;
while (fast&&fast->next) {
pre=slow; // 记录 slow 的前一个节点
slow=slow->next;
fast=fast->next->next;
}
pre->next=nullptr; // 断开 slow 的前一个节点和 slow 的连接
return slow; // 链表后半部分
}
// 合并两个有序链表(双指针),归并思想
ListNode* merge(ListNode* list1, ListNode* list2) {
ListNode dummy; // 用哨兵节点简化代码逻辑
ListNode* cur=&dummy; // cur 指向新链表的末尾
while(list1&&list2){
if(list1->val<=list2->val){
cur->next=list1; // 把 list1 加到新链表中
list1=list1->next;
}
else{
cur->next=list2;
list2=list2->next;
}
cur=cur->next;
}
cur->next=list1?list1:list2; // 拼接剩余链表
return dummy.next;
}
public:
ListNode* sortList(ListNode* head) {
if (!head||!head->next) return head;
// 找到中间节点 head2,并断开 head2 与其前一个节点的连接,然后分治、合并
// 比如 head=[4,2,1,3],那么 middleNode 调用结束后 head=[4,2] head2=[1,3]
ListNode* head2 = middleNode(head);
head=sortList(head);
head2=sortList(head2);
return merge(head, head2);
}
};
13.合并k个升序链表
利用小根堆实现,小根堆里维护每个非空链表未被处理的第一个结点。
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
struct Cmp{
bool operator()(ListNode* a, ListNode* b){
return a->val>b->val;
}
};
priority_queue<ListNode*,vector<ListNode*>,Cmp> min_heap; //优先队列模拟小根堆
for(ListNode* node:lists){
if (node) min_heap.push(node); //把所有非空链表的头结点入堆
}
ListNode dummy(0);
ListNode* tail=&dummy; //tail负责维护合并后的新链表
while(!min_heap.empty()){
ListNode* min_node=min_heap.top(); //剩余结点中的最小结点
min_heap.pop();
if(min_node->next) min_heap.push(min_node->next);
tail->next=min_node; //把min_node添加到新链表末尾
tail=tail->next; //准备合并下一个结点
}
return dummy.next;
}
};
14.LRU缓存
struct DLinkedNode {
int key, value;
DLinkedNode* prev;
DLinkedNode* next;
DLinkedNode():key(0),value(0),prev(nullptr),next(nullptr){}
DLinkedNode(int _key,int _value):key(_key),value(_value),prev(nullptr),next(nullptr){}
};
class LRUCache {
private:
unordered_map<int, DLinkedNode*> cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
public:
LRUCache(int _capacity): capacity(_capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head->next=tail;
tail->prev=head;
}
int get(int key){
if(!cache.count(key)) return -1;
// 如果 key 存在,先通过哈希表定位,再移到头部
DLinkedNode* node=cache[key];
moveToHead(node);
return node->value;
}
void put(int key, int value) {
if (!cache.count(key)) {
// 如果 key 不存在,创建一个新的节点
DLinkedNode* node = new DLinkedNode(key, value);
// 添加进哈希表
cache[key] = node;
// 添加至双向链表的头部
addToHead(node);
size++;
if(size>capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode* removed = removeTail();
// 删除哈希表中对应的项
cache.erase(removed->key);
// 防止内存泄漏
delete removed;
size--;
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
DLinkedNode* node = cache[key];
node->value = value;
moveToHead(node);
}
}
void addToHead(DLinkedNode* node) {
node->prev=head;
node->next=head->next;
head->next->prev=node;
head->next=node;
}
void removeNode(DLinkedNode* node) {
node->prev->next=node->next;
node->next->prev=node->prev;
}
void moveToHead(DLinkedNode* node){
removeNode(node);
addToHead(node);
}
DLinkedNode* removeTail(){
DLinkedNode* node=tail->prev;
removeNode(node);
return node;
}
};