暑假資讀 [3]

講師

  • 227 張庭瑋
  • 什麼都不會
  • handle: alvingogo
  • DC: Alvin Chang#6995

資料結構

什麼是資料結構

資料結構+演算法=程式                               
                                            --Anonymous

Standard Template Library(STL)

  • C++ 標準模板庫
  • 裡面有很多 C++ 幫你寫好的東西
  • 要用之前要 include
  • 可以去 https://cplusplus.com/ 找

關於那些 include

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <ctime>
#include <cstring>
#include <set>
#include <map>
#include <cstdlib>
#include <cmath>
#include <deque>
#include <utility>
#include <numeric>
#include <climits>
using namespace std;

int main(){
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int main(){
    return 0;
}

你要哪個

iterator

iterator

  • 中文稱迭代器
  • 有點類似 pointer,儲存記憶體位置
  • 作用在 STL 的各種 container 上
  • 可以利用 ++ 跟 -- 運算子,作用就是找到下一個位置的元素
  • 常見的 iterator 有 begin() 跟 end()

vector

雖然第二堂課講過了但是可以再講一些觀念

vector

  • 又稱 dynamic array
  • 大小不是固定不變,可以任意更改
  • 比一般的陣列好用
  • 傳進函式裡面的方法也比一般的陣列好用 事實是我根本不會把陣列傳進函式

宣告

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    vector<int> a;
    vector<int> b(10);
    vector<int> c(n);
    vector<int> d(n,10);
    vector<int> e={1,2,3};
    vector<int> f=d;
    vector<int> g(d.begin()+2,d.end());
    vector<vector<int> > two;
    return 0;
}

一些函式

#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    vector<int> v(n);
    v[0]=1;
    fill(v.begin,v.end(),0);
    iota(v.begin(),v.end(),0); //用來讓 v 的元素被設成 0,1,2,3...
    v.resize(10);
    v.reserve(100);
    v.resize(15,3);
    v.clear();
    v.push_back(48763);
    v.emplace_back(8763);
    v.pop_back();
    unsigned int a=v.size();
    bool b=v.empty();
    int c=count(v.begin(),v.end(),1);
    return 0;
}

push_back

  • vector 有兩個重要的數字,分別叫做 capacity 跟 size
  • capacity 永遠 >= size
  • push_back 的時候會檢查目前的 size 是不是 < capacity, 如果是就直接在後面放入一個元素,否則跟系統要兩倍大的 capacity,把目前的東西複製過去,再在後面放一個元素

reserve & resize

  • reserve(n) 是針對 capacity 的,如果目前的 capacity < n,跟系統要大小為 n 的 capacity,把目前的東西複製過去
  • resize(n) 是針對 size 的,如果目前的 size < n,就把 size 變成 n,然後後面補 0 (或你想要的東西),如果 n > capacity 則會先照上面那一套流程做

注意事項

  • push_back 有點慢
  • 只有 size 裡面才是可以用 [] 存取的資料,capacity 只是預留空間而已,裡面的東西是不確定的

遍歷

int main(){
    int n;
    cin >> n;
    vector<int> v(n);
    for(int i=0;i<n;i++){
        cin >> v[i];
    }
    return 0;
}
int main(){
    int n;
    cin >> n;
    vector<int> v(n);
    for(int &h:v){
        cin >> h;
    }
    return 0;
}

pair

pair

  • 是一種結構
  • 只能放兩個東西
  • 那兩個東西可以是不一樣的型態
  • 用 first 跟 second 存取

宣告

#include <bits/stdc++.h>
#define fs first
#define sc second
using namespace std;

int main(){
    pair<int,int> a;
    pair<int,double> b={1,-1.23};
    pair<char,double> c('Y',0.48763);
    pair<int,pair<int,int> > d=make_pair(1,make_pair(2,3));
    pair<string,vector<pair<int,int> > > v;
    vector<pair<int,int> > g(10,{2,3});
    return 0;
}

取值

#include <bits/stdc++.h>
using namespace std;

int main(){
    pair<int,int> a(1,2);
    int b=a.first,c=a.second;
    return 0;
}

stack, queue, deque 

stack

  • 又稱堆疊
  • 常用的比喻是洗盤子跟拿盤子
  • 是一種後進先出的資料結構

queue

  • 又稱佇列
  • 就跟排隊一樣是先進先出

deque

  • 又稱雙向佇列
  • 關於他的念法我們專門再做一個表單讓大家吵架
  • 有點像 stack 跟 queue 的結合,哪邊先進去或出來都可以

很抽象嗎 來看 code

#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;
}
#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;
}
#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, queue, deque
也有 size 跟 empty 這些函數

其實這三個東西都是可以用 vector 寫出來的
有興趣可以寫寫看

題單

priority_queue

priority_queue

  • 中文叫做優先佇列
  • 可以支援三種操作:加入一個數、刪除最大(小)值、取得最大(小)值,都是 O(logn)
  • 預設是只能取最大值,除非你是 YTP
  • 不說了 直接看 code

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;
}

size 跟 empty 也可以用

set

set

  • 跟數學上的集合很相似
  • 可支援的操作是加入元素、刪除元素、查詢一個元素是否在集合裡面 也都是 O(logn)
  • 會自動幫我們排序
  • 常數有點大 使用要小心

code

#include <bits/stdc++.h>
using namespace std;

int main(){
    set<int> s;
    s.insert(3);
    s.insert(6);
    s.insert(8);
    s.insert(4);
    s.erase(6);
    s.find(3);
    s.erase(s.find(3));
    for(auto h:s){

    }
    for(set<int>::iterator it=s.begin();it!=s.end();it++){
        
    }
    bool a=s.empty();
    int z=s.size();

    return 0;
}

multiset

  • 跟 set 類似
  • 元素可重複,依然會自動排序
  • 刪除方法要特別注意
  • 具體而言 erase(value) 會刪除所有值為 value 的元素
  • 有時候我們只想刪一個就得用 erase(find(value))

lower_bound

  • 可以找到 >= 他的最小元素
  • 在 vector set map 等等的都可以用

    vector<int> v;
    set<int> s;
    cout << v[lower_bound(v.begin(),v.end(),3)-v.begin()] << "\n";
    cout << *s.lower_bound(4) << "\n";

map & unordered_map

map

  • 中文似乎叫做映射(?
  • 由兩個東西組成:key 跟 value
  • 可以利用陣列取值的方式
  • 一樣會自動排序

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);
    for(auto h:m){//key

    }
    for(map<int,int>::iterator it=m.begin();it!=m.end();it++){
        
    }
    int a=m.size();
    bool b=m.empty();
    return 0;
}

unordered_map

  • 是一種 hash table
  • 會依據輸入的數值做 hash
  • 不會自動幫你排序
  • 使用小心,hash 很容易碰撞
  • 有時候寧可犧牲一個 log 也不願用 unordered_map 
  • 我很久沒用了(?
  • code 就不放了 跟 map 的幾乎一模一樣

題單時間

CSES 1619 CSES 1091 CSES 2216
CSES 2217 CSES 1640 CSES 1645
其實這些都是從 CSES sorting and searching 抓來的,直接把那個分類刷完不虧

Made with Slides.com