目录
栈
最小栈
- 双栈
字符匹配
- 使用到了unordered_map
字符串解码
- 用vector操作方便得多
单调栈
- 往后数第一个比自己大的数
- 两个单调栈:分别往两边数,找到比自己小的数,确定区间
队列
链表
头插法
删除元素k
倒转链表
#include <iostream>
#define maxn 110
using namespace std;
struct node {
int data;
int next;
}link[maxn];
int main() {
int n, first, id;
scanf("%d %d", &n, &first);
for (int i = 0; i < n; i++) {
scanf("%d", &id);
scanf("%d %d", &link[id].data, &link[id].next);
}
int p = link[first].next, q = first;
while (p != -1) {
link[q].next = link[p].next;
link[p].next = first;
first = p;
p = link[q].next;
}
p = first;
while (p != -1) {
printf("%d %d %d\n", p, link[p].data, link[p].next);
p = link[p].next;
}
}
两个指针p,q,p指当前待处理的结点,q指当前已经反过来的序列的尾巴,采用头插法。
1234->2134->3214->4321
- 设置temp_head,temp_tail指针维护每一组首尾,注意不要让组间断开,组内就用倒转链表的算法√
环形链表
- 快慢指针
- unordered_set
- unordered_set:可以很简单返回环的起始结点,但空间复杂度为O(n)
- 快慢指针:一点点数学推理,两次相遇
链表排序🥲
合并两个有序链表
- 迭代写法
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode *prehead = new ListNode(-1);
// temp指前一个已经确定顺序结点的地址
ListNode *temp = prehead, *temp1 = list1, *temp2 = list2;
while (temp1 != NULL && temp2 != NULL) {
if (temp1 -> val < temp2 -> val) {
temp -> next = temp1;
temp1 = temp1 -> next;
} else {
temp -> next = temp2;
temp2 = temp2 -> next;
}
temp = temp -> next;
}
// 处理还没处理完的
if (temp1 != NULL) temp -> next = temp1;
if (temp2 != NULL) temp -> next = temp2;
return prehead -> next;
}
};
- 递归写法
排序链表🥲
- 自顶向下归并排序
递归写法,空间:O(logn)
sortList进行分块(快慢指针)
merge进行合并(合并两个有序链表)
/**
* 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* sortList(ListNode* head) {
return sortList(head, NULL);
}
ListNode* sortList(ListNode* head, ListNode* tail) {
// head指头,tail指最后一个结点之后
if (head == NULL) return head; // 空结点
if (head -> next == tail) { // 一个结点,把结点解开,直接返回
head -> next = NULL;
return head;
}
ListNode *p = head, *q = head;
while (p != tail && p -> next != tail) {
p = p -> next -> next;
q = q -> next;
}
head = sortList(head, q);
q = sortList(q, tail);
head = merge(head, q);
return head;
}
ListNode* merge(ListNode* head1, ListNode* head2) {
ListNode *prehead = new ListNode(-1);
// temp1, temp2指向当前比较结点,temp指向“前一个被确定了的结点”
ListNode *temp = prehead, *temp1 = head1, *temp2 = head2;
while (temp1 != NULL && temp2 != NULL) {
if (temp1 -> val < temp2 -> val) {
temp -> next = temp1;
temp1 = temp1 -> next;
} else {
temp -> next = temp2;
temp2 = temp2 -> next;
}
temp = temp -> next;
}
if (temp1 != NULL) temp -> next = temp1;
if (temp2 != NULL) temp -> next = temp2;
return prehead -> next;
}
};
- 自底向上
空间:O(1)
合并K个升序链表🥲
- 归并
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return mergeKLists(lists, 0, lists.size());
}
ListNode* mergeKLists(vector<ListNode*>& lists, int left, int right) {
if (left == right) return NULL;
if (left == right - 1) return lists[left];
int mid = (left + right) / 2;
ListNode *leftLists, *rightLists;
leftLists = mergeKLists(lists, left, mid);
rightLists = mergeKLists(lists, mid, right);
return merge(leftLists, rightLists);
}
ListNode* merge(ListNode* list1, ListNode* list2) {
ListNode *prehead = new ListNode(-1);
ListNode *temp = prehead;
while (list1 != NULL && list2 != NULL) {
if (list1 -> val < list2 -> val) {
temp -> next = list1;
list1 = list1 -> next;
} else {
temp -> next = list2;
list2 = list2 -> next;
}
temp = temp -> next;
}
if (list1 != NULL) temp -> next = list1;
if (list2 != NULL) temp -> next = list2;
return prehead -> next;
}
};
两个链表相加
删除链表倒数第N个结点
- 在对链表进行操作时,一种常用的技巧是添加一个哑节点(dummy node),它的next指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。
- 方法一:快慢指针
- 方法二:栈
两两交换链表结点
- 迭代:三个指针,创建头节点
- 递归
复制链表
- unordered_map+回溯/迭代
- 节点拆分
LRU缓存
- 哈希+双向链表
- 双向链表最好设置空的头节点和尾节点,统一操作