By 想回家看直播的 佑佑
啊他們怎麼都用algo當標題
他們是不是在排擠我
好嘛那我也用algo
今天這裡放的是
被嚇到的晴
優先佇列
有優先級的佇列
aka 會幫你排序好
記得要有<queue>函式庫
宣告方式
priority_queue<型別,容器,排列方式> 名字;
priority_queue<int> pq;
priority_queue<int,vector<int> > pq;
priority_queue<int,vector<int>,less<int> > pq;
//這三個一模一樣喔
//容器預設vector,排列方式預設less(由大排到小)
型別:int,long long,pair<int,int>之類的
容器:反正就用vector就對了
排列方式:less<型別> 由大排到小
greater<型別>由小排到大
可以#define pq priority_queue;
那你就可以
pq<int> mypq;
把東東推進pq中並排列
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
O(log N)
回傳pq裡最前面的值
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
cout << pq.top();//輸出7
O(1)
把pq裡最前面的值踢掉
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
cout << pq.top();//輸出7
pq.pop();//{5,3}
cout << pq.top();//輸出5
O(log N)
回傳pq的大小
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
cout << pq.top();//輸出7
pq.pop();//{5,3}
cout << pq.top();//輸出5
cout << pq.size();//輸出2
O(1)
回傳pq是否為空
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
cout << pq.top();//輸出7
pq.pop();//{5,3}
cout << pq.top();//輸出5
cout << pq.size();//輸出2
cout << pq.empty();//輸出1
由於是回傳「是否」為空
所以回傳值為一個bool值
當pq裡有東西都回傳1
沒東西時回傳0
O(1)
#include<bits/stdc++.h>
using namespace std;
struct comp{
bool operator()(int a,int b){
return a<b;
}
};
int main(){
priority_queue<int,vector<int>,comp> pq;
}
這是由小到大
當你不想用struct寫一個排列函式
#include<bits/stdc++.h>
using namespace std;
int main(){
priority_queue<int,vector<int>,decltype([](int a,int b){return a<b;})> pq;
pq.push(5);
pq.push(7);
cout<<pq.top()<<endl;
}
一樣是由小到大
啊807說他也不知道decltype是什麼
啊所以不要像我一樣寫出pq[3]之類的東西
啊所以一樣不要像我一樣寫出pq.push_back()
鍊接串列
趁著你們還清醒講點難搞的
賽車比賽,給你n個人,初始是編號1在第一名,編號2在第二名,編號n在第n名
也可能有人直接被踢出遊戲
會想怎麼做?
會告訴你哪個編號超越了前一個人
$$1\,\le\,n\,\le\,10^5$$
第一反應就開個array,有人被超越就swap他們兩個
有人被踢出遊戲就把他的值改成0之類的
但是會太慢導致你吃TLE
這樣完全可以做
Why?
因為有人會被踢出遊戲
所以每次要swap的時候都要判斷下一項是不是0
所以
就太久了
最多有100000個人
失蹤:
既然要存每個點的值
那我們就開一個陣列啊
既然要存每個點往後接到哪個點
那就再開一個陣列啊
既然要存每個點往前接到哪個點
那就再開一個陣列啊
pre代表每一個點的前一個點是val中的第幾項
val用來存值
nxt代表每一個點的下一個點是val中的第幾項
先用表格來示範一下
pre | |
val | |
nxt |
我希望點與點連結起來的樣子
1
pre | |
val | 1 |
nxt |
我希望點與點連結起來的樣子
1 -> 2
pre | 0 | |
val | 1 | 2 |
nxt | 1 |
2的前一項是1
1在val陣列中是第0項
1的下一項是2
2在val陣列中是第1項
我希望點與點連結起來的樣子
1 -> 2 -> 3 -> 4
pre | 0 | 1 | 2 | |
val | 1 | 2 | 3 | 4 |
nxt | 1 | 2 | 3 |
我希望點與點連結起來的樣子
1 -> 3 -> 2 -> 4
pre | 0 | 1 | 2 | |
val | 1 | 2 | 3 | 4 |
nxt | 1 | 2 | 3 |
2的下一項是4
4在val陣列中是第3項
我希望點與點連結起來的樣子
1 -> 3 -> 2 -> 4
pre | 0 | 1 | 2 | |
val | 1 | 2 | 3 | 4 |
nxt | 1 | 3 | 3 |
3的下一項是2
2在val陣列中是第1項
4的前一項是2
2在val陣列中是第1項
我希望點與點連結起來的樣子
1 -> 3 -> 2 -> 4
pre | 0 | 1 | 1 | |
val | 1 | 2 | 3 | 4 |
nxt | 1 | 3 | 1 |
3的下一項是2
2在val陣列中是第1項
4的前一項是2
2在val陣列中是第1項
我希望點與點連結起來的樣子
1 -> 3 -> 2 -> 4
pre | 2 | 0 | 1 | |
val | 1 | 2 | 3 | 4 |
nxt | 2 | 3 | 1 |
以此類推
int pre[200000];
int val[200000];
int nxt[200000];
cin >> val[x];
pre[x] = a;//把x的前一項改成a
nxt[x] = b;//把x的後一項改成b
pre[b] = x;//把b的前一項改成x
nxt[a] = x;//把a的後一項改成x
把x插到a,b之間
nxt[pre[x]] = nxt[x];//把x的前一項的下一項改成x的下一項
pre[nxt[x]] = pre[x];//把x的下一項的前一項改成x的前一項
nxt[x] = pre[x];//把x的下一項改成x的前一項
pre[x] = pre[pre[x]];//把x的前一項改成x的前一項的前一項
nxt[pre[pre[x]]] = x;//把x的前一項的前一項的下一項改成x
pre[nxt[nxt[x]]] = x;//把x的下一項的下一項的前一項改成x
x會超過x前面的車
我是小丑
int runner_pr=prv[x];
int hum=nxt[x];
nxt[prv[prv[x]]]=x;
prv[nxt[d]]=runner_pr;
prv[x]=prv[prv[x]];
nxt[x]=runner_pr;
prv[runner_pr]=x;
nxt[runner_pr]=hum;
nxt[pre[x]] = nxt[x];//把x前一項的下一項改成x的下一項
pre[nxt[x]] = pre[x];//把x下一項的前一項改成x的前一項
nxt[x] = -1;//x的下一項改成-1
pre[x] = -1;//x的前一項改成-1
把x從陣列中刪除
-1代表前/後沒有相連的東西
大概就是這樣,三個陣列的做法你會看到一堆陣列名字和[]
如果你實在看不懂上面的東西
可以看看去年的struct+指標版
雖然linked_list我幾乎沒用過
集合
當然要用之前要 #include<set>
順帶一提它是一棵紅黑樹
set<型態> 命名
#define pii pair<int,int>
set<int> num;
set<pii> score;
預設從小排到大
把值插入set中
如果set中已有相同元素則無事發生
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
O(log N)
回傳set中某個元素的數量
因為在set中數量非0即1,所以可以拿來當bool用
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
num.count(1);//回傳0
num.count(3);//回傳1
O(log N)
踢除set中某個元素,並回傳true
若沒有該元素可踢除,則回傳false
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
while(num.erase(3)){
cout << "byebye";
}
O(log N)
回傳set剩餘的元素數量
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
cout << num.size();//輸出2
O(1)
回傳一個bool,代表set是否為空
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
if(!num.empty()){
cout << "something in the set\n"
num.erase(3);
num.erase(5);
}
else{
cout << "now nothing in the set\n"
}
//輸出
//something in the set
//now nothing in the set
O(1)
強制清空set中所有元素
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
if(!num.empty()){
num.clear();
}
//{}
O(N)
顧名思義,未經排序的set
語法與set基本一致
只是將宣告的set改成unordered_set
set | unordered_set | |
---|---|---|
.insert() | O(log N) | O(1) |
.erase() | O(log N) | O(1) |
.count() | O(log N) | O(N) |
unordered_set<int> num;
num.insert(10);{10}
num.insert(2);{10,2}
num.clear();
if(num.empty()){
cout << "nothing";
}
else{
cout << "something";
}
//輸出nothing
顧名思義,有重複元素的set
語法與set基本一致
只是將宣告的set改成multiset
有重複元素
所以.count()會回傳該元素的數量喔
multiset<int> num;
num.insert(1);//{1}
num.insert(1);//{1,1}
num.count(1);// 2
num.clear();
if(num.empty()){
cout << "nothing";
}
else{
cout << "something";
}
//輸出nothing
關聯式容器
當然要用之前要 #include<map>
順帶一提它也是一棵紅黑樹
啊它就是
index可以不為數字的
array啊
map<key型態,value型態> 命名
map <int, int> num; // int 對到 int
map <char, int> a; // char 對到 int
map <string, int> animal; // string 對到 int
map <string, string> couple;
couple.insert({"晴", "佑佑"});
你可以
但是insert是壞東西
更簡單的做法是跟array一樣
map <string, string> couple;
couple["晴"] = "佑佑";
O(log N)
啊就,取值,你在 array 中怎麼做,在 map 中就怎麼做
map <string, string> museum;
museum["晴"] = "顯然我是清純小女僕";
museum["世宗"] = "我超勝利";
museum["佑佑"] = "沒關係我先逝世";
museum["Cc"] = "HHPY";
museum["水獺"] = "硬起來了";
museum["柴柴"] = "這就是標準的斷章取義";
museum["807"] = "你檢查腋下";
cout << museum["佑佑"];
//沒關係我先逝世
O(log N)
啊就,清空
map <int, int> dic;
dic[71] = 1e9 + 17;
cout << dic[71] << '\n'; //1000000017
dic.clear();
cout << dic[71] << '\n'; //沒看過的 key 預設是 0
O(N)
key一樣從0開始往後加,並且可以隨時新增value
學長你有什麼問題幹嘛不用vector就好
學長你有什麼問題幹嘛不用vector就好
事實上有時候map會優於vector
假如今天題目給n
$$1\,\le\,n\,\le\,1e15$$
用vector開1e15的大小?
放心時間空間都爛給你看
假如今天題目給n
$$1\,\le\,n\,\le\,1e15$$
但你發現這麼大的範圍總共只會給你10筆資料
這時候map只要開10格就解決了
所以你會發現
當數據離散時
map是好東西
顧名思義,未經排序的map
語法與map基本一致
只是將宣告的map改成unordered_map
好眼熟的前綴
map | unordered_map | |
---|---|---|
加key | O(log N) | O(1) |
改值 | O(log N) | O(1) |
演算法函式庫
好耶最簡單的部分
標頭檔之一,用之前記得
#include <algorithm>
幫你寫好一堆好用的函式
其實萬用標頭檔就包含這個了
不用什麼都手刻了好感動
max(a,b)
min(a,b)
回傳a,b中較大的那一個
回傳a,b中較小的那一個
int a=0;
int b=1;
cout << max(a,b);
//輸出1
cout << min(a,b);
//輸出0
max(a,b)
min(a,b)
順帶一提,如果你要比較三個以上的數據的話
int a=0;
int b=1;
int c=2;
cout << max(a, max(b,c));
//輸出2
請在max/min裡面包max/min
swap(a,b)
將a,b中的數值交換
a,b可以是任何STL或變數
但必須同型態
int a=7;
int b=17;
swap(a,b);
cout << a << " " << b;
//輸出17 7
你們還記得iterator(迭代器)是什麼嗎
忘了去翻上節課簡報
這邊只舉一些例子
.begin()
表示首位元素的記憶空間位址
.end()
表示末位元素的下一個的記憶空間位址
lower_bound(iterator,iterator,value)
upper_bound(iterator,iterator,value)
lower_bound回傳的指標指向
「大於等於」value的「最小值」
回傳一個指標
upper_bound回傳的指標指向
「大於」value的「最小值」
聽起來是很繞口啦
對排序好的資料
lower_bound(iterator,iterator,value)
upper_bound(iterator,iterator,value)
vector<int> = {1,2,3,4,5};
auto lower = lower_bound(arr.begin(),arr.end(),3);
auto upper = upper_bound(arr.begin(),arr.end(),3);
cout << &lower << " " << &upper;
//輸出3 4
順帶一提,auto是用來自動判別資料型態然後宣告的
像他在這裡就會偵測到lower_bound回傳的是指標
於是自動把lower宣告成一個指標
這樣就不用一直加*了好耶
lower_bound(iterator,iterator,value)
upper_bound(iterator,iterator,value)
vector<int> = {1,2,3,4,5};
auto lower = lower_bound(arr.begin(),arr.end(),3);
auto upper = upper_bound(arr.begin(),arr.end(),3);
cout << &lower << " " << &upper;
//輸出3 4
arr中
大於等於3的最小值為3
故lower = 3
大於3的最小值為4
故upper = 4
先忽略這個白到哭的顏色
這是去年你們學長的學長遇到的困擾
這是去年你們學長的學長的學長的簡報
這是今年你們學長的簡報
明年的學術,你知道該怎麼做了吧
當你今天要排序10000個數字
學長be like:寫個for迴圈,每次swap兩個數字
學長哪裡沒寫好:需要兩個for迴圈
for(int i = 0; i < 10000; i++){
for(int j = 0; j < 10000-i; j++){
if(arr[j] >= arr[j+1]){
swap(arr[j], arr[j+1]);
}
}
}
每次run完整個陣列一定會把最大/最小值搬到最右邊
但不一定把最小/最大值搬到最左邊
$$O(N^2)$$
有沒有不用每次要排序的時候都要磨掉一堆指紋的寫法
有!
std::sort
它甚至是O(N log N),超級快
int arr[10]{2,4,1,5,3,8,6,9,7,10};
sort(arr,arr+10);//預設從小排到大
//{1,2,3,4,5,6,7,8,9,10}
sort(arr,arr+10,greater<int>);
//{10,9,8,7,6,5,4,3,2,1}
sort(iterator,iterator,排列方式)
排列方式有
less<int>
greater<int>
啊你如果要其他的就像前面一樣自己寫一個函式囉
提供一下愛用array的人的寫法
vector<int> vec = {2,4,1,5,3,8,6,9,7,10};
sort(vec.begin(),vec.end());//預設從小排到大
//{1,2,3,4,5,6,7,8,9,10}
sort(vec,begin(),vec.end(),greater<int>);
//{10,9,8,7,6,5,4,3,2,1}
sort(iterator,iterator,排列方式)
排列方式有
less<int>
greater<int>
啊你如果要其他的就像前面一樣自己寫一個函式囉
顯然vector特別好用
肯定有啊
最優的情況是O(1)
最壞的情況是O(∞)
學長你做簡報做瘋囉
啊O(∞)不就永遠做不完
最優的情況是O(1)
最壞的情況是O(∞)
你運氣夠好就隨機一次剛好排成順序
運氣不好就排到死都排不好
對整個要排序的容器做random
然後檢查排好了沒