C++ STL list
Created Feb 12, 2025 - Last updated: Feb 12, 2025
Evergreen 🌳
algorithm
链表
STL
题目 luogu P1160 队列安排
一个学校里老师要将班上$N$个同学排成一列,同学被编号为$1∼N$,他采取如下的方法:
-
1.先将$1$号同学安排进队列,这时队列中只有他一个人;
-
2.$2∼N$号同学依次入列,编号为$i$的同学入列方式为:老师指定编号为$i$的同学站在编号为$1∼(i−1)$中某位同学(即之前已经入列的同学)的左边或右边;
-
3.从队列中去掉$M$个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
思路1:
通过链表模拟
#include <iostream>
#include <set>
using namespace std;
struct Node {
int ID;
Node *front, *next;
};//定义链表节点
Node* addNode(Node* head, int k, int p, int ID) //插入节点函数
{
Node* temp = head;
while (temp->ID != k)
temp = temp->next;
Node* newNode = new Node { ID, nullptr, nullptr };
if (p == 0) { // 插入到左边
newNode->next = temp;
newNode->front = temp->front;
if (temp->front)
temp->front->next = newNode;
temp->front = newNode;
if (temp == head)
head = newNode;
} else { // 插入到右边
newNode->front = temp;
newNode->next = temp->next;
if (temp->next)
temp->next->front = newNode;
temp->next = newNode;
}
return head;
}
Node* cutNode(Node* head, int ID)
{
Node* temp = head;
while (temp && temp->ID != ID)
temp = temp->next;
if (!temp)
return head; // 没找到直接返回
if (temp->front)
temp->front->next = temp->next;
if (temp->next)
temp->next->front = temp->front;
if (temp == head)
head = temp->next; // 处理删除头结点情况
delete temp;
return head;
}
int main()
{
int n;
cin >> n;
Node* head = new Node { 1, nullptr, nullptr };
for (int i = 0; i < n - 1; i++) {
int k, p;
cin >> k >> p;
head = addNode(head, k, p, i + 2);
}
int m;
cin >> m;
set<int> num;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
if (num.count(x))
continue;
num.insert(x);
head = cutNode(head, x);
}
Node* temp = head;
while (temp) {
cout << temp->ID << " ";
temp = temp->next;
}
return 0;
}
缺点: 寻找时遍历链表时间复杂度$O(n)$; 综合分析: 插入操作:$O(N²)$ 删除操作:$O(MN)$ 遍历输出:$O(N)$ 总复杂度为:
$$O(N^2+MN+N)$$
思路2:
优化方向
1.用unordered_map<int, Node*>
记录节点地址,查找$O(1)$,降低$O(N²)$到$O(N)$
2.使用 list 代替手写链表 C++ STL list 结构本身就是双向链表,能减少手写链表的开销。
#include <iostream>
#include <unordered_map>
#include <list>
#include <set>
using namespace std;
int main() {
int n;
cin >> n;
list<int> studentList;
unordered_map<int, list<int>::iterator> posMap; // 存储每个编号的迭代器位置
studentList.push_back(1);
posMap[1] = studentList.begin();
for (int i = 0; i < n - 1; i++) {
int k, p, ID = i + 2;
cin >> k >> p;
auto it = posMap[k]; // O(1) 找到 k 位置
if (p == 0) {
posMap[ID] = studentList.insert(it, ID); // O(1) 插入左边,insert返回值是迭代器
} else {
posMap[ID] = studentList.insert(next(it), ID); // O(1) 插入右边,insert返回值是迭代器
}
}
int m;
cin >> m;
set<int> removeSet;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
if (removeSet.count(x)) continue;
removeSet.insert(x);
studentList.erase(posMap[x]); // O(1) 删除
}
for (int id : studentList) {
cout << id << " ";
}
return 0;
}
优化后,总复杂度为: $$O(N)+O(M)+O(N)=O(N)$$