---
slideOptions:
transition: slide
---
<!-- .slide: data-transition="fade-in convex-out" -->
# ```std::sort```<br>&<br>```next_permutation```
南天門日月卦長
---
<!-- .slide: data-transition="fade-in convex-out" -->
## 課前小提醒
* 今天要教的東西都會在```<algorithm>```標頭檔裡喔<br>記得```#include<algorithm>```!
* algorithm的中文翻譯就是演算法的意思
* 裡面有很多別人幫你寫好的演算法```template<>```
* [cplusplus Reference algorithm](http://www.cplusplus.com/reference/algorithm/)
---
<!-- .slide: data-transition="fade-in convex-out" -->
## ```std::sort```
---
## 排序好慢
還記得我們之前教的排序sort嗎?
複雜度都是$\mathcal O(N^2)$
但是排序目前被證明最快可以做到$\mathcal O(N log N)$
----
## STL可以排序嗎?
而且我們教的排序只能用在陣列上
那STL的<br>vector、stack、queue、deque、list可以排序嗎?
----
## STL sort
* 幸好C++ STL的開發者解決了這個問題
* 在```<algorithm>```裡面一些專門用來排序的函數
* 讓我們一起來看看這些函數吧!
---
## ```std::sort()```
* 複雜度$\mathcal O(N log N)$
* 用template實作,不用擔心型態問題
* 使用[Introsort(introspective sort)](https://en.wikipedia.org/wiki/Introsort)
* 可以使用在一般的陣列<br>或是有Random Access Iterator的STL容器
----
## Random Access Iterator?
初學者可以理解成:
有Random Access Iterator的STL容器
都有類似陣列的功能
* 以下為目前教過有Random Access Iterator的容器
* std::vector
* std::deque
----
## sort使用方法(STL容器)
```sort(起始iterator, 結尾iterator);```
```cpp=
std::vector<int> s;
for(int i=0;i<10;++i){
s.push_back(rand());//隨機產生元素
}
std::sort(s.begin(), s.end());//由小排到大
for(int e:s) std::cout << e << '\n';
```
----
## sort使用方法(一般陣列)
可以把iterator想成指標
```sort(陣列起始位置, 陣列結束位置);```
```cpp=
int s[10];
for(int i=0;i<10;++i) s[i] = rand();
std::sort(s, s+10);//由小排到大
for(int e:s) std::cout << e << '\n';
```
---
## compare function 1
比較函數
----
## 由大排到小?
直接呼叫sort他會幫你把東西由小排到大
那如果要由大排到寫怎麼辦呢?
難道在sort完之後再用for把陣列顛倒?
----
## 隱藏的東西
* std::sort其實有第三個隱藏的參數
* 這個參數要填的是一個特定型態的函數!
```cpp=
bool cmp(int a,int b){
return a > b;
}
int main(){
vector<int> s = {7, 1, 2, 2, 9, 4, 8, 7};
sort(s.begin(), s.end(), cmp);
for(int e:s) cout<<e<<endl;
return 0;
}
```
----
## 解析
* 以由大排到小為例,Type是要排序的元素型態
* 函數會回傳一個bool值:
* true: ```if(a!=b)```排序後a會排在b的前面
* false: ```if(a!=b)```排序後b會排在a的前面
```cpp=
bool function_name(Type a,Type b){
return a > b;
}
```
---
## compare function 2
struct/class 也可以拿去排序喔!
----
## struct排序
* 給你一個struct的陣列
* 你要先依照id由小到大排序
* 如果有id相同的部分就按造val由大到小排序
``` cpp=
struct GG{
int id, val;
};
vector<GG> s;
```
----
## 比較函數
* 原則和一般的比較函數一樣:
* true: ```if(a!=b)```排序後a會排在b的前面
* false: ```if(a!=b)```排序後b會排在a的前面
```cpp=
bool cmp(const GG &a, const GG &b){
if(a.id!=b.id) return a.id < b.id;
return a.val > b.val;
}
```
----
## 更精簡的寫法
這樣寫更短且函數執行解果不會改變
```cpp=
bool cmp(const GG &a, const GG &b){
return a.id<b.id||(a.id==b.id&&a.val>b.val);
}
```
---
<!-- .slide: data-transition="fade-in convex-out" -->
## std::stable_sort
---
## 穩定排序
* 如果陣列中有兩個相同的元素
* 排序的時候他們的相對位置不會改變的排序
* 就稱為穩定排序
* 剛剛教的std::sort **「不是」** 穩定排序
----
## std::stable_sort
* STL也有內建穩定排序
* 使用[merge sort](https://en.wikipedia.org/wiki/Merge_sort)
* 用法和std::sort一樣
* 也可以使用比較函數
* 以下附上幾個範例給大家參考
----
## 一般陣列
```cpp=
int s[]={7,1,2,2,8,9,5,6};
stable_sort(s, s+8);
```
----
## STL 容器
```cpp=
vector<int> s={7,1,2,2,8,9,5,6};
stable_sort(s.begin(), s.end());
```
----
## 比較函數
```cpp=
struct P{
int id, val;
P(int id, int val):id(id), val(val){}
}
bool cmp(const P &a, const P &b){
return a.id<b.id||(a.id==b.id&&a.val>b.val);
}
vector<P> s;
int main(){
for(int i=0;i<5;++i){
int id=rand();
s.puah_back(P(id,rand()));
s.puah_back(P(id,rand()));
}
stable_sort(s.begin(), s.end(), cmp);
}
```
---
<!-- .slide: data-transition="fade-in convex-out" -->
## next_permutation
中文直接翻譯就是下一個排列
---
## 所有排列
* 高中數學學過,$N$個東西可以有$N!$種排列
* 將所有排列按造字典順序印出來
* 你會發現第一個排列是由小排到大<br>最後一個排列是由大排到小
----
## 範例
* {1,2,3}的所有排列(依字典順序排):
1. {1, 2, 3}
2. {1, 3, 2}
3. {2, 1, 3}
4. {2, 3, 1}
5. {3, 1, 2}
6. {3, 2, 1}
----
## 下一個排列
* 我們說某個排列$P$的下一個排列
* 指的是將所有排列依字典順序排之後
* 編號為$P$的編號+1的那個排列
* 例如{1, 3, 2}的下一個排列就是{2, 1, 3}
---
## 產生下一個排列
* [Next lexicographical permutation algorithm](https://www.nayuki.io/page/next-lexicographical-permutation-algorithm)
* C++ STL已經幫你寫好了,只要會用就行了
----
## std::next_permutation
* 傳入的參數和sort一樣
* 他會還傳一個bool<br>如果是true表示現在這個排列不是最後一個排列
```cpp=
int s[] = {1, 3, 2};
bool end = next_permutation(s, s+3);
cout<<end<<'\n';
for(int i=0;i<3;++i) cout<<s[i]<<' ';
```
----
## STL 容器也可以用
```cpp=
vector<int> s = {1, 3, 2};
bool end = next_permutation(s.begin(), s.end());
cout<<end<<'\n';
for(int i=0;i<3;++i) cout<<s[i]<<' ';
```
----
## 產生所有排列
* 只要給出第一個排列
* 不斷呼叫next_permutation
* 直到回傳值變成false就行了
```cpp=
int s[]={1,2,3};
do{
for(int i=0;i<3;++i){
if(i) cout<<' ';
cout<<s[i];
}
cout<<'\n';
}while(next_permutation(s, s+3));
```
----
## 比較函數
next_permutation也可以用比較函數喔!
程式碼已經重複出現很多次了我就不放了
---
## 題目練習
[文字轉轉轉](https://neoj.sprout.tw/problem/153/)