[TOC]
---
## 課程補充資料
#### 2-1 函式
> 將陣列傳到函式內
```cpp!
int total(int arr[], int n) {
int summ=0;
for(int i=0; i<n; i++) {
summ += arr[i];
}
return summ;
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
cout<< total(a, 5);
}
```
#### 2-2 遞迴函式
- [Recursion Visualizer](https://recursion.vercel.app/)
- [Hanoi Tower
](https://www.youtube.com/watch?v=8XQmuPKOgy8)
```cpp!
void Hanoi(char From, char Buff, char To, int disk) {
if(disk == 1) {
cout<< "move " << From << " -> " << To<< endl;
}
else {
Hanoi(From, To, Buff, disk - 1);
Hanoi(From, Buff, To, 1);
// cout<< "move " << From << " -> " << To<< endl;
Hanoi(Buff, From, To, disk - 1);
}
}
// Hanoi('A', 'B', 'C', 3);
```
#### 3-1 指標、參考
- 指標 (pointer)
```cpp!
int a;
int *ptr_a; // 宣告指標
ptr_a = &a; // & 變數 -> 取位址
*ptr_a = 5; // * 位址 -> 取變數(並修改)
// 對 *ptr_a 的修改,也會影響到變數 a
```
- 參考 (reference)
```cpp!
int a;
int &ref_a = a; // 宣告參考、連結到 a 變數
ref_a = 5; // 直接修改到 a 的值
```
#### 3-4 多維陣列
- 陣列 & 函式
```cpp!
void show_1D(int arr[], int n) {
for(int i=0; i<n; i++) cout<< arr[i]<< " ";
cout<< endl;
}
// arr[][100] <- 第二格必填、且不能是變數
void show_2D(int arr[][100], int m, int n) {
for(int i=0; i<m; i++) {
for(int j=0; j<n; j++) {
cout<< arr[i][j]<< " ";
}
cout<< endl;
}
}
void show_another_2D(int arr[][100], int m, int n) {
for(int i=0; i<m; i++) {
show_1D(arr[i], n); // 可以把 arr[i] 視為一個一維陣列
}
}
int main() {
int arr_1d[10] = {}, arr_2d[50][100] = {};
show_1D(arr_1d, 10);
show_2D(arr_2d, 50, 100);
}
```
- 無限制長度陣列 `vector`
::: spoiler 展開
```cpp=
#include <iostream>
#include <vector>
// 需要引入函式庫才能使用
using namespace std;
int main() {
vector<int> arr; // 宣告一維陣列,int 型態,"長度為 0"
/* 填入資料 */
arr.push_back(123); // 填入一筆資料 123
arr.push_back(456); // 在陣列最後填入新數字 456
// 目前狀態: arr[0]: 123, arr[1]: 456
for (int i=0; i<5; i++) {
int a; cin>> a;
arr.push_back(a); // 將 5 個數字輸入並儲存至 arr
}
/* 查看大小 */
cout<< arr.size()<< endl; // 目前 arr 長度為 7
/* 顯示全部資料 */
for(int i=0; i<arr.size(); i++) {
cout<< arr[i]<< " ";
} // cout 123 456 ...五個輸入的數字
}
```
> 常犯的錯誤
```cpp!=
int main() {
vector<int> arr;
cout<< arr[0]; // ERROR: 目前 arr 長度為 0,無法存取
}
```
> 特別的初始化方式
```cpp=
int main() {
vector<int> arr(10, 2); // 初始化長度為 10,資料為 2 的 int 陣列
// 使用陣列,宣告 10 個 vector,每個 vector 長度為 0
vector<int> arr_2d[10];
// 使用 vector 宣告 10 個 vector,每個 vector 長度為 0
vector< vector<int> > arr_2dd(10);
// 使用 vector 宣告 10 個 vector,每個 vector 長度為 20,並初始值為 5
// 這也可以拿來用在宣告二維陣列
vector< vector<int> > arr_2ddd(10, vector<int> (20, 5));
}
```
> 再更延伸一些,傳遞函式時,vector 與傳統陣列的差異
```cpp=
void reset_arr_1(int arr[], int n) {
for(int i=0; i<n; i++) {
arr[i] = 0;
}
}
void reset_vec_1(vector<int> arr) { // 不用傳遞 n,因為 arr.size() 就是長度
for(int i=0; i<arr.size(); i++) {
arr[i] = 0;
}
}
void reset_vec_2(vector<int> &arr) { // 從 reset_vec_1 微調輸入部分
for(int i=0; i<arr.size(); i++) {
arr[i] = 0;
}
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(5, 123); // 長度為 5 資料為 123
reset_arr_1(arr, 5); // 可以清空 arr
reset_vec_1(vec); // 無法清空 vec
// 所以要用第二個版本,有點類似傳遞指標的概念,使用這個叫 reference 的東西
// void reset_vec_2(vector<int> &arr)
reset_vec_2(vec); // 可以清空 vec
}
```
:::
#### 3-3 Linked List
- [Slides](https://slides.com/oscarsslides/linked-list)
#### 3-5 字串
[ASCII: 文字編碼](https://upload.wikimedia.org/wikipedia/commons/1/1b/ASCII-Table-wide.svg)
- recap:
```c++!
char c = 'a';
if(c == 'a') // true
if(c == "a") // Compiled Error
if(c < 'c') // true
```
```cpp!
char c; cin>> c;
if('a' <= c && c <= 'z') {} // 抓小寫英文
if('0' <= c && c <= '9') {} // 抓數字
sum += c - '0' // 計算數字
```
- c++ string 讀整行 (連空格都吃)
```c++!
string s;
getline(cin, s);
```
#### 4-1 堆疊
- [後序運算](https://ithelp.ithome.com.tw/articles/10244316)
#### 5-1 演算法分析 - 時間複雜度
- [不同排序法的效能差異](https://www.youtube.com/watch?v=BeoCbJPuvSE)
- [Desmos - Time Complexity](https://www.desmos.com/calculator/zpzzboy6sz?lang=zh-TW)
#### 5-2 排序
- [視覺化排序流程](https://visualgo.net/zh/sorting)
#### 5-4 窮舉
稍微微調了一下課本的 permutation
:::spoiler permutation
```cpp=
#include <iostream>
using namespace std;
// 輸出陣列內容
void print(int arr[], int n) {
for(int i=0; i<n; i++) {
cout<< arr[i]<< " ";
}
cout<< endl;
}
// 檢查 target 有沒有在 arr[0~n-1] 出現
bool exists(int arr[], int n, int target) {
for(int i=0; i<n; i++) {
if(arr[i] == target) {
return true;
}
}
return false;
}
void permutation(int arr[], int n, int place) {
if(n == place) {
print(arr, n);
return ; // 離開函式
}
// else
for(int i=0; i<n; i++) {
// 試圖在 arr[ place ] 填入 i
if(exists(arr, place, i) == false) {
arr[place] = i;
permutation(arr, n, place + 1); // 繼續往後填寫 (place+1)
}
}
}
int main() {
int arr[5] = {};
permutation(arr, 5, 0); // 陣列, 長度, 從哪開始填寫
}
```
:::
如果還是理解困難的話,可以看看程式是如何變這樣的
:::spoiler 單純的 combination
```cpp=
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
for(int k=0; k<n; k++){
cout<< i<< " "<< j<< " "<< k<< endl;
}
}
}
```
:::
- 變陣列版本
:::spoiler combination + array
```cpp=
void print(int arr[], int n) {
for(int i=0; i<n; i++) {
cout<< arr[i]<< " ";
}
cout<< endl;
}
int main() {
int n=3, arr[3] = {};
int to_fill; // 填在哪一格
for(int i=0; i<n; i++) {
to_fill = 0;
arr[to_fill] = i;
for(int j=0; j<n; j++) {
to_fill = 1;
arr[to_fill] = j;
for(int k=0; k<n; k++){
to_fill = 2;
arr[to_fill] = k;
print(arr, n);
}
}
}
}
```
:::
- 再將重複的層層迴圈,塞到遞迴中
:::spoiler combination + array + recursion
```cpp=
void print(int arr[], int n) {
for(int i=0; i<n; i++) {
cout<< arr[i]<< " ";
}
cout<< endl;
}
void combination(int arr[], int n, int to_fill) {
if(n == to_fill) {
print(arr, n);
return ;
}
// else
for(int i=0; i<n; i++) {
arr[to_fill] = i;
combination(arr, n, to_fill + 1);
}
}
int main() {
int n=3, arr[3] = {};
combination(arr, n, 0);
}
```
:::
這樣應該就可以理解了 (?
premutation 就是不能重複的 combination 而已
#### 6-3 八皇后
> 從 permuatation 再往後延伸,斜線不衝突 `conflict`
:::spoiler 八皇后
```cpp=
#include <iostream>
using namespace std;
int max_score = 0;
void print(int arr[], int n) {
for(int i=0; i<n; i++) {
cout<< arr[i]<< " ";
}
cout<< endl;
}
// 檢查 target 有沒有在 arr[0~n-1] 出現
bool exists(int arr[], int n, int target) {
for(int i=0; i<n; i++) {
if(arr[i] == target) {
return true;
}
}
return false;
}
// 斜線衝突
bool conflict(int arr[], int to_fill, int i) {
for(int j=0; j<to_fill; j++) {
if( abs(to_fill - j) == abs(i - arr[j]) ) {
return true;
}
}
return false;
}
void combination(int arr[], int n, int to_fill) {
if(n == to_fill) {
print(arr, n);
return ;
}
// else
for(int i=0; i<n; i++) {
// to_fill == 2
if(exists(arr, to_fill, i)) { // [0, 1, ?]
continue;
}
if(conflict(arr, to_fill, i)) {
continue;
}
arr[to_fill] = i;
combination(arr, n, to_fill + 1);
}
}
int main() {
int n=8, arr[8] = {};
combination(arr, n, 0);
}
```
:::
#### 6-4 動態規劃
[簡報2
](https://slides.com/oscarsslides/the-coin-questions)
#### 4-3 & 6-5 樹狀結構

`from https://app.smartdraw.com/`
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| | x | - | + | + | 3 | x | 1 | 3 | 4 | | | 2 | 3 | | |
```
string tree = " x-++3x134 23 "; // length: 16
index: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
value: x - + + 3 x 1 3 4 2 3
```
:::spoiler 規律

:::
:::spoiler 二元樹視覺化函式 (幫助 DEBUG)
```cpp=
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
void show(int tree[], int maxLevel) {
int nodeWidth = 2; // 節點寬度
int index = 1; // 1-indexed
for (int level = 1; level <= maxLevel; ++level) {
int numNodes = pow(2, level - 1); // 該層節點數
int spaceBetween = pow(2, maxLevel - level + 1); // 節點間距
int initialSpace = spaceBetween / 2;
// 前置空格
cout << string(initialSpace * nodeWidth, ' ');
for (int i = 0; i < numNodes; ++i) {
cout << setw(nodeWidth) << tree[index++];
// 節點之間的空格
cout << string((spaceBetween * nodeWidth) - nodeWidth, ' ');
}
cout << endl << endl; // 每層空一行
}
}
```
用法
```cpp=
int tree[32];
show(tree, 4); // 顯示 4 層
```
:::
:::spoiler 二元樹走訪
[後序運算](https://ithelp.ithome.com.tw/articles/10244316)
```
pre-order : x-+343 +x231
post-order: 34+3- 23x1+x
inorder : 3+4-3x 2x3+1
```
:::
#### Heap
[視覺化](https://visualgo.net/en/heap)
:::spoiler Heap 範例 Code
https://visualgo.net/en/heap
```cpp=
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
void show(int tree[], int maxLevel) {
int nodeWidth = 2; // 節點寬度
int index = 1; // 1-indexed
for (int level = 1; level <= maxLevel; ++level) {
int numNodes = pow(2, level - 1); // 該層節點數
int spaceBetween = pow(2, maxLevel - level + 1); // 節點間距
int initialSpace = spaceBetween / 2;
// 前置空格
cout << string(initialSpace * nodeWidth, ' ');
for (int i = 0; i < numNodes; ++i) {
cout << setw(nodeWidth) << tree[index++];
// 節點之間的空格
cout << string((spaceBetween * nodeWidth) - nodeWidth, ' ');
}
cout << endl << endl; // 每層空一行
}
}
int heap_top(int heap[]) {
return heap[1];
}
void heap_push(int heap[], int n, int value) {
// 1 ... n
int pos = n + 1;
heap[pos] = value;
while(pos != 0 && heap[pos] < heap[pos/2]) {
swap(heap[pos], heap[pos/2]);
pos = pos / 2;
}
}
void heap_pop(int heap[], int n) {
// 1 ... n
heap[1] = heap[n];
n -= 1;
int pos = 1;
while(true) {
// show(heap, 5);
int min_pos = pos;
int left = pos*2, right=pos*2+1;
if(left < n && heap[left] < heap[min_pos]) {
min_pos = left;
}
if(right < n && heap[right] < heap[min_pos]) {
min_pos = right;
}
if(min_pos == pos) {
break; // pos 最小 or left, right 超出範圍
}
else {
swap(heap[pos], heap[min_pos]);
pos = min_pos;
}
}
}
int main() {
// index 0 不使用,-1 表示空節點
int tree[64] = {};
for(int i=1; i<=15; i++) {
tree[i] = i;
}
show(tree, 4);
cout<< "---------------------------------------------------"<< endl;
heap_push(tree, 15, 0);
show(tree, 5);
cout<< "---------------------------------------------------"<< endl;
heap_pop(tree, 16);
show(tree, 5);
return 0;
}
```
:::
#### 4-4 圖形結構
[Graph Visualizer](https://graphonline.top/)
[BFS / DFS](https://seanperfecto.github.io/BFS-DFS-Pathfinder/)
<div style="height: 500px;"></div>