時間複雜度 & 基礎資料結構
Lecturer:Lemon
Slides:yeedrag
複雜度? 能吃嗎
複雜度是我們估計一個程式的執行花費的工具
也就是代表程式的運算次數 or 記憶體用量
複雜度越高,當然電腦的負擔就越大
而你們就會吃 TLE、MLE
所以在我們開始寫程式碼前
我們通常會先估計程式的複雜度
避免我們陷入明明刻完程式碼
測資卻全部都錯光光的窘境
O - notation
如果你有看過演算法的題目、講義
你應該會常常看到\(O(1) , O(n), O(n!) \)之類的
前面的\(O\)就代表O - notation
O - notation
在O - notation下
我們只取跟\(n\)有關且增長最快的項
嚴謹的證明當然不簡單
但可以說這樣的表示法是因為
在\(n\)很大的時候 i.e. \(n = 10^6\)
其他的數字對複雜度的影響其實就不大ㄌ
e.g.
\(n^2 + n + 2 = O(n^2) \)
\(2^n + n^2 = O(2^n)\)
\(100000 = O(1)\)
複雜度估算
把複雜度以O - notation表示
將\(n\)的值帶進去,就大約是我們的複雜度ㄌ
時間複雜度 一秒內複雜度大約可以到\(10^8 \sim 10^9\)
空間複雜度 一般的題目複雜度大約可以到\(10^7\)左右
懂ㄇ(?
#include <iostream>
using namespace std;
int main() {
//這是一個把數字倒著輸出的程式
int n;
cin >> n;
int seq[n];
for(int i = 0; i < n; ++i) {
cin >> seq[i];
}
for(int i = n-1; i >= 0; --i) {
cout << seq[i] << ' ';
}
cout << '\n';
return 0;
}
What are STLs?
反正就是容器函式迭代器的酷東西.w.
今天會教的STL們
- Queue
- Stack
- Vector
- Pair/Tuple
我有Array,幹嘛學他們?
A:你開心也可以,但很多題目使用STL會使其方便非常多,但不同資料結構的複雜度可能有差異喔!
迭代器(?
簡單來說,假設今天我們有一個容器C
但我們的 C 長的並不像陣列
原本我們可以透過for迴圈對一個Array做遍歷
但 C 長得比較畸形
所以STL中便提供了迭代器來解決這樣的問題
雖然迭代器稍微有點毒瘤(?
但如果打校內賽編譯器很爛就不得不用ㄌ
迭代器種類
- 隨機存取(Random Access)迭代器
- 雙向(Bidirectional)
- 單向(Forward)迭代器
宣告
資料結構名<型別>::iterator 名字
寫出來大概長這樣
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int>::iterator it;
//vector後面會教喔喔喔
}
.begin()
回傳資料結構的第一個元素的迭代器
#include <iostream>
#include <vector>
using namespace std;
int main() {
//建立一個vector
vector<int> yeedragorz;
//讓it等於vector的第一項的迭代器
vector<int>::iterator it = yeedragorz.begin();
}
.end()
回傳資料結構的最後一項元素的後一項的迭代器
(對 就是這樣 不要懷疑
#include <iostream>
#include <vector>
using namespace std;
int main() {
//建立一個vector
vector<int> yungyaorz;
//讓it等於vector的第最後一項的後面一項的迭代器
vector<int>::iterator it = yungyaorz.end();
}
迭代器到底可以幹嘛
#include <iostream>
#include <vector>
using namespace std;
int main() {
//vector裡面放{1, 2}
vector<int> lemonweak = {1, 2};
vector<int>::iterator it = lemonweak.begin();
//*it取值
cout << *it << '\n';
}
透過*的操作(跟指標一樣 雖然我們沒教)
可以取得迭代器所對應的值
OUTPUT:
1
迭代器到底可以幹嘛
#include <iostream>
#include <vector>
using namespace std;
int main() {
//vector裡面放{1, 2}
vector<int> lemonweak = {1, 2};
vector<int>::iterator it = lemonweak.begin();
//it移到下一項
it++;
cout << *it << '\n';
}
透過++的操作
可以得到迭代器對應的後一項
OUTPUT:
2
迭代器到底可以幹嘛
#include <iostream>
#include <vector>
using namespace std;
int main() {
//vector裡面放{1, 2}
vector<int> lemonweak = {1, 2};
//記得.end()是最後的後一項
vector<int>::iterator it = lemonweak.end();
//it移到前一項
it--;
cout << *it << '\n';
}
根據迭代器種類的不同
有些運算是被允許的
例如 --的操作可以找到迭代器的前一項
OUTPUT:
2
所以...
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {1, 3, 2};
for(vector<int>::iterator it = v.begin(), it != v.end(); ++it) {
cout << *it << '\n';
}
}
OUTPUT:
1
3
2
我們可以用迴圈迭代ㄌ
好難寫QAQ
迭代器雖然寫起來並不直覺
但如果使用的好的話會是非常有用的工具ㄛ
( btw迭代器運算的時間複雜度都是\(O(1) \) )
尤其在編譯器版本不夠新的比賽
更幾乎是一定得使用迭代器呢!
那接下來我們就可以正式進入資料結構的世界囉
蕾絲狗
容器之王 Vector (我也不知道中文
需要#include<vector>
Vector是甚麼?
其實就是陣列
可以伸縮自如的那種
如果是Array
宣告時就要有大小了
例如:arr[10]
但vector沒在跟你管這些的啦
直接伸縮
宣告
vector<int> v;
//宣告名字為v,型態為int的vector
vector<型態> 名字
插入元素(後面)
(不能前面ㄛ)
vector<int> v;
v.push_back(5);
v.push_back(7);
.push_back()
時間複雜度: \(O(1)\)
訪問最前面元素
vector<int> v;
v.push_back(5);
v.push_back(7);
cout<<v.front();//5
.front()
時間複雜度: \(O(1)\)
訪問最後面元素
vector<int> v;
v.push_back(5);
v.push_back(7);
cout<<v.back();//7
.back()
時間複雜度: \(O(1)\)
將最後面元素移除
(不能前面ㄛ)
vector<int> v;
v.push_back(5);
v.push_back(7);
v.pop_back();//7被移出去
cout<<v.back();//5
.pop_back()
時間複雜度: \(O(1)\)
檢測Vector大小
.size()
時間複雜度: \(O(1)\)
vector<int> v;
v.push_back(5);
v.push_back(7);
v.pop_back();//7被移出去
cout<<v.size();//1
v.pop_back();//5被移出去
cout<<v.size();//0
檢測Vector是否為空
vector<int> v;
v.push_back(5);
if(v.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
.empty()//為空回傳true
時間複雜度: \(O(1)\)
其實接下來才是最重要的
vector<int> v;
v.push_back(5);
v.push_back(7);
v.push_back(8);
cout << v[1]; //7
for(int i = 0; i < v.size(); ++i) {
cout << v[i] << ' ';
}
輸出 "5 7 8"
vector可以用[],像陣列一樣取值
時間複雜度: \(O(1)\)
容器適配器
Container adapter
我們會利用某些容器的特殊性質
取其精隨,然後改造它
就變成了一個新的容器ㄛ
484 很神奇
1. Queue 隊列
需要#include<queue>
Queue是甚麼?
可以想成像排隊一樣,先進來的,就會先出去,秉持FIFO(先進先出)的特性。
Queue 只能訪問隊首/隊尾元素喔!
Queue的宣告和操作:
宣告
queue<int> q;
//宣告名字為q,型態為int的queue
queue<型態> 名字
插入元素
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
.push(x)
時間複雜度: \(O(1)\)
訪問隊首/尾元素
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
cout<<q.front();//5
cout<<q.back();//7
.front()/.back()
時間複雜度: \(O(1)\)
將隊首元素出隊
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
q.pop();//移除5
q.front();//回傳7
.pop()
時間複雜度: \(O(1)\)
檢測隊列大小
queue<int> q;
q.push(5); {5}
int s = q.size();//s = 1
q.push(7); {5,7}
s = q.size()// s = 2
.size()
時間複雜度: \(O(1)\)
檢測隊列是否為空
queue<int> q;
q.push(5); {5}
if(q.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
.empty()//為空回傳true
時間複雜度: \(O(1)\)
練習:
2. Stack 堆疊
需要#include<stack>
Stack是甚麼?
可以想成像一疊盤子一樣,後來放上去的,就要先拿走才能拿下面的,秉持FILO(先進後出)
的特性。
Stack 只能訪問最上方元素喔!
宣告
stack<int> stk;
//宣告名字為stk,型態為int的stack
stack<型態> 名字
插入元素
stack<int> stk;
stk.push(5);
stk.push(7);
.push(x)
時間複雜度: \(O(1)\)
訪問最上層元素
stack<int> stk;
stk.push(5);
stk.push(7);
cout<<stk.top();//7
.top()
時間複雜度: \(O(1)\)
將最上層元素移除
stack<int> stk;
stk.push(5);
stk.push(7);
stk.pop();//7被移出去
cout<<stk.top();//5
.pop()
時間複雜度: \(O(1)\)
檢測堆疊大小
stack<int> stk;
stk.push(5);
stk.push(7);
cout<<stk.size();//2
stk.pop();//7被移出去
cout<<stk.size()//1
.size()
時間複雜度: \(O(1)\)
檢測堆疊是否為空
stack<int> stk;
stk.push(5);
if(stk.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
.empty()//為空回傳true
時間複雜度: \(O(1)\)
練習:
3. Pair/Tuple
需要#include<utility> for pair
#include<tuple> for tuple
宣告
//宣告名字為p,有兩個int的pair
pair<int, int> p;
//宣告名字為t,有三個int的pair
tuple<int, int, int> t;
pair<型態, 型態> 名字
tuple<型態, 型態, 型態> 名字
賦值
pair<int, int> p1 = {1, 2};
pair<int, int> p2 = {2, 5};
p1 = p2; // p1 = {2, 5}
//tuple以此類推
pair = {a, b} 或另外一個型別相同的 pair
tuple = {a, b, c} 或另外一個型別相同的 tuple
取值
pair<int, int> p = {1, 2};
cout << p.first << ' ' << p.second; // 1 2
//tuple以此類推
.first 第一個 .second第二個 (.third 第三個)
記得讀英文ㄛ
酷酷的東東
pair<int, int> p = {1, 2};
p = make_pair(4, 10); // p = {4, 10}
//tuple以此類推
make_pair(a, b) 生成一個pair型別還幫你用好
同理我們有make_tuple(a, b, c)
STL
By lemonilemon
STL
- 647