{algo[3]}
By 想回家看直播的 佑佑
啊他們怎麼都用algo當標題
他們是不是在排擠我
好嘛那我也用algo
今天要教什麼
- priority queue
- list
- set
- map
- sort
- algorithm
- 題單
今天這裡放的是
被嚇到的晴

Priority queue
優先佇列

複習一下queue
-
佇列
-
排隊
-
先進先出
-
先來先走
-
FIFO
priority

priority_queue
有優先級的佇列
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;
.push()
把東東推進pq中並排列
priority_queue<int> pq;
pq.push(3);//{3}
pq.push(7);//{7,3}
pq.push(5);//{7,5,3}
O(log N)
.top()
回傳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)
.pop()
把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)
.size()
回傳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)
.empty()
回傳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;
}
這是由小到大
By 807的神奇寫法
當你不想用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的函式做不到對中間項改值
啊所以不要像我一樣寫出pq[3]之類的東西
- pq是push()
- vector是push_back()
啊所以一樣不要像我一樣寫出pq.push_back()
linked_list
鍊接串列
趁著你們還清醒講點難搞的
那我們先來看例題 by失蹤
賽車比賽,給你n個人,初始是編號1在第一名,編號2在第二名,編號n在第n名
也可能有人直接被踢出遊戲
會想怎麼做?
會告訴你哪個編號超越了前一個人
1≤n≤105
那我們先來看例題 by失蹤
第一反應就開個array,有人被超越就swap他們兩個
有人被踢出遊戲就把他的值改成0之類的
但是會太慢導致你吃TLE
這樣完全可以做
Why?
那我們先來看例題 by失蹤
因為有人會被踢出遊戲
所以每次要swap的時候都要判斷下一項是不是0
所以
就太久了
最多有100000個人
linked_list是什麼
- 每個點存自己的值
- 每個點會知道前一個點和下一個點
- 可以往兩個點之間插入點
linked_list是什麼
失蹤:
既然要存每個點的值
那我們就開一個陣列啊
既然要存每個點往後接到哪個點
那就再開一個陣列啊
既然要存每個點往前接到哪個點
那就再開一個陣列啊
宣告
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我幾乎沒用過
set
集合
set是什麼
- 具有唯一性
- 內容經過排序
- 好多衍生東東
當然要用之前要 #include<set>
順帶一提它是一棵紅黑樹
宣告
set<型態> 命名
#define pii pair<int,int>
set<int> num;
set<pii> score;
預設從小排到大
.insert()
把值插入set中
如果set中已有相同元素則無事發生
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
O(log N)
.count()
回傳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)
.erase()
踢除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)
.size()
回傳set剩餘的元素數量
set<int> num;
num.insert(3);//{3}
num.insert(5);//{3,5}
num.insert(3);//{3,5}
cout << num.size();//輸出2
O(1)
.empty()
回傳一個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)
.clear()
強制清空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)
unordered_set
顧名思義,未經排序的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
unordered_set<int> num;
num.insert(10);{10}
num.insert(2);{10,2}
num.clear();
if(num.empty()){
cout << "nothing";
}
else{
cout << "something";
}
//輸出nothing
multiset
顧名思義,有重複元素的set
語法與set基本一致
只是將宣告的set改成multiset
有重複元素
所以.count()會回傳該元素的數量喔
multiset
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
map
關聯式容器
map是什麼
- 它看起來好陣列喔
- 沒那麼多衍生東東
當然要用之前要 #include<map>
順帶一提它也是一棵紅黑樹
map是什麼
- 自訂index(key)
- key不侷限於數字
- key有唯一性
- 一個key對一個value
- 也會自動排序
啊它就是
index可以不為數字的
array啊
宣告
map<key型態,value型態> 命名
map <int, int> num; // int 對到 int
map <char, int> a; // char 對到 int
map <string, int> animal; // string 對到 int
加入一個新的key
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)
.clear()
啊就,清空
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≤n≤1e15
用vector開1e15的大小?
放心時間空間都爛給你看
小問題

假如今天題目給n
1≤n≤1e15
但你發現這麼大的範圍總共只會給你10筆資料
這時候map只要開10格就解決了
所以你會發現
當數據離散時
map是好東西

unordered_map
顧名思義,未經排序的map
語法與map基本一致
只是將宣告的map改成unordered_map
好眼熟的前綴
map | unordered_map | |
---|---|---|
加key | O(log N) | O(1) |
改值 | O(log N) | O(1) |
algorithm
演算法函式庫
好耶最簡單的部分
<algorithm>
標頭檔之一,用之前記得
#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(N2)
怎麼做快一點?
有沒有不用每次要排序的時候都要磨掉一堆指紋的寫法
有!
std::sort
它甚至是O(N log N),超級快
sort
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的人的寫法
sort
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特別好用
有比sort更快的方法嗎
肯定有啊
Bogo Sort 猴子排序
最優的情況是O(1)
最壞的情況是O(∞)
學長你做簡報做瘋囉
啊O(∞)不就永遠做不完
有比sort更快的方法嗎
Bogo Sort 猴子排序
最優的情況是O(1)
最壞的情況是O(∞)
你運氣夠好就隨機一次剛好排成順序
運氣不好就排到死都排不好
對整個要排序的容器做random
然後檢查排好了沒
題單
algo[3]
By ganyumywife
algo[3]
- 316