题目链接:Insert or Erase
题目要求不断对一个序列进行增、删操作,容易想到学习数据结构时关于这个问题的讨论,显然使用链表是一个非常不错的选择。
作为modern cpp选手,我非常倾向于使用STL中的容器解决这个问题,于是顺路学习了下常用相关操作。
std::list
is a container that supports constant time insertion and removal of elements from anywhere in the container. Fast random access is not supported. It is usually implemented as a doubly-linked list.—— cppreference
上面这段话引用自cppreference,大体就是说std::list
是一个容器,支持从容器中的任意位置定时插入和移除元素。不支持快速随机存取。它通常以双链表的形式实现。
这里我们并不讨论其实现细节,读者感兴趣可自行查看源码。
.insert()
或.emplace()
:当你写下.emplace(x,y)
,其中x
是指向同一列表中某个元素的迭代器时,它的意思是在x
之前创建一个与y
相等的元素。请注意,迭代器x
是必须的。时间复杂度严格为 O ( 1 ) O(1) O(1)。.erase()
:当你擦除.erase(x)
,其中x
是指向同一列表中元素的迭代器时,字面意思是删除它。时间复杂度严格为 O ( 1 ) O(1) O(1)。.splice()
:它连接两个列表,当你使用.splice(iterator, list)时,这意味着list列表将插入当前调用函数的列表的iterator位置。(这个功能很强大,但在本问题中没有用到)。
在本题中,由于在增删时,我们总是需要对迭代器进行操作,因此我们就需要对序列中的每个整数所对应位置的迭代器进行存储。
特别注意,std::list与std::vector不同的一点是,除非它的迭代器被删除,否则是不会破坏掉的(不会像std::vector那样因为序列的改变使得序列中元素位置和迭代器发生改变),这就是为什么我们能够保存下来不断使用。
因此本题时间复杂度: O ( N l o g N ) O(NlogN) O(NlogN) 注: O ( l o g N ) O(logN) O(logN)为使用std::map的复杂度。
本题代码:
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int N;
std::cin >> N;
std::vector<int> A(N);
for (int i = 0; i < N; i++) {
std::cin >> A[i];
}
std::map<int, std::list<int>::iterator> f;
int Q;
std::cin >> Q;
std::list<int> ans;
for (int i = 0; i < N; i++) {
ans.emplace_back(A[i]);
f[A[i]] = std::prev(ans.end());
}
for (int i = 0; i < Q; i++) {
int o;
std::cin >> o;
if (o == 1) {
int x, y;
std::cin >> x >> y;
auto it = std::next(f[x]);
ans.emplace(it, y);
f[y] = std::prev(it);
} else {
int x;
std::cin >> x;
ans.erase(f[x]);
}
}
for (auto x : ans) {
std::cout << x << " ";
}
return 0;
}