## C++
https://cp.wiwiho.me/lessons/
https://oi-wiki.org/
https://www.techiedelight.com/zh-tw/determine-if-a-string-is-numeric-in-cpp/
https://github.com/brianbbsu/8BQube
### 加速
```cpp=
ios_base::sync_with_stdio(0);
cin.tie(0);
```
### vector
```cpp=
#include <vector>
vector<int> v(10, 0); // 宣告一個大小為 10, 全為 0 的 vector
v.push_back(1); // 在 vector 最後面新增一個元素 1
v.pop_back(); // 一次只能從尾端移除一個元素,不能指定移除的數量
v.size(); // 取得目前 vector 裡的元素個數
v.capaciy(); // 取得目前 vector 裡的預先配置的空間大小
v.reserve(5); // 預留 5 個空間
v.shrink_to_fit(); // 釋放(free)那些尚未使用的空間
v.resize(5); // resize 變大時會把多的元素補 0
v.resize(5, 10); // 順便指定初始值為 10
reverse(v.begin(), v.end()); // 將 vector 顛倒
v.erase(v.begin() + 2); // 刪除第三個元素
v.erase(v.begin(), v.begin() + 3); // 刪除前 3 個元素
```
#### sort()
```cpp=
#include <algorithm>
// 自訂排序
bool cmp(const int a, const int b) {
return a < b; // 由大到小排序,a 是第二個元素, b 是第一個元素。
}
vector<int> v = {2, 4, 5, 3, 1, 6, 7};
sort(v.begin(), v.end()); // 從小到大排序
sort(v.begin(), v.end(), cmp);
```
### stack
```cpp=
#include <stack>
stack<int> s;
s.push(1);
// 用 vector 建構 stack
vector<int> v = {1, 2, 3};
stack<int, vector<int>> s(v);
// 也可以用 deque
deque<int> dq = {1, 2, 3};
stack<int, deque<int>> s(dq);
s.pop(); // 從頂端移除元素
s.empty(); // 判斷是否為空
s.size(); // 判斷元素數量
s.top(); // 查看頂端元素
```
### deque
deque 是一個雙向佇列(double-ended queue),在頭尾兩端插入及刪除十分快速
```cpp=
#include <deque>
deque<int> dq = {1, 2, 3, 4};
dq.push_back(5); // [1, 2, 3, 4, 5]
dq.pop_front(); // [2, 3, 4, 5]
dq.push_front(0); // [0, 2, 3, 4, 5]
dq.pop_back(); // [0, 2, 3, 4]
```
### queue
```cpp=
#include <queue>
queue<int> q;
q.push(1); // 在尾部加入元素
q.pop(); // 取出頭部元素,會將元素從 queue 中移除
q.front(); // 取得頭部元素,不會將元素移除
q.back(); // 取得尾巴元素
q.size();
q.empty();
q.clear(); // 清空 queue;
```
### map
```cpp=
#include <map>
map<int, string> mp = {
{0, "Tom"}
};
mp[1] = "Alen";
mp.insert(pair<int, string>(2, "John"));
// 遍歷 map 會發現 key 由小到大排序
for (auto m : mp) {
cout << mp.first << " -> " << mp.second;
}
mp.erase(1); // 刪除元素
// 判斷元素是否存在
// count() 存在回傳 1, 不存在回傳 0
int cnt = mp.count(1);
// find() 找到元素會回傳 iterator, 否則回傳 end() iterator
auto it = mp.find(1);
```
### set
set 容器裡面的元素是**唯一的**,具有**不重複**的特性,而且是**有排序的**容器, set 容器裡面**元素的值是不可修改**,但 set 容器**可以插入或刪除元素**
```cpp=
#include <set>
// 初始化
set<int> st{1, 2, 3, 4, 5};
// 用 array 初始化
int arr[] = {1, 2, 3, 4, 5};
set<int> st(arr, arr + 5);
st.insert(6); // 加入元素
st.erase(5); // 刪除指定元素
st.clear(); // 清空 set
st.empty();
// 只能迴圈遍歷元素
for (auto it=st.begin(); it!=st.end(); it++) cout << *it << " ";
// 判斷元素是否存在
// count() 存在回傳 1, 不存在回傳 0
st.count(4);
// find() 找到元素會回傳 iterator, 否則回傳 end() iterator
auto search = st.find(4);
if (search != st.end()) cout << "found " << *search;
```
### String
### pair
```cpp=
#include <pair>
pair<int, string> p;
pair<int, string> p(1, "Tom");
```
### find()
```cpp=
#include <algorithm>
int arr[] = {1, 2, 3, 4, 5, 6};
*p = find(arr, arr+5, 3); // find 3
if (p == arr + 5) cout << "not found\n";
else cout << "found: " << *p << endl;
vector<int> v = {1, 2, 3, 4, 5, 6};
vector<int>::iterator it = find(v.begin(), v.end(), 3);
if (it != v.end())
cout << "found " << *it << ", index: " << distance(v.begin(), it) << endl;
```
#### find_if()
```cpp=
#include <algorithm>
template<typename T>
bool equal_5(T val) {
return val == 5;
}
vector<int> v = {1, 2, 3, 4, 5, 6};
vector<int>::iterator it = find_if(v.begin(), v.end(), equal_5<int>);
if (it != v.end()) cout << "found\n";
```
### 判斷字母數字
```cpp=
#include <ctype.h>
if (isalpha('a')) cout << "alpha\n";
```
## Java
## 圖的儲存
### 鄰接矩陣
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int n, m;
vector<bool> vis;
vector<vector<bool>> adj;
bool find_edge(int u, int v) { return adj[u][v]; }
void dfs(int u) {
if (vis[u]) return;
vis[u] = true;
for (int v = 1; v <= n; ++v) {
if (adj[u][v]) {
dfs(v);
}
}
}
int main() {
cin >> n >> m;
vis.resize(n + 1, false);
adj.resize(n + 1, vector<bool>(n + 1, false));
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
adj[u][v] = true;
}
return 0;
}
```
### 鄰接表
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int n, m;
vector<bool> vis;
vector<vector<int>> adj; // adj[u] 儲存與 u 相鄰的節點
bool find_edge(int u, int v) {
for (int i = 0; i < adj[u].size(); ++i)
if (adj[u][i] == v)
return true;
return false;
}
void dfs(int u) {
if (vis[u]) return;
vis[u] = true;
for (int i = 0; i < adj[u].size(); ++i) dfs(adj[u][i]);
}
int main() {
cin >> n >> m;
vis.resize(n + 1, false);
adj.resize(n + 1);
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
}
return 0;
}
```
## 質數
### 質數檢測
```cpp=
bool millerRabin(int n) {
if (n < 3 || n % 2 == 0) return n == 2;
int u = n - 1, t = 0;
while (u % 2 == 0) u /= 2, ++t;
// test_time 為測試次數,建議設為不小於 8
// 的整數以確保正確率,但也不宜過大,否則會影響效率
for (int i = 0; i < test_time; ++i) {
int a = rand() % (n - 2) + 2, v = quickPow(a, u, n);
if (v == 1) continue;
int s;
for (s = 0; s < t; ++s) {
if (v == n - 1) break; // 得到平凡平方根 n-1,通過此輪測試
v = (long long)v * v % n;
}
if (s == t) return 0;
}
return 1;
}
```
### 質數篩選
```cpp=
void init(int n) {
for (int i = 2; i <= n; ++i) {
if (!vis[i]) pri[cnt++] = i;
for (int j = 0; j < cnt; ++j) {
if (1ll * i * pri[j] > n) break;
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) {
// i % pri[j] == 0,換言之,i 之前被 pri[j] 篩過了
// 由於 pri 裡面質數是從小到大的,所以 i乘上其他的質數的結果一定會被
// pri[j]的倍數篩掉,就不需要在這裡先篩一次,所以這裡直接 break
break;
}
}
}
}
```
## MST
### Prim's Algorithm
- 以 **vertex** 為主要考量
- 適合用在稠密圖
- edge 很多時,較 Kruskal 有效率
- 演算法 (N = number of vetex)
- 建一個存 edges 的 priority queue,權重小的優先
- 見一個 bool arr,存哪些點已經加入樹中
- 從任意的點當作起點,不斷執行下列演算法,直到產生 N-1 個 edges
- 若選中的節點已在樹中,跳過本次
- 將目前節點標示為已加入
- PQ 加入和目前節點相鄰的所有 edges
- 從 PQ 中拿出一個 edge,並移動到下個節點
```java
class MST {
static class Edge {
public int v, t, w;
public Edge(int _v, int _t, int _w) {
v = _v;
t = _t;
w = _w;
}
}
static ArrayList<Edge> prim(int[][] g) {
int n = g.length, m = 0;
PriorityQueue<Edge> edges = new PriorityQueue<>(new Comparator<Edge>() {
@Override
public int compare(MST.Edge e1, MST.Edge e2) {
return e1.w - e2.w;
}
});
int v = 0;
boolean[] contains = new boolean[n];
ArrayList<Edge> mst = new ArrayList<>();
while (m < n) {
if (contains[v])
continue;
contains[v] = true;
for (int t = 0; t < n; t++) {
if (g[v][t] != 0) {
if (!contains[t])
edges.add(new Edge(v, t, g[v][t]));
}
}
Edge next = edges.poll();
mst.add(next);
v = next.t;
m++;
}
return mst;
}
}
```
### Kruskal's algorithm
- 以 edge 為主的演算法
- 適合稀疏圖 (點很多,但是邊不多)
- 因為要先求出所有邊並排序,圖有很多 edge 時很不適合
- 演算法
- 建一個 array 存所有 edges,並 sort
- 從第一個 edge 開始向後看,執行以下演算法,直到產生 N-1 個 edge
- 拿出目前最小的 edge,並檢查加入此 edge 是否形成 cycle,若會 cycle 則跳過
- 若兩個點都已經在 MST 中,會產生 cycle
- 將 edge 加入 MST,並進行下一次演算法
```java
import java.util.*;
class MST {
static class Edge {
public int v, t, w;
public Edge(int _v, int _t, int _w) {
v = _v;
t = _t;
w = _w;
}
}
static ArrayList<Edge> kruskal(int[][] g) {
int n = g.length, m = 0;
ArrayList<Edge> edges = new ArrayList<>();
for (int i = 0; i < n; i++) {
// init j=i+1 避免重複的 edge
for (int j = i + 1; j < n; j++) {
if (g[i][j] == 0)
continue;
edges.add(new Edge(i, j, g[i][j]));
}
}
edges.sort(new Comparator<Edge>() {
@Override
public int compare(MST.Edge e1, MST.Edge e2) {
return e1.w - e2.w;
}
});
boolean[] contains = new boolean[n];
ArrayList<Edge> mst = new ArrayList<>();
for (int i = 0; i < edges.size() && m < n; i++) {
Edge edge = edges.get(i);
if (contains[edge.v] && contains[edge.t])
continue;
mst.add(edge);
m++;
}
return mst;
}
}
```
## Shortest Path
### All Pair
Floyd:適用於任何圖,但必須存在最短路徑。
```cpp=
for (k = 1; k <= n; k++) {
for (x = 1; x <= n; x++) {
for (y = 1; y <= n; y++) {
f[x][y] = min(f[x][y], f[x][k]+f[k][y])
}
}
}
```
Bellman–Ford:可求負權重的圖,並判斷是否存在最短路徑。
鬆弛操作(經過新的中繼點)對應公式:$dis(v) = \min(dis(v), dis(u) + w(u, v))$
```cpp=
struct edge {
int v, w;
};
vector<edge> e[maxn];
int dis[maxn];
const int inf = 0x3f3f3f3f;
bool bellmanford(int n, int s) {
memset(dis, 63, sizeof(dis));
dis[s] = 0;
bool flag; // 判斷一輪循環過程中是否發生鬆弛操作
for (int i = 1; i <= n; i++) {
flag = false;
for (int u = 1; u <= n; u++) {
if (dis[u] == inf) continue;
// 無窮大與常數加減仍為無窮大
// 因此最短路長度為 inf 的點引出的邊不可能發生鬆弛操作
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
flag = true;
}
}
}
// 沒有可以鬆弛的邊時就停止
if (!flag) break;
}
// 第 n 輪循環仍可鬆弛時說明 s 點可以抵達負環
return flag;
}
```
### Single-Source
Dijkstra:求**非負權重**單源最短路徑。
```cpp=
// 使用 priority_queue 實現
struct edge {
int v, w;
};
struct node {
int dis, u;
bool operator>(const node& a) const { return dis > a.dis; }
};
vector<edge> e[maxn];
int dis[maxn], vis[maxn];
priority_queue<node, vector<node>, greater<node> > q;
void dijkstra(int n, int s) {
memset(dis, 63, sizeof(dis));
dis[s] = 0;
q.push({0, s});
while (!q.empty()) {
int u = q.top().u;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
```