Lecturer:Lemon
Slides:yeedrag
複雜度是我們估計一個程式的執行花費的工具
也就是代表程式的運算次數 or 記憶體用量
複雜度越高,當然電腦的負擔就越大
而你們就會吃 TLE、MLE
所以在我們開始寫程式碼前
我們通常會先估計程式的複雜度
避免我們陷入明明刻完程式碼
測資卻全部都錯光光的窘境
如果你有看過演算法的題目、講義
你應該會常常看到\(O(1) , O(n), O(n!) \)之類的
前面的\(O\)就代表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;
}
反正就是容器函式迭代器的酷東西.w.
A:你開心也可以,但很多題目使用STL會使其方便非常多,但不同資料結構的複雜度可能有差異喔!
簡單來說,假設今天我們有一個容器C
但我們的 C 長的並不像陣列
原本我們可以透過for迴圈對一個Array做遍歷
但 C 長得比較畸形
所以STL中便提供了迭代器來解決這樣的問題
雖然迭代器稍微有點毒瘤(?
但如果打校內賽編譯器很爛就不得不用ㄌ
寫出來大概長這樣
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int>::iterator it;
//vector後面會教喔喔喔
}
回傳資料結構的第一個元素的迭代器
#include <iostream>
#include <vector>
using namespace std;
int main() {
//建立一個vector
vector<int> yeedragorz;
//讓it等於vector的第一項的迭代器
vector<int>::iterator it = yeedragorz.begin();
}
回傳資料結構的最後一項元素的後一項的迭代器
(對 就是這樣 不要懷疑
#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
我們可以用迴圈迭代ㄌ
迭代器雖然寫起來並不直覺
但如果使用的好的話會是非常有用的工具ㄛ
( btw迭代器運算的時間複雜度都是\(O(1) \) )
尤其在編譯器版本不夠新的比賽
更幾乎是一定得使用迭代器呢!
那接下來我們就可以正式進入資料結構的世界囉
蕾絲狗
需要#include<vector>
可以伸縮自如的那種
如果是Array
宣告時就要有大小了
例如:arr[10]
但vector沒在跟你管這些的啦
直接伸縮
vector<int> v;
//宣告名字為v,型態為int的vector
(不能前面ㄛ)
vector<int> v;
v.push_back(5);
v.push_back(7);
時間複雜度: \(O(1)\)
vector<int> v;
v.push_back(5);
v.push_back(7);
cout<<v.front();//5
時間複雜度: \(O(1)\)
vector<int> v;
v.push_back(5);
v.push_back(7);
cout<<v.back();//7
時間複雜度: \(O(1)\)
(不能前面ㄛ)
vector<int> v;
v.push_back(5);
v.push_back(7);
v.pop_back();//7被移出去
cout<<v.back();//5
時間複雜度: \(O(1)\)
時間複雜度: \(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<int> v;
v.push_back(5);
if(v.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
時間複雜度: \(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"
時間複雜度: \(O(1)\)
Container adapter
我們會利用某些容器的特殊性質
取其精隨,然後改造它
就變成了一個新的容器ㄛ
484 很神奇
需要#include<queue>
Queue 只能訪問隊首/隊尾元素喔!
queue<int> q;
//宣告名字為q,型態為int的queue
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
時間複雜度: \(O(1)\)
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
cout<<q.front();//5
cout<<q.back();//7
時間複雜度: \(O(1)\)
queue<int> q;
q.push(5); {5}
q.push(7); {5,7}
q.pop();//移除5
q.front();//回傳7
時間複雜度: \(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
時間複雜度: \(O(1)\)
queue<int> q;
q.push(5); {5}
if(q.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
時間複雜度: \(O(1)\)
需要#include<stack>
Stack 只能訪問最上方元素喔!
stack<int> stk;
//宣告名字為stk,型態為int的stack
stack<int> stk;
stk.push(5);
stk.push(7);
時間複雜度: \(O(1)\)
stack<int> stk;
stk.push(5);
stk.push(7);
cout<<stk.top();//7
時間複雜度: \(O(1)\)
stack<int> stk;
stk.push(5);
stk.push(7);
stk.pop();//7被移出去
cout<<stk.top();//5
時間複雜度: \(O(1)\)
stack<int> stk;
stk.push(5);
stk.push(7);
cout<<stk.size();//2
stk.pop();//7被移出去
cout<<stk.size()//1
時間複雜度: \(O(1)\)
stack<int> stk;
stk.push(5);
if(stk.empty()){
cout<<"empty!\n";
} else {
cout<<"not empty!\n"
}
//輸出 "not empty!"
時間複雜度: \(O(1)\)
需要#include<utility> for pair
#include<tuple> for tuple
//宣告名字為p,有兩個int的pair
pair<int, int> p;
//宣告名字為t,有三個int的pair
tuple<int, int, int> t;
pair<int, int> p1 = {1, 2};
pair<int, int> p2 = {2, 5};
p1 = p2; // p1 = {2, 5}
//tuple以此類推
pair<int, int> p = {1, 2};
cout << p.first << ' ' << p.second; // 1 2
//tuple以此類推
pair<int, int> p = {1, 2};
p = make_pair(4, 10); // p = {4, 10}
//tuple以此類推