競程簡介 & STL
內建資料結構與函式
Ian Wen
關於講師
- OJ handle: ianwen, SheepCantCode, IanWen001
- APCS 4+4
- 我不會數學
- 建資學術
特別感謝
- pringdesu 高翊恩
- 我簡報部分偷他的
啥是競程
競賽程式
競賽程式
為啥要打競賽程式
- 參與比賽
拿獎品 或有吃的 - 增強自己的數學/邏輯能力
- 先修高中/大學數學
- 豐富自己的學習歷程
- 對升學有一定幫助 (APCS組之類的)
STL
Standard Template Library
- 標準樣板函式庫 (不是標準函式庫)
- 由 Algorithm Container Function Iterator 構成
- 使用前先 #include 要用的東西 (啊我都用 bits/stdc++ 啦)
- cpp reference
Containers & Iterator
資料結構
- 各種不同的 "容器"
- 不同容器有不同的 "性質"
- 運用不同 "性質" 可以快速完成特定 "操作"
Pair
pair<int, int> p;
pair<int, int>(int x, int y) | 初始化p | O(1) |
int first; | pair的第一項 | O(1) |
int second; | pair的第二項 | O(1) |
pair<type_A, type_B> make_pair(type_A a, type_B b);
Pair 宣告
#include <bits/stdc++.h>
using namespace std;
int main(){
pair<int,int> a; // 最常用應該就長這樣,包含了兩個 int 的 pair
pair<int,int> b;
b = {1,2}; // 可以用大括弧包起來
b = make_pair(1,2); // 也可以用 make_pair() 包起來
pair<char,double> c('A',0.1); // 或是宣告時直接在名稱後面加上 (),裡面放初始值
pair<int,pair<int,int>> d;
d = {1,{2,3}}; // 沒有人說你不能在 pair 裡面放 pair
pair<string,vector<pair<int,int>>> v; // 或是放 vector
vector<pair<int,int>> g(10,{2,3}); // 當然你也可以放進其他 container 裡面
}
Pair 存取
#include <bits/stdc++.h>
using namespace std;
int main(){
pair<int,int> a(1,2); // 隨便宣告個 pair
cout << a.first << ' ' << a.second; // 1 2
pair<pair<int,int>,pair<int,int>> b({1,2},{3,4}); // pair 裡面放 pair
cout << b.first.first << ' ' << b.second.second; // 1 4
}
陣列型
- queue
- stack
- vector
- array
- deque
- bitset
Queue
queue<int> q;
unsigned size() | 回傳q目前裝了幾個人 | O(1) |
void push(int x) | 將x放入q的最末端 | O(1) |
void pop() | 將q的最前端元素移除 | O(1) |
int &front() | 存取最前端元素 | O(1) |
Queue 宣告 & 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
queue<int> q;
q.push(10);
q.push(20);
cout << q.front() << "\n"; //10
q.pop();
cout << q.front() << "\n"; //20
return 0;
}
Stack
stack<int> st;
unsigned size() | 回傳st目前裝了幾個元素 | O(1) |
void push(int x) | 將x放入st的最末端 | O(1) |
void pop() | 將st的最末端元素移除 | O(1) |
int &top() | 存取最後一個被放入的元素 | O(1) |
Stack 宣告 & 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
stack<int> s;
s.push(10);
s.push(20);
cout << s.top() << "\n"; //20
s.pop();
cout << s.top() << "\n"; //10
return 0;
}
Vector
vector<int> v;
vector<int>(int n, int x) | 開一個大小為n,每項初始值為x的陣列 | O(n) |
unsigned size() | 回傳v目前裝了幾個人 | O(1) |
void push_back(int x) | 將x放入v的最末端 | O(1) |
void pop_back() | 將v的最末端元素移除 | O(n) |
void clear() | 清除v的所有元素 | O(n) |
int &operator[](int id) | 存取從頭數來的第id項 | O(1) |
Vector 宣告
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> a; // 一維 vector
vector<int> b(10); // 大小為十的 vector
vector<int> c(n); // 大小為 n 的 vector
vector<int> d(n,10); // 大小為 n 且每個元素預設是 10 的 vector
vector<int> e={1,2,3}; // 元素為 1 2 3 的 vector
vector<int> f=d; // 直接 = 某個結構一樣的 vector
vector<int> g(d.begin()+2,d.end()); // 從 d 的第2項 (0-based) 取到最後一項
vector<vector<int>> two(n,vector<int>(n,0)); // 二維 vector,預設全部都是 0
}
Vector 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> v(n);
v[0]=1;
v.clear();
v.resize(10);
v.reserve(100);
v.push_back(48763);
v.pop_back();
int vsize = v.size();
return 0;
}
Array
array<int, 10> arr;
看起來比較酷的陣列?
Deque
deque<int> dq;
unsigned size() | 回傳dq目前裝了幾個人 | O(1) |
void push_front(int x) | 將x放入dq的最前端 | O(1) |
void pop_front() | 將dq的最前端元素移除 | O(1) |
void push_back(int x) | 將x放入dq的最末端 | O(1) |
void pop_back() | 將dq的最末端元素移除 | O(1) |
int &front() | 存取dq最前端元素 | O(1) |
int &back() | 存取dq最末端元素 | O(1) |
void clear() | 清除所有元素 | O(n) |
int &operator[](int id) | 存取從頭數來的第id項 | O(1) |
Deque 宣告 & 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
deque<int> dq;
dq.push_back(10);
dq.push_back(20);
cout << dq.back() << "\n"; //20
dq.pop_back();
cout << dq.front() << "\n"; //10
dq.push_front(15);
dq.push_back(30);
cout << dq.front() << "\n"; //15
return 0;
}
Bitset
bitset<1000> b;
void reset() | 將b的每個元素變成false | O(n) |
bool &operator[](int id) | 存取b的第id個bit | O(1) |
高級型
- priority_queue
- set
- multiset
- map
Priority Queue
priority_queue<int> pq;
unsigned size() | 回傳pq目前裝了幾個人 | O(log n) |
void push(int x) | 將x放入pq | O(log n) |
void pop() | 將pq的最頂端元素移除 | O(log n) |
int &top() | 存取目前最大的元素 | O(1) |
void clear() | 清除pq的所有元素 | O(n) |
priority_queue 宣告 & 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int> pq;
pq.push(3);
pq.push(6);
cout << pq.top() << "\n"; //6
pq.pop();
cout << pq.top() << "\n"; //3
return 0;
}
Set
set<int> S;
unsigned size() | 回傳目前裝了幾個人 | O(1) |
pair<iterator,bool> insert(int x) | 將x放入S中 | O(log n) |
iterator find(int x) | 回傳指向x的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除S的所有元素 | O(n) |
Set 宣告 & 使用
#include <bits/stdc++.h>
using namespace std;
int main(){
set<int> s;
s.insert(3);
s.insert(4);
s.insert(5);
s.find(3);
for (auto i:s) {
cout << i << '\n';
}
s.erase(3);
for (auto i:s) {
cout << i << '\n';
}
}
Multiset
multiset<int> MS;
unsigned size() | 回傳目前裝了幾個人 | O(1) |
iterator insert(int x) | 將x放入MS中 | O(log n) |
iterator find(int x) | 回傳指向x的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除MS的所有元素 | O(n) |
MultiSet
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> a; // 一維 vector
vector<int> b(10); // 大小為十的 vector
vector<int> c(n); // 大小為 n 的 vector
vector<int> d(n,10); // 大小為 n 且每個元素預設是 10 的 vector
vector<int> e={1,2,3}; // 元素為 1 2 3 的 vector
vector<int> f=d; // 直接 = 某個結構一樣的 vector
vector<int> g(d.begin()+2,d.end()); // 從 d 的第2項 (0-based) 取到最後一項
vector<vector<int>> two(n,vector<int>(n,0)); // 二維 vector,預設全部都是 0
}
Map
map<int, int> M;
unsigned size() | 回傳目前裝了幾組人 | O(1) |
int &opreator[](int key) | 存取key對應到的值 | O(log n) |
iterator find(int key) | 回傳指向key的iterator | O(log n) |
void erase(iterator it) | 將it指向的元素移除 | O(log n) |
void clear() | 清除M的所有元素 | O(n) |
Map
#include <bits/stdc++.h>
using namespace std;
int main(){
map<int,int> m;
m[3]=1;
if(m.find(5)==m.end()){ // find key
m[5]=2;
}
m.insert({2,2});
m.erase(3);
int a=m.size();
bool b=m.empty();
return 0;
}
iterator是啥?
- 在STL中與容器內的元素互動的「指標」
- 有時候不能暴力用[]存取時就要用iterator
int32_t main() {
int a[10];
for (int *p = a; p < a + 10; p++) {
cin >> *p;
}
}
int32_t main() {
vector<int> v(10);
for (auto it = v.begin(); it != v.end(); it++) {
cin >> *it;
}
}
iterator比較
隨機存取iterator:
雙向存取iterator:
沒有iterator:
- vector
- array
- deque
- set
- multiset
- map
- queue
- stack
- priority_queue
(bitset支援隨機存取,但沒有iterator)
iterator常用語法
iterator data_str.begin() | 回傳該資料結構的「開頭」iterator |
iterator data_str.end() | 回傳該資料結構的「結尾」iterator |
iterator next(iterator it) | 回傳「下一個」iterator |
iterator prev(iterator it) | 回傳「上一個」iterator |
*it | 存取it指到的位址的那個人 |
it -> func(); it -> attr; | (*it).func(); (*it).attr; |
it + 3 | 回傳「下3個」iterator |
it - 3 | 回傳「上3個」iterator |
it_1 - it_0 | 回傳兩iterator間的距離 |
iterator常用操作
※vector排序
int32_t main() {
int n;
cin >> n;
int vector<int> v(n);
for (auto &i : v) cin >> i;
sort(v.begin(), v.end());
}
iterator常用操作
※set遍歷
int32_t main() {
int n, br;
set<int> S;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> br;
S.insert(br);
}
for (auto it = S.begin(); it != S.end(); it++) {
cout << *it << ' ';
}
cout << endl;
...
}
iterator常用操作
※set找人
int32_t main() {
int n, br;
set<int> S;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> br;
S.insert(br);
}
cin >> br;
auto it = S.find(br);
if (it == S.end()) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
...
}
iterator常用操作
※map遍歷
int32_t main() {
int n, key, val;
map<int, int> M;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> key >> val;
M[key] += val;
}
for (auto it = M.begin(); it != M.end(); it++) {
cout << it -> first >> ": " >> it -> second << endl;
}
}
iterator常用操作
※map找人
map<int, int> M;
inline int advenced_find(int key) {
auto it = M.find(key);
if (it == M.end()) return -1;
return it -> second;
}
Algorithms & Functions
sort() - 排序
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> v(n);
for (int i=0;i<n;i++) {
cin >> v[i];
}
sort(v.begin(),v.end());
for (int i=0;i<n;i++) {
cout << v[i] << ' ';
}
}
O(nlog(n))
#include <bits/stdc++.h>
using namespace std;
bool cmp(int a ,int b) {
return a < b; // 原本從小到大,變成從大到小
}
int main() {
int n;
cin >> n;
vector<int> v(n);
for (int i=0;i<n;i++) {
cin >> v[i];
}
sort(v.begin(), v.end(), cmp);
for (int i=0;i<n;i++) {
cout << v[i] << ' ';
}
}
sort() - 自訂排序
O(nlog(n))
unique() - 移除重複元素
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<int> v(n);
for (int i=0;i<n;i++) {
cin >> v[i];
}
unique(v.begin(),v.end());
for (int i=0;i<n;i++) {
cout << v[i] << ' ';
}
}
O(n)
min() - 找最小值
#include <bits/stdc++.h>
using namespace std;
int main(){
cout << min(2, 1); // 這樣只能放兩個
cout << min({1, 3, 2, 4}); // 加個大括弧就可以放很多個
}
max() - 找最大值
#include <bits/stdc++.h>
using namespace std;
int main(){
cout << max(2, 1); // 這樣只能放兩個
cout << max({1, 3, 2, 4}); // 加個大括弧就可以放很多個
}
__gcd() - 找最大公因數
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
cout << "gcd(6, 20) = " << __gcd(6, 20) << '\n';
}
部分編譯器不能用
但你可以自己寫
#include <iostream>
#include <algorithm>
using namespace std;
int gcd(int m, int n) {
int t = 1;
while(t != 0) {
t = m % n;
m = n;
n = t;
}
return m;
}
int main() {
cout << "gcd(6, 20) = " << gcd(6, 20) << '\n'; // problem solved
}
題
基礎 STL
By wen Ian
基礎 STL
- 513