---
# System prepended metadata

title: STL & 排序二分搜

---

<style>
/*黑條*/
.blackout {
    color: #191919;
    background-color: #191919;
  }
  .blackout:hover {
    color: white;
  }
/* 設定右上角留言按鈕 */
#comment-app .open-comments {
    background: transparent;
}
.btn-default {
  background: transparent;
  border-color: #fff99399;
}
.btn-default:hover {
  background: transparent;
  -webkit-animation: discolor 5s linear infinite,
                      glint 2s ease infinite;
          animation: discolor 5s linear infinite,
                      glint 2s ease infinite;
}
.ui-comment-app .open-comments .btn.ui-open-comments {
  color: #D14606;
}
.ui-comment-app .open-comments .btn.ui-open-comments:hover {
  color: #fff993;
}
/* 設置愛心、收藏、小鈴鐺按鈕 */
.community-button {
  color: #D14606;
}
.community-button:hover {
  color: #FFF993;
  background: transparent;
  -webkit-animation: discolor 5s linear infinite,
                      glint 2s ease infinite;
          animation: discolor 5s linear infinite,
                      glint 2s ease infinite;
}

/* h6 date 標籤 */
.markdown-body h6 em {
    position: absolute;
    top: 40px;
    right: 20px;
}

/* 目錄 [TOC] */
.markdown-body .toc > ul {
    position: relative;
    padding-top: 40px;
    padding-bottom: 12px;
    background: #fff;
    border-radius: 4px;
}
.markdown-body .toc > ul::before {
    position: absolute;
    content: '——目錄——';
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    font-size: 24px;
    text-shadow: 3px 3px 3px #ca20;
}

/* 設定圖檔的顯示 */
.markdown-body img {
    background-color: transparent;
    border-radius: 4px;
}

/* 設定小目錄的背景顏色 */
.ui-toc-dropdown {
    background-color: #fff;
}

/* 設定行動裝置中，小目錄按鈕 */
.ui-toc-label.btn {
    background: linear-gradient(180deg, #ca252160 0%, #eca44260 100%);
    color: #ffffff90;
}
.ui-toc-label.btn:hover {
    background: linear-gradient(180deg, #ca252190 0%, #eca44290 100%);
    color: #ffffff;
}

/* 設定小目錄內連結 */
.ui-toc-dropdown .nav>li>a,
.toc-menu>a {
    color: #8E8D8D;
    font-weight: bold;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
    color: #FFF993;
    border-left: 1px solid #FFF993;
}

.ui-toc-dropdown .nav>.active:focus>a,
.ui-toc-dropdown .nav>.active:hover>a,
.ui-toc-dropdown .nav>.active>a {
    color: #FFF993;
    border-left: 1px solid #FFF993;
    -webkit-animation: discolor 5s linear infinite,
                        glint 2s ease infinite;
            animation: discolor 5s linear infinite,
                        glint 2s ease infinite;
}

.toc-menu>a:focus,
.toc-menu>a:hover {
    color: #FFF993;
}



/* 設定 mark */
.markdown-body mark {
    color: #fff;
    background: #3E8252;
    border-radius: 4px;
    margin: 2px;
}
/* 設定 strong 粗體 */
.markdown-body strong {
    background: #;
    border-radius: 4px;
    padding-left: 2px;
    padding-right: 2px;
}

/* 設定連結 */
a,
.open-files-container li.selected a {
    color: #9399DD;
}
a:hover,
.open-files-container li.selected a:hover {
    color: #9399DD;
}

/* 設定 ins */
.markdown-body ins {
    text-decoration: none;
    text-shadow: 3px 3px 3px #AFAFAF;
}


/* 回到最上面的按鈕 */
.markdown-body a > .fa-chevron-up {
    position: fixed;
    bottom: 20px;
    right: 20px;
    padding: 4px;
    border-radius: 4px;
    color: #fff;
    background: linear-gradient(180deg, #ca252160 0%, #eca44260 100%);
}
.markdown-body a:hover > .fa {
    background: linear-gradient(180deg, #ca252195 0%, #eca44295 100%);
    -webkit-animation: discolor 5s linear infinite,
                        glint 2s ease infinite;
            animation: discolor 5s linear infinite,
                        glint 2s ease infinite;
}


/* 動畫 */
@keyframes discolor {
    0%, 100% {filter: hue-rotate(0);}
    50% {filter: hue-rotate(360deg);}
}

</style>
# 競賽程式進階技巧
:::info
所有範例程式非唯一正解
有任何錯誤歡迎糾正
這份講義包含$STL和sort-searching$
對初學者來說很硬，要背很多東西，但一旦會了就非常方便
:::
:::spoiler 題庫
先給你們一隻水豚，小心品嚐 **:>**

![](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/capybara.jpg?raw=true)
* [$一維vector$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a194)
* [$一維vector(2)$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a439)
* [$二維vector$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a255)
* [$set$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a153)
* [$set2$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a155)
* [$map1$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a176)
* [$map2$](https://zerojudge.cchs.chc.edu.tw/ShowProblem?problemid=a178)
:::

---

## vector 
### vector介紹
 `vector`算是從基礎語法晉升到競賽程式最明顯的指標了，所以特別的重要，**vector是可以動態改變大小的陣列**，不像傳統陣列開了就不能再修改
### vector建立
```cpp
vector<type> v(size, value);
• type:資料型態 e.g:int&char
• size:陣列的初始大小
• value:陣列的初始值
```
$e.g.$
```cpp=
//以下語法都是合法的
vector<int> v1; // v1 = {} 空 的 陣 列
vector<char> v2; // v2 = {} 空 的 陣 列
vector<int> v3(5); // v3 = {0, 0, 0, 0, 0}
vector<int> v4(3, 17); // v4 = {17, 17, 17}
vector<int> v5 = {4, 8, 7};
```
### vector常用語法
```cpp=
vector<int> name;
• name.push_back(val):將 val 加進陣列的最後  //O(1)
• name.pop_back():將陣列最後的元素刪除  //O(1)
• name.size():回傳陣列的大小(無號整數)  //O(1)
• name.empty():回傳陣列是否為空  //O(1)
• name.clear():將陣列清空  //O(size)
• name.resize(size, value):
將陣列的大小設為 size,多出來的部分設為 val //O(Δsize)
```
$e.g.$
```cpp=
vector<int> v; // v = {}
v.push_back(5); // v = {5}
v.push_back(2); // v = {5, 2}
/***********************************/
v.resize(5, 3); // v = {5, 2, 3, 3, 3}
v.size(); // 5
v.empty(); // false
/***********************************/
v.pop_back(); // v = {5, 2, 3, 3}
v.clear(); // v = {}
```
### 二維vector
二維vector常常被使用來做*linked-list*或圖，完全沒有空間被浪費

$e.g.$
```cpp=
vector<vector<int>> v1; // v1 = {} 空 的 矩 陣

vector<vector<int>> v2(3);
// v2 = { {}, {}, {} } 矩 陣 內 有 三 個 空 的 陣 列

vector<vector<int>> v3(3, vector <int>(2));
// v3 = { {0, 0}, {0, 0}, {0, 0} }

vector<vector<int>> v4(3, vector <int>(2, 5));
// v4 = { {5, 5}, {5, 5}, {5, 5} 

//如果您使用的是devc++則改成這樣，否則會報錯
//                |這裡原本沒空格
vector<vector<int> > v1;
/**************************/
vector<vector<int>> v(3); // v = { {}, {}, {} }
v[1].push_back(1); // v = { {}, {1}, {} }
```
## $STL$
**註**: `string`也算$STL$
### 迭代器
***大部分***都能這樣用所以可以背起來 
![image](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/vec1.png?raw=true)
![image](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/vec2.png?raw=true)
```cpp=
vector<int> v = {1, 3, 6, 7, 8}
//function(L, R, arg),作用於 [L, R) ; L, R可以用v.begin()和v.end()
• min_element(L, R):陣列最小元素的 iterator  //O(n)
• max_element(L, R):陣列最大元素的 iterator  //O(n)
//以下使用二分搜
• binary_search(L, R, val): 
要先排序,回傳是否有找到 val  //O(log n)    
• lower_bound(L, R, val):
要先排序,陣列數值大於等於 val 的 iterator  //O(log n)
• upper_bound(L, R, val):
要先排序,陣列數值大於 val 的 iterator  //O(log n)
```
$e.g.$
```cpp=+
vector<int> v1={1, 3, 5, 8};
auto it = lower_bound(v.begin(), v.end(), 3);  //使用auto讓it等於搜索出來的迭代器
*it = 7;  //透過指標，直接指向it在記憶體中的位置，就可以改變或使用了
```
### $sort$
可以將STL排序，大部分用在vector
$e.g.$
```cpp=
sort(L, R) 以及 vector<int> v={4, 8, 7, 6, 3} 當例子。
• sort(L, R); //將陣列的 [L, R) 由小到大排序
• sort(v.begin(), v.end()); //[3, 4, 6, 7, 8]
• sort(v.begin(), v.begin()+3); //[4, 7, 8, 6, 3]
• sort(v.begin()+3, v.begin());  //RE
```
* `sort`的`*arg` 
`*arg`是排序方式，傳入一個布林函式，作為比較函式，做到自定義排序，會以這個形式呈現`sort(v.begin(), v.end(), *arg);`
$e.g.$
```cpp=+
//由大到小排序
bool mycmp(int infront, int back){
    return infront > back;
}
sort(v.begin(), v.end(), greater<int>()); //將陣列從大到小排序
sort(v.begin(), v.end(), mycmp); //使用自訂義排序
sort(v.rbegin(), v.rend()); //special
```
`greater<int>`是讓`sort`由大到小排列，可以學看看

* **以下很重要**
`mycmp`回傳的布林值是兩個相鄰的資料的關係，讓`sort`函式內部知道要怎麼排序，`infront>back`是告訴`sort`函式，**前面的數字要大於後面的數字**，於是`sort`函式就會由大到小排序了


### 包裝容器`pair`
![image](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/pair.png?raw=true)
*用法*
```cpp
pair<type1, type2> name
• type:元素型別(可以不同)
------------------------
• name.first:取得前面的元素
• name.second:取得後面的元素
```
$e.g.$
```cpp=+
pair<int, int> p1; // p1 = { , } 空 的 pair
pair<int, int> p2(4, 8); // p2 = {4, 8}
pair<int, int> p3 = {4, 8};
p3.first; // 4
p3.second; // 8

pair<pair<int, int>, int> p4 = {{4, 8}, 7};
p4.first; // {4, 8}
p4.first.first; // 4
p4.second; // 7
vector<pair<int, int>> v1(100, {1, 2});
cin>>v1[0].first;
vector<pair<int, int>> v2;
v2.push_back(make_pair(1, 2));
```
如果要對`vector<pair<int, int>>`進行排序，會先排序前項，如果前項相同再排列後項，如果要進行其他方式，那可能要使用自定義排序
### 線性容器
* 線性容器通用函式
1. `name.size()`
2. `name.empty()`
**不支援所有迭代器{`name.begin(), name.end()`}**
#### ***queue&stack***

![](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/q.png?raw=true)
**可以吧`queue`想像成一個水管，只能右進左出，只能訪問最前面跟最後面
而`stack`則是像一個桶子，後進先出，只能訪問最上面**
* ***函式***
```cpp=
//以下都合法
stack<int> s;
• s.push(val):將 val 加進 stack 的最上面,O(1)
• s.pop():將 stack 最上面的元素刪除,O(1)
• s.top():取得 stack 最上面的元素,O(1)
queue<int> q;
• q.push(val):將 val 加進 queue 的最後面,O(1)
• q.pop():將 queue 最前面的元素刪除,O(1)
• q.front():取得 queue 最前面的元素,O(1)
• q.back():取得 queue 最後面的元素,O(1)
```
#### ***priority_queue***
![](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/pq.png?raw=true)
將最重要的元素放在最頂端，只能訪問最前項，預設是將最大當作最重要

$e.g.$
```cpp=+
priority_queue<int> pq
• pq.push(val):將 val 加進 pq 裡面,O(log n)
• pq.pop():將 pq 最上面的元素刪除,O(log n)
• pq.top():取得 pq 最上面的元素,O(1)
```
* **註 : `pq`的時間常數比`set`還要低**
* [進階語法](https://yuihuang.com/cpp-stl-priority-queue/)
### 樹狀容器
#### ***set&multiset***
***set***主要的功能有**排序**與**去重**：
把資料送入***set***時，若***set***內已有該數值就會被刪除，若沒有就會被排序
**即所有相同資料在*set*中只能存在一個**
```cpp=
set<int> s;
int val=1;
s.empty();  //回傳布林值：s是否為空
s.size();  //回傳整數：s的大小
s.begin();  //回傳迭代器：s最前面的位置
s.end();  //回傳迭代器：s最後位置的下一個位置
s.insert(val);  //插入數值val到s中 回傳pair<iterator,bool>插入位置和是否成功 O(log n)
s.count(val);  //查看s中val數值的數量（set中必為1或0）O(log n)
s.erase(val);  //刪除s中的val元素 回傳iterator刪除元素的下一個位置O(log n)
s.erase(iterator);  //刪除iterator迭代器位置的數值 回傳bool是否刪除成功 O(1)
s.find(val);  //查找val數值的位置 回傳iterator O(logn)
```
***multiset***是允許多個相同數值存在的***set***
使用方法和***set***大致相同
```cpp=
multiset<int> ms;
int val=1;
ms.empty();  //回傳布林值：s是否為空
ms.size();  //回傳整數：s的大小
ms.begin();  //回傳迭代器：s最前面的位置
ms.end();  //回傳迭代器：s最後位置的下一個位置
ms.insert(val);  //插入數值val到s中 回傳iterator插入位置（必定插入成功） O(log n)
ms.count(val);  //查看s中val數值的數量 O(K+log n)
ms.erase(val);  //刪除s中的所有val元素 回傳整數被刪除的元素數量 O(K+log n)
ms.erase(iterator); //刪除iterator迭代器位置的數值 回傳iterator刪除元素的下一個位置 O(1)
ms.find(val);  //查找val數值的位置 若有多個數值 回傳最前面的那個 O(logn)
```


#### ***map***
![](https://github.com/FliGhtzzz/ccirc/blob/main/pictures/mapit.png?raw=true)
用於儲存離散化的資料，像是一個陣列，但陣列索引可能是任何資料型態，特別是當需要開一個很大的陣列的時候可以考慮使用它，通常都以`map<int, int>`來做
```cpp=
//map<key_type,value_type> mp;  這是宣告方式
map<char,int> mp;  //預設每一項都是0
mp['A'];  //在mp中宣告一個引索為A的數
mp['A']=1;
mp['A']+=5;
cout<<mp['A'];  //6
mp.erase(iterator);
mp.erase(key);  //刪除該索引的
mp.find(key);  //回傳該索引的位置 找不到則回傳mp.end()
```
其實還有一些很厲害的$STL$ ，但~~我很懶~~，就放在這邊等你們探索吧
* deque
* unordered map
* bitset 
* gp_hash_table
## 二分搜尋

* **非常非常非常重要的演算法**
* **非常非常非常重要的演算法**
* **非常非常非常重要的演算法**

很重要，所以要說三遍
:::info
可以在極短的時間內找到需要的值
但只能在**排序好**的資料中使用
:::


**傳統的二分搜過程是：**
>1. 選擇資料中的中位數
>2. 若所要的資料大於中位數則丟棄所有小於較小的資料
>3. 再對大於的資料中繼續找中位數並丟棄不需要的資料
>4. 直到找到所需的資料

由於二分搜不需要檢查每個資料，只需要折半再折半，因此對比線性搜尋的$O(N)$時間，時間複雜度為$O(\log N)$的二分搜非常<span class=blackout>  　**星爆**  　</span>快


---

### 傳統二分搜範例寫法
要注意維護`左閉右開`或`左開右閉`
* **遞迴版本**
```cpp=
int a[10] = {1, 3, 4, 5, 6, 7, 8, 10, 11, 12};  //已排序好的陣列
int binerySearch(int l, int r, int val) {       //左;右;要尋找的值
    if(l+1 == r) return l;                      //找到剩最後一項,回傳答案
    int mid = (l + r)/2;
    if(a[mid] > val) {                          //小於找左邊
        return binerySearch(l, mid, val);
    }
    else {                                      //大於找右邊
        return binerySearch(mid, r, val);
    }
}
```

* **迴圈版本**

```cpp=
int a[10]={1, 3, 4, 5, 6, 7, 8, 10, 11, 12};  //要搜尋的陣列
int ans, val=5, l=0, r=9;  //所求在陣列的值，所求，左邊界，右邊界
while(true){
    if(l+1 == r){  //刪到只剩下一個值（即答案）
        ans=l;
        break;
    }
    int mid = (l + r)/2;
    if(a[mid] > val) r=mid;//小於所求找左邊
    else l = mid;          //大於所求找右邊
}
```
重點是先找到中間點，如果要找的值大於中點，則找中點的右邊，否則則找中點的左邊，若區間只有一個值，那就是答案了

##


**但是要左邊右邊找找去好麻煩
還要管左閉右開之類的問題**

所以這邊提供一個比較好的二分搜寫法||我平常也用這個||，我稱為跳跳二分搜
##
### 跳跳二分搜
```cpp=
int a[10]={1, 3, 4, 5, 6, 7, 8, 10, 11, 12};
int ans=0;
int val=5;
for(int j=10;j>0;j>>=1){
    while(ans+j<10&&a[ans+j]<=val){
        ans+=j;
    }
}
```
跟上面的傳統二分搜比起來簡單很多吧!只需要判斷能不能往前跳，如果不能要跳小格一點，等到一次跳的距離變成0格，答案就出來了，時間複雜度也一樣是$O(logN)$ **可是二分搜只能在排序好的陣列中使用欸!** 要怎麼做出排序好的陣列？

---

## 排序

排序演算法有很多
e.g.`泡沫排序`,`選擇排序`,`快速排序`,`合併排序`,`希爾排序`...非常非常多種，最常使用的是快速排序和合併排序，因為時間複雜度低，只需要$O(NlogN)$，但原理不好理解，如果之後有時間再解釋
* 在$STL$的部分就有說到$sort$了，這裡就不再重複

