競程簡介 & 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