# 排序、二分搜
---
# C++內建排序
## sort()函式
----
sort()有兩個輸入、一個非必要輸入
分別是排序起點、終點,和比較方法
起點和終點為迭代器或指標,比較方法則是
----
排序會從起點排到終點前一個,
內建的排序會比自己寫的排序快,所以除非有特殊需求,不然不用自己寫
----
可以對傳統陣列排序
```cpp=
int arr[] = {4, 1, 5, 1, 2, 0};
sort(arr, arr+6);
for(int i:arr){ // 這樣可以
cout<<i<<' ';
} // 0 1 1 2 4 5
cout<<'\n';
```
----
也可以對STL的vector排序
```cpp=
vector<int> v = {4, 1, 3, 1, 2};
sort(v.begin(), v.end()); // 從最開始排序到最後一個
// .end()代表的是最後一項的下一項,所以不用再加1
for(int i:v){
cout<<i<<' ';
} // 1 1 2 3 4
cout<<'\n';
```
----
也可以對部分的陣列排序
```cpp=
// 傳統陣列
int arr[] = {4, 1, 5, 1, 2, 0};
sort(arr+1, arr+5); // 從索引[1]排序到索引[5-1]
for(int i:arr){
cout<<i<<' ';
} // 4 1 1 2 5 0
cout<<'\n';
// vector
vector<int> v = {4, 1, 3, 1, 2};
sort(v.begin()+1, next(v.end(), -1)); // 迭代器可以直接加
// 也可以用next()來移動
for(int i:v){
cout<<i<<' ';
} // 4 1 1 3 2
cout<<'\n';
```
----
## 例題 [ZJ a104](https://zerojudge.tw/ShowProblem?problemid=a104)
重複輸入直到結束
每次給要排序的數量和數列
輸出由小到大排序好的數列
補充:如果要輸入到結束,可以用while(cin>>n)
----
## 解答
```cpp=
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
while(cin>>n){
vector<int> v(n);
for(int i=0; i<n; ++i){
cin>>v[i];
}
sort(v.begin(), v.end());
for(int i=0; i<n; ++i){
cout<<v[i]<<' ';
}
cout<<'\n';
}
return 0;
}
```
---
# 改變排序比較
c++有內建的方法可以讓我們把sort變成降序
但如果是一些內容物不能比較的陣列(像是二維陣列)就要用一些自己的方法
----
```cpp=
vector<int> v = {4, 1, 3, 1, 2};
sort(v.begin(), v.end(), greater<int>()); // 改成大的在前面
// 要注意型態
for(int i:v){
cout<<i<<' ';
} // 4 3 2 1 1
cout<<'\n';
```
----
預設是less\<T\>,可以改成greater\<T\>
要注意,在排序中,相等不應該回傳真
否則在某些排序法中會出錯
要排序string的內容,要用less\<char\>或
greater\<char\>
---
# 自訂比較方法
---
## 方法一
# 比較函式
----
以一個bool函式,做為第三個參數傳入
兩個參數都要是目標類型
----
### 範例
比vector\<int\>
看哪個的總和比較小
```cpp=
bool compare (vector<int> v1, vector<int> v2){
int s1=0, s2=0;
for(int i=0; i<v1.size(); i++){
s1+=v1[i];
s2+=v2[i];
}
if(s1<s2)
return true;
else
return false;
}
```
----
```cpp=
vector<int> v1 = {2, 1, 3, 4, 2}, v2 = {3, 2, 0, 1, 4};
cout<<compare(v1, v2)<<'\n'; // 直接比兩個陣列
vector<vector<int>> v;
v.push_back(v1); v.push_back(v2); // 放進二微陣列
sort(v.begin(), v.end(), compare);
for(vector<int> vi:v){
for(int i:vi)
cout<<i<<' ';
cout<<'\n';
}
cout<<'\n';
```
輸出
```
0 (代表False)
3 2 0 1 4
2 1 3 4 2
```
---
## 方法二
# 運算子多載
----
宣告一個bool類型函式,接著
operator加上比較符號(通常是<),代表運算子多載
接著,有兩個參數,都是要比較的類型
最後,寫好要怎麼比
----
### 範例
比vector\<int\>
先比大小,再比每一項
```cpp=
bool operator < (vector<int> v1, vector<int> v2){
// 宣告運算子多載
if(v1.size()<v2.size()) // 先比陣列大小
return true;
if(v1.size()>v2.size())
return false;
for(int i=0; i<v1.size(); ++i){// 再逐項比較
if(v1[i]<v2[i])
return true;
if(v1[i]>v2[i])
return false;
}
return false; // 如果上面比完都沒回傳,代表兩個一樣
}
```
----
接著,我們可以直接用這個來比較,也可以用sort()來比vector\<int\>了
```cpp=16
vector<int> v1 = {4, 1, 3, 1, 2}, v2 = {1, 2, 5, 4, 6};
cout<<(v1<v2)<<'\n'; // 直接比陣列
vector<vector<int>> v;
v.push_back(v1); v.push_back(v2); // 放入二維陣列
sort(v.begin(), v.end()); // 改成大的在前面
for(vector<int> vi:v){
for(int i:vi)
cout<<i<<' ';
cout<<'\n';
}
cout<<'\n';
```
輸出
```
1 (代表True)
1 2 5 4 6
4 1 3 1 2
```
---
# 二分搜
----
## 搜尋
搜尋是在給一個元素的情況下
找到對應的索引值
最簡單的方法是逐一存取,只是會花不少時間
----
二分搜必須在已經排好的資料中搜尋
首先,取得在陣列中央的值,比較和目標比較
如果比目標小,代表目標更大,就減少了一半的搜索範圍
如果筆目標小,也一樣減少了一半範圍
重複直到找到或確定不在陣列中
----
程式範例
```cpp=
// v 是一個已經排序好的vector
int bisearch(vector<int> v, int target, int begin, int end){
int l=begin, r=end, m; // l跟r是目標可能在的範圍
while(l<=r){
m=(l+r)/2; // 每次取中間
if(v[m]==target){ // 如果找到了
while(v[m]==target) --m; // 找到這些相同數字的第一個
return m+1;
}
if(v[m]<target) l = m+1; // 在m左邊的都不可能
else r = m-1; // 在m右邊的都不可能
}
return -1; // 如果沒找到(通常會在外面處理沒找到的情況)
}
```
----
## 例題 [ZJ f679](https://zerojudge.tw/ShowProblem?problemid=f679)
給定一由小到大排序的數列及其大小
找到目標編號是否在數列中
----
## 例題解答
```cpp=
#include <bits/stdc++.h>
using namespace std;
vector<int> v;
int n;
bool bisearch(int x){
int l=0, r=n, m;
while(l<=r){
m=(l+r)/2;
if(v[m]==x) return true;
if(v[m]>x) r=m-1;
else l=m+1;
}
return false;
}
int main()
{
int q, x;
cin>>n>>q;
v.resize(n,0);
for(int i=0; i<n; i++){
cin>>v[i];
}
for(int i=0; i<q; i++){
cin>>x;
if(bisearch(x))
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}
```
---
# 額外補充
---
# 排序法
不會講太細,有興趣可以查
----
## 泡沫排序
每一輪會從第一項跟第二項比,到最後兩項相比
每次比較會把小的放前面,大的放後面
因為每一輪都會讓最大的到最後而稱作泡沫
如果有一輪都沒執行交換就代表排好了
時間複雜度$O(n^2)$
----
## 插入排序
每次取未排序的第一項,找到它應該要
在已排序區的位置(搜尋)
時間複雜度$O(n^2)$
----
## 合併排序
每次將資料分成兩半,直到剩下一個時不再分割
接著將資料以原本的分割順序合併,並同時排序
時間複雜度$O(n\ logn)$,但空間需求較大
----
## 快速排序
每次從資料中選出一個基準
比基準小的放一邊,比較大的放另一邊
接著兩邊再各自選出基準排序
持續直到排序結束
時間複雜度$O(n\ logn)$
---
# sort()
# lambda匿名函式
---
lambda匿名函式可以在函式內寫函式
```cpp=
vector<vector<int>> v={{2,4,2,3},{2,0,2,5}};
sort(v.begin(), v.end(),
[](vector<int> v1, vector<int> v2){ // 宣告lambda函式
int n=min(v1.size(), v2.size());
for(int i=0; i<n; i++){ // 先逐項比,直到其中一個的最後
if(v1[i]<v2[i])
return true;
if(v1[i]>v2[i])
return false;
}
if(v1.size()<v2.size()) // 最後比陣列大小
return true;
return false;// 沒有比較小
});
for(auto vi:v){
for(int i:vi)
cout<<i<<' ';
cout<<'\n';
}
```
輸出
```
2 0 2 5
2 4 2 3
```
{"description":"排序是將資料以給定的比較方法排列","contributors":"[{\"id\":\"00ad9127-6491-4b3d-829b-7847a217f8e5\",\"add\":6483,\"del\":947}]","title":"排序、二分搜"}