【C++】競程筆記(algorithm標頭檔)
【C++】競程筆記(algorithm標頭檔)
分析效率
1 | void bubble_sort(int arr[], int n) { // arr: 要排序的陣列,n: 陣列大小 |
計算步驟數,來分析程式效率。如上為氣泡排序法,執行步驟為:n+5*n(n-1)/2 = 5/2*n^2 - 3/2 *n。
時間複雜度(Time Complexity)
用 Big-O 表示。
只會表示函數成長趨勢最大的那一個,並且捨棄所有係數跟成長趨勢緩慢的項數。
泡沫排序法的 Big-O 如:O(n^2)。
快速計算時間複雜度
如泡沫排序法就是迴圈執行 n 次,判斷式檢查 n 次。
所以就是 O(n^2)。
空間複雜度(Space Complexity)
記憶體空間的使用程度。假設宣告一個長度為 n 的陣列,則其空間複雜度至少為 O(n)。
質數判斷
1 | bool is_prime(int n) { |
n == 1:O(1)
for 迴圈:因為 i 平方 <= n,所以等價於 i <= sqrt(n),就是兩邊開平方根。-> O(根號n)
n%i == 0:O(1)
return true:O(1)
所以總共時間複雜度是:O(1) + O(根號n)*O(1) = O(根號n)
基礎排序法
- 選擇排序法(Selection Sort)
1 | void selectionSort(std::vector<int>& arr) { |
時間複雜度:O(n²)
空間複雜度:O(1)
swap(a, b):交換 a、b 兩數值。
原理:剛開始先選擇最小索引元素當作最小值下去比較,那這個由我們自己定義的「最小值」,就要出發去找序列裡面真正的最小值,然後跟他做交換。之後這個最小值就不動,接下來索引 1 開始尋找倒數二小值,這樣一直重複下去。
- 插入排序法(Insertion Sort)
1 | void insertionSort(std::vector<int>& arr) { |
時間複雜度:平均情況和最壞情況下為 O(n²);最佳情況(即陣列已經排序)下,時間複雜度為 O(n)。
空間複雜度:O(1),因為該算法只使用了常數級別的額外空間。
原理:從最小索引元素開始,遍歷比較每個元素,有元素比它小,就插入到它前面去,以此類推。
- 泡沫排序法(Bubble Sort)
1 | void bubbleSort(std::vector<int>& arr) { |
時間複雜度:O(n²)
空間複雜度:O(1)
原理:前後比對,互相交換,小的在前面,大的在後面。
計數排序法
1 | void counting_sort(int a[], int n) { |
C 為陣列中元素數量。
時間複雜度:O(n+C)
空間複雜度:O(C)
前綴和(Prefix-Sum)
假設一個序列 a[4] = {1,2,3,4},則它前綴和對應的序列為 P。
P[0] = a[0]
P[1] = a[0] + a[1]
P[2] = a[1] + a[2]
P[3] = a[2] + a[3]
所以 P[4] = {1,3,5,7}
我們可以得到以下這條式子:
a[n-1] = a[n-2] + a[n-1] 設 n 為陣列長度,且當索引等於 0 時,P[0] = a[0]。
以下是 Prefix-Sum 的圖解:

Image Source:https://rishabh1000khandelwal.medium.com/parallel-prefix-sum-scatter-and-gather-d6d7323c03a1
計數排序的原理其實就是基於 Prefix-Sum 而來的,所以以下是計數排序進階寫法:
1 | struct Data { |
algorithm 標頭檔
需引入標頭檔:
1 |
大部分 methods 遵循以下語法:
1 | algorithm_name(container.begin(), container.end(), ...); |
container 是一個容器物件。
begin() 和 end() 是容器的成員函數,回傳指向容器開始和結束的迭代器(iterator)。
sort()
對容器中的元素進行排序。
時間複雜度:O(nlogn)
語法:sort(container.begin(), container.end(), compare_function);
1 |
|
std::partial_sort:區間排序,前 n 個元素有序。
1 | // 示範, 可自行調整 |
std::stable_sort:穩定排序,保留相等元素的相對順序。
1 | std::stable_sort(vec.begin(), vec.end()); |
sort() 中的 compare_function
預設 sort() 都是升序排序(由小到大),若要降序排序(由大到小),則需要自訂函數:
1 | bool cmp(int x, int y) { |
然後就可以放在 sort() 最右邊,這樣就升序了:
1 | sort(container.begin(), container.end(), cmp); |
加入 struct 結構體寫法:
1 | struct Data { |
搜尋演算法
函數:find()
在容器中尋找與給定值相符的第一個元素。
語法:
1 | auto it = find(container.begin(), container.end(), value); |
若有找到,it 將指向匹配的元素;如果沒有找到,it 將等於 container.end()。
註:it 就是 iterator,迭代器。
1 |
|
因為 iterator 本身是一個記憶體空間的位址,所以加上 * Dereference 運算子,讓值顯示出來。
std::binary_search:對有序區間進行二分搜。
1 | std::sort(vec.begin(), vec.end()); // 二分搜前置條件:資料要先排序好 |
std::find_if:找出第一個符合特定條件的元素。
1 | auto it = std::find_if(vec.begin(), vec.end(), [](int x) { return x > 3; }); |
這條的意思是:在 vec 容器中尋找第一個大於 3 的元素。
後面 [](int x) { return x > 3; } 為 lambda 運算式。表接收一個參數 x,若 x > 3 則回傳 true,否則 false。
同樣的,若找不到 it 就 = vec.end()。
copy()
將一個範圍內的元素複製到另一個容器或陣列。
語法:
1 | copy(source_begin, source_end, destination_begin); |
1 |
|
輸出結果:
1 | 1 2 3 4 5 |
equal()
比較兩個容器或兩個範圍內的元素是否相等。
語法:
1 | bool result = equal(first1, last1, first2); |
1 |
|
輸出結果:
1 | Vectors are equal. |
reverse()
反轉區間內的元素順序。
語法:
1 | std::reverse(vec.begin(), vec.end()); |
fill()
將指定區間內的所有元素指定為某個值。
語法:
1 | std::fill(vec.begin(), vec.end(), 0); // 所有元素設為 0 |
replace()
將區間內的某個值替換為另一個值。
語法:
1 | std::replace(vec.begin(), vec.end(), 1, 99); // 將所有 1 替換為 99 |
swap()
兩互相交換元素。
語法:
1 | swap(a, b); // a, b 值會對調 |
min()、max()
回傳最小或最大的值。
語法:
1 | min(a, b); |
min_element(first, last)、max_element(first, last)
尋找容器或陣列中最小或最大的元素。
語法:
1 | auto min_it = std::min_element(vec.begin(), vec.end()); |
但要記得,如果要求值的話在前面加上 *,如:*min_it or *std::min_element(vec.begin(), vec.end());。
nth_element(first, nth_pos, last)
尋找容器中第 k 小的數字。也用於對指定範圍內的元素進行部分排序,但不會完全排序。
語法:
1 | nth_element(StartIterator, NthIterator, EndIterator); |
NthIterator:指向範圍內第 n 個位置的迭代器,要放正確元素的位置。
1 |
|
輸出結果:
1 | 第 5 小的元素是:3 |
平均時間複雜度為 O(n);最壞情況為 O(n^2),n 是範圍內的元素數量。
unique(first, last)
將容器中相鄰重複元素過濾到只剩單一個元素存在。
1 |
|
小結(總表)
| 函式 | 描述 | 語法 |
|---|---|---|
sort() | 對容器中的元素進行排序 | sort(container.begin(), container.end(), compare_function); |
partial_sort() | 只排序前 n 個元素 | std::partial_sort(vec.begin(), vec.begin() + n, vec.end()); |
stable_sort() | 穩定排序,保留相等元素的相對順序 | std::stable_sort(vec.begin(), vec.end()); |
find() | 搜尋第一個匹配的元素 | auto it = find(container.begin(), container.end(), value); |
binary_search() | 在已排序的容器中執行二分搜 | std::binary_search(vec.begin(), vec.end(), value); |
find_if() | 搜尋符合條件的第一個元素 | auto it = std::find_if(vec.begin(), vec.end(), condition); |
copy() | 複製一個範圍內的元素到另一個容器 | copy(source_begin, source_end, destination_begin); |
equal() | 比較兩個範圍內的元素是否相等 | bool result = equal(first1, last1, first2); |
reverse() | 反轉區間內的元素順序 | std::reverse(vec.begin(), vec.end()); |
fill() | 將區間內的所有元素設定為某個值 | std::fill(vec.begin(), vec.end(), value); |
replace() | 將區間內的某個值替換為另一個值 | std::replace(vec.begin(), vec.end(), old_value, new_value); |
swap() | 交換兩個變數的值 | std::swap(a, b); |
min() / max() | 回傳最小或最大的值 | min(a, b); 或 max(a, b); |
min_element() / max_element() | 找出最小或最大元素的迭代器 | auto min_it = std::min_element(vec.begin(), vec.end()); |
nth_element() | 找出第 k 小的元素,部分排序 | nth_element(vec.begin(), vec.begin() + k, vec.end()); |
unique() | 移除相鄰的重複元素 | auto it = unique(vec.begin(), vec.end()); vec.erase(it, vec.end()); |
numeric 標頭檔
要使用此標頭檔前需要引入標頭檔:
1 |
主要用於數值運算的標頭檔,會用就能夠少掉幾行程式碼的撰寫。
accumulate()
accumulate() 用於計算容器中所有元素的總和,原始作法是 for 迴圈遍歷。
語法:
1 | accumulate(Iter first, Iter last, T init); |
Iter first:迭代器起始點。
Iter last:迭代器終點。
T init:預設值。
1 |
|
輸出結果:
1 | Sum: 15 |
iota(first, last, val);
於給定的序列容器範圍中填入一段連續的數字。
簡單來說像是:
1 | for (int i = 0;i<n;i++){ |
只是簡化成一行:iota(a.begin(), a.end(), 0);
1 |
|
輸出結果:
1 | 1 2 3 4 5 |
gcd()
算最大公因數。
1 |
|
輸出結果:
1 | GCD: 6 |
lcd()
算最小公倍數。
最小公倍數公式:LCM(a, b) = |a * b| / GCD(a, b)(在不用函數的情況下)
1 |
|
輸出結果:
1 | LCM: 144 |




