Data Structure

数据结构

机考前的临时抱佛脚

栈的修改与访问是按照后进先出的原则进行的,因此栈通常被称为是后进先出(last in first out)表,简称 LIFO 表。

STL

C++ 中的 STL 提供了一个容器 std::stack,使用前需要引入 stack 头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// clang-format off
template<
class T,
class Container = std::deque<T>
> class stack;
// 新建两个栈 st1 和 st2
std::stack<int> st1, st2;
// 为 st1 装入 1
st1.push(1);
// 将 st1 赋值给 st2
st2 = st1;
// 输出 st2 的栈顶元素
cout << st2.top() << endl;
// 输出: 1

队列

队列(queue)是一种具有「先进入队列的元素一定先出队列」性质的表。由于该性质,队列通常也被称为先进先出(first in first out)表,简称 FIFO 表。

STL

C++ 在 STL 中提供了一个容器 std::queue,使用前需要先引入 queue 头文件。

1
2
3
4
5
6
7
8
std::queue<int> q1, q2;
// 向 q1 的队尾插入 1
q1.push(1);
// 将 q1 赋值给 q2
q2 = q1;
// 输出 q2 的队首元素
std::cout << q2.front() << std::endl;
// 输出: 1

双端队列

双端队列是指一个可以在队首/队尾插入或删除元素的队列。相当于是栈与队列功能的结合。

STL

C++ 在 STL 中也提供了一个容器 std::deque,使用前需要先引入 deque 头文件

哈希表

哈希表又称散列表,一种以「key-value」形式存储数据的数据结构。所谓以「key-value」形式存储数据,是指任意的键值 key 都唯一对应到内存中的某个位置。只需要输入查找的键值,就可以快速地找到其对应的 value。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
struct hash_map {  // 哈希表模板

struct data {
long long u;
int v, nex;
}; // 前向星结构

data e[SZ << 1]; // SZ 是 const int 表示大小
int h[SZ], cnt;

int hash(long long u) { return (u % SZ + SZ) % SZ; }

// 这里使用 (u % SZ + SZ) % SZ 而非 u % SZ 的原因是
// C++ 中的 % 运算无法将负数转为正数

int& operator[](long long u) {
int hu = hash(u); // 获取头指针
for (int i = h[hu]; i; i = e[i].nex)
if (e[i].u == u) return e[i].v;
return e[++cnt] = (data){u, -1, h[hu]}, h[hu] = cnt, e[cnt].v;
}

hash_map() {
cnt = 0;
memset(h, 0, sizeof(h));
}
};

并查集

并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素。

并查集支持两种操作:

  • 合并(Union):合并两个元素所属集合(合并对应的树)
  • 查询(Find):查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class UFS {
public:
//初始化并查集,iota实现从0开始的递增序列vector
UFS(gg n) : ufs(n + 5) { iota(begin(ufs), end(ufs), 0); } //查找结点所在树的根结点并进行路径压缩
gg findRoot(gg x) { return ufs[x] == x ? x : ufs[x] = findRoot(ufs[x]); } //合并两个结点所在集合,如果已在同一集合,返回false
bool unionSets(gg a, gg b) {
a = findRoot(a), b = findRoot(b);
if (a == b) {
return false;
}
ufs[a] = b;
return true;
}
int find(int x, int* fa)
{
return (x==fa[x])?x:(fa[x]=find(fa[x], fa));
}
private:
vector<gg> ufs;
};

堆性质:父亲的权值不小于儿子的权值(大根堆)。同样的,我们可以定义小根堆。本文以大根堆为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void up(int x) {
while (x > 1 && h[x] > h[x / 2]) {
swap(h[x], h[x / 2]);
x /= 2;
}
}

void down(int x) {
while (x * 2 <= n) {
t = x * 2;
if (t + 1 <= n && h[t + 1] > h[t]) t++;
if (h[t] <= h[x]) break;
std::swap(h[x], h[t]);
x = t;
}
}

线段树

线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。

线段树可以在 $O(logn)$ 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>

int n, a[100005], d[270000], b[270000];

void build(int l, int r, int p) {
if (l == r) {
d[p] = a[l];
return;
}
int m = l + ((r - l) >> 1);
build(l, m, p << 1), build(m + 1, r, (p << 1) | 1);
d[p] = d[p << 1] + d[(p << 1) | 1];
}

void update(int l, int r, int c, int s, int t,
int p) {
if (l <= s && t <= r) {
d[p] = (t - s + 1) * c, b[p] = c;
return;
}
int m = s + ((t - s) >> 1);
if (b[p]) {
d[p << 1] = b[p] * (m - s + 1), d[(p << 1) | 1] = b[p] * (t - m);
b[p << 1] = b[(p << 1) | 1] = b[p];
b[p] = 0;
}
if (l <= m) update(l, r, c, s, m, p << 1);
if (r > m) update(l, r, c, m + 1, t, (p << 1) | 1);
d[p] = d[p << 1] + d[(p << 1) | 1];
}

int getsum(int l, int r, int s, int t, int p) { // 取得答案,和前面一样
if (l <= s && t <= r) return d[p];
int m = s + ((t - s) >> 1);
if (b[p]) {
d[p << 1] = b[p] * (m - s + 1), d[(p << 1) | 1] = b[p] * (t - m);
b[p << 1] = b[(p << 1) | 1] = b[p];
b[p] = 0;
}
int sum = 0;
if (l <= m) sum = getsum(l, r, s, m, p << 1);
if (r > m) sum += getsum(l, r, m + 1, t, (p << 1) | 1);
return sum;
}

int main() {
std::ios::sync_with_stdio(0);
std::cin >> n;
for (int i = 1; i <= n; i++) std::cin >> a[i];
build(1, n, 1);
int q, i1, i2, i3, i4;
std::cin >> q;
while (q--) {
std::cin >> i1 >> i2 >> i3;
if (i1 == 0)
std::cout << getsum(i2, i3, 1, n, 1) << std::endl;
else
std::cin >> i4, update(i2, i3, i4, 1, n, 1);
}
return 0;
}

下周把数据结构这部分写完,还剩二叉树内容,只打算补充 Splay 和 Treap


Data Structure
https://shangzz2001.github.io/2024/08/13/Data-Structure/
作者
Odysseus
发布于
2024年8月13日
许可协议