1. Thuật toán không thay đổi chuỗi
Các thuật toán này không thay đổi các phần tử trong container mà chúng thao tác.
1.1. `find` và `find_if`
find(begin, end, value): Tìm phần tử đầu tiên có giá trị bằngvalue, trả về iterator đến phần tử đó (hoặcendnếu không tìm thấy).find_if(begin, end, predicate): Tìm phần tử đầu tiên thỏa mãn điều kiện của hàmpredicate.find_end(begin, end, sub_begin, sub_end): Tìm vị trí cuối cùng mà một dãy con xuất hiện trong dãy lớn hơn.
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 3, 5, 7, 9};
// Tìm phần tử có giá trị 5
auto it = std::find(numbers.begin(), numbers.end(), 5);
if (it != numbers.end()) {
std::cout << "Tim thay: " << *it << std::endl; // Xuất: 5
}
// Tìm phần tử đầu tiên lớn hơn 6
auto it2 = std::find_if(numbers.begin(), numbers.end(), [](int x) {
return x > 6;
});
std::cout << "Phan tu > 6 dau tien: " << *it2 << std::endl; // Xuất: 7
// Tìm dãy con {3, 5}
std::vector<int> sub = {3, 5};
auto it3 = std::find_end(numbers.begin(), numbers.end(), sub.begin(), sub.end());
if (it3 != numbers.end()) {
// it3 trỏ đến phần tử đầu tiên của dãy con cuối cùng xuất hiện.
// Để lấy vị trí bắt đầu, ta cần trừ đi độ dài dãy con - 1.
// Tuy nhiên, find_end trả về iterator đến phần tử ngay SAU phần tử cuối của dãy con.
// Để lấy vị trí bắt đầu, ta cần tính toán khác.
// Cách thông thường là lặp và so sánh.
// Ví dụ này đơn giản, nếu find_end trả về end thì ko có.
// Nếu không, ta có thể làm như sau:
// auto start_it = it3 - sub.size(); // Cần cẩn thận với điều này
// std::cout << "Dãy con bat dau tai vi tri: " << start_it - numbers.begin() << std::endl;
// Tuy nhiên, cách diễn giải chuẩn của find_end là nó trả về iterator đến PHẦN TỬ ĐẦU TIÊN SAU dãy con cuối cùng.
// Nếu nó KHÔNG phải là end, thì dãy con đã được tìm thấy.
// Để tìm vị trí bắt đầu, ta có thể tính ngược lại:
if (it3 != numbers.end()) { // Điều kiện này hơi thừa vì đã kiểm tra ở trên
auto start_of_subsequence = it3 - sub.size(); // Cần check cận biên
if (start_of_subsequence >= numbers.begin()) {
std::cout << "Dãy con bat dau tai vi tri: " << start_of_subsequence - numbers.begin() << std::endl; // Xuất: 1
}
}
} else {
std::cout << "Khong tim thay day con." << std::endl;
}
return 0;
}
1.2. `count` và `count_if`
count(begin, end, value): Đếm số lượng phần tử có giá trị bằngvalue.count_if(begin, end, predicate): Đếm số lượng phần tử thỏa mãn điều kiện của hàmpredicate.
#include <vector>
#include <iostream>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 2, 4, 2};
int count_of_2 = std::count(vec.begin(), vec.end(), 2); // Đếm số lần xuất hiện của 2, kết quả là 3
int even_count = std::count_if(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // Đếm số lượng số chẵn, kết quả là 4
std::cout << "So luong so 2: " << count_of_2 << std::endl;
std::cout << "So luong so chan: " << even_count << std::endl;
return 0;
}
1.3. `for_each`
Áp dụng một hàm lên từng phần tử trong phạm vi.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), [](int& x) {
x *= 2; // Nhân đôi giá trị của mỗi phần tử
});
// Bây giờ vec là {2, 4, 6, 8, 10}
for(int val : vec) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
1.4. `equal` và `mismatch`
equal(b1, e1, b2): So sánh hai phạm vi[b1, e1)và[b2, b2 + (e1 - b1))xem chúng có bằng nhau không.mismatch(b1, e1, b2): Trả về một cặp iterator, chỉ đến cặp phần tử không khớp đầu tiên trong hai phạm vi.
#include <vector>
#include <iostream>
#include <algorithm>
#include <iomanip> // Cho boolalpha
int main() {
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {1, 2, 4};
std::vector<int> c = {1, 2, 3, 4};
// So sánh 3 phần tử đầu của a và b
bool are_equal = std::equal(a.begin(), a.end(), b.begin());
std::cout << "a == b? " << std::boolalpha << are_equal << std::endl; // Xuất: false
// Tìm cặp phần tử không khớp đầu tiên giữa a và c
auto mis = std::mismatch(a.begin(), a.end(), c.begin());
if (mis.first != a.end()) {
std::cout << "Bat dau khong khop: " << *mis.first << " vs " << *mis.second << std::endl;
} else {
std::cout << "Hai pham vi dau tien giong nhau." << std::endl; // Xuất ra dòng này vì a và c khớp nhau 3 phần tử đầu
}
// Ví dụ với mismatch thực sự
std::vector<int> d = {1, 2, 5};
auto mis2 = std::mismatch(a.begin(), a.end(), d.begin());
if (mis2.first != a.end()) {
std::cout << "Bat dau khong khop: " << *mis2.first << " vs " << *mis2.second << std::endl; // Xuất: 3 vs 5
}
return 0;
}
1.5. `all_of`, `any_of`, `none_of`
Kiểm tra xem tất cả, có tồn tại hay không có phần tử nào trong phạm vi thỏa mãn điều kiện.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {2, 4, 6, 8};
bool all_even = std::all_of(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // true
bool any_odd = std::any_of(vec.begin(), vec.end(), [](int x) {
return x % 2 != 0;
}); // false
bool none_negative = std::none_of(vec.begin(), vec.end(), [](int x) {
return x < 0;
}); // true
std::cout << "Tat ca la so chan? " << std::boolalpha << all_even << std::endl;
std::cout << "Co so le nao khong? " << std::boolalpha << any_odd << std::endl;
std::cout << "Khong co so am nao? " << std::boolalpha << none_negative << std::endl;
return 0;
}
2. Thuật toán thay đổi chuỗi
Các thuật toán này sửa đổi các phần tử trong container mà chúng thao tác.
2.1. `copy` và `copy_if`
copy(begin, end, dest): Sao chép các phần tử từ[begin, end)đến vị trí bắt đầu bởidest.copy_if(begin, end, dest, predicate): Sao chép các phần tử thỏa mãnpredicateđếndest.
#include <vector>
#include <algorithm>
#include <iterator> // Cho back_inserter
#include <iostream>
int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(5); // Cần cấp phát đủ không gian trước
// Sao chép tất cả các phần tử
std::copy(source.begin(), source.end(), destination.begin());
std::cout << "Sao chep tat ca: ";
for(int val : destination) std::cout << val << " "; // destination: [1,2,3,4,5]
std::cout << std::endl;
// Sao chép các phần tử chẵn vào một container mới
std::vector<int> evens;
std::copy_if(source.begin(), source.end(), std::back_inserter(evens), [](int x) {
return x % 2 == 0;
});
std::cout << "Sao chep so chan: ";
for(int val : evens) std::cout << val << " "; // evens: [2,4]
std::cout << std::endl;
return 0;
}
Lưu ý: back_inserter(dest) sẽ tự động gọi push_back, không cần cấp phát không gian trước.
2.2. `transform`
Áp dụng một hàm lên từng phần tử trong phạm vi và lưu kết quả vào một phạm vi khác.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3};
std::vector<int> squares(nums.size());
// Tính bình phương (chuyển đổi một đối số)
std::transform(nums.begin(), nums.end(), squares.begin(), [](int x) {
return x * x;
});
std::cout << "Binh phuong: ";
for(int val : squares) std::cout << val << " "; // squares: [1,4,9]
std::cout << std::endl;
// Cộng các phần tử từ hai container (chuyển đổi hai đối số)
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
std::vector<int> sum(a.size());
std::transform(a.begin(), a.end(), b.begin(), sum.begin(), [](int x, int y) {
return x + y;
});
std::cout << "Tong: ";
for(int val : sum) std::cout << val << " "; // sum: [5,7,9]
std::cout << std::endl;
return 0;
}
2.3. `replace`, `replace_if` và `replace_copy`
replace(begin, end, old_val, new_val): Thay thế tất cả các phần tử có giá trịold_valbằngnew_val.replace_if(begin, end, predicate, new_val): Thay thế các phần tử thỏa mãnpredicatebằngnew_val.replace_copy(begin, end, dest, old_val, new_val): Sao chép và thay thế các phần tử (không sửa đổi container gốc).
#include <vector>
#include <algorithm>
#include <iterator> // Cho back_inserter
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3, 2, 5};
// Thay thế tất cả giá trị 2 bằng 20
std::replace(nums.begin(), nums.end(), 2, 20);
std::cout << "Sau replace: ";
for(int val : nums) std::cout << val << " "; // nums: [1,20,3,20,5]
std::cout << std::endl;
// Thay thế các phần tử lớn hơn 10 bằng 0
std::replace_if(nums.begin(), nums.end(), [](int x) {
return x > 10;
}, 0);
std::cout << "Sau replace_if: ";
for(int val : nums) std::cout << val << " "; // nums: [1,0,3,0,5]
std::cout << std::endl;
// Sao chép và thay thế số 3 bằng 300 (container gốc không đổi)
std::vector<int> res;
std::replace_copy(nums.begin(), nums.end(), std::back_inserter(res), 3, 300);
std::cout << "Sau replace_copy: ";
for(int val : res) std::cout << val << " "; // res: [1,0,300,0,5]
std::cout << std::endl;
return 0;
}
2.4. `remove`, `remove_if` và `erase`
remove(begin, end, value): Di chuyển các phần tử có giá trị bằngvaluevề cuối container, trả về một iterator mới đến vị trí logic cuối cùng (không thực sự xóa phần tử, cần kết hợp vớierase).remove_if(begin, end, predicate): Di chuyển các phần tử thỏa mãnpredicatevề cuối.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> nums = {1, 2, 3, 2, 4};
// Xóa logic tất cả các số 2 (di chuyển chúng về cuối)
auto new_end_it = std::remove(nums.begin(), nums.end(), 2);
// nums bây giờ có thể trông như {1, 3, 4, 2, 2} (các phần tử sau new_end_it không xác định)
std::cout << "Sau remove (logic): ";
for(auto it = nums.begin(); it != nums.end(); ++it) std::cout << *it << " ";
std::cout << std::endl;
// Xóa vật lý (thực sự loại bỏ các phần tử)
nums.erase(new_end_it, nums.end());
std::cout << "Sau erase: ";
for(int val : nums) std::cout << val << " "; // nums: [1,3,4]
std::cout << std::endl;
// Kết hợp lambda để xóa các số chẵn
nums = {1, 2, 3, 4, 5};
nums.erase(std::remove_if(nums.begin(), nums.end(), [](int x) {
return x % 2 == 0;
}), nums.end());
std::cout << "Sau remove_if & erase (chan): ";
for(int val : nums) std::cout << val << " "; // nums: [1,3,5]
std::cout << std::endl;
return 0;
}
2.5. `unique`
Loại bỏ các phần tử trùng lặp liên tiếp trong phạm vi, trả về một iterator mới đến vị trí logic cuối cùng. Thường được sử dụng kết hợp với erase.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {1, 1, 2, 2, 3, 3, 3, 4, 5};
auto last_unique = std::unique(vec.begin(), vec.end());
vec.erase(last_unique, vec.end()); // vec bây giờ là {1, 2, 3, 4, 5}
std::cout << "Sau unique & erase: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
2.6. `reverse`
Đảo ngược thứ tự các phần tử trong phạm vi.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::reverse(vec.begin(), vec.end()); // vec trở thành {5, 4, 3, 2, 1}
std::cout << "Sau reverse: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
2.7. `rotate`
Xoay các phần tử trong phạm vi sao cho một phần tử cụ thể trở thành phần tử đầu tiên.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// Xoay sao cho phần tử tại vec.begin() + 2 (là 3) trở thành phần tử đầu tiên
std::rotate(vec.begin(), vec.begin() + 2, vec.end()); // vec trở thành {3, 4, 5, 1, 2}
std::cout << "Sau rotate: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
2.8. `shuffle`
Hoán vị ngẫu nhiên các phần tử trong phạm vi (yêu cầu C++11 trở lên).
#include <vector>
#include <algorithm>
#include <random> // Cho random_device, mt19937
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::random_device rd; // Tạo seed ngẫu nhiên
std::mt19937 generator(rd()); // Sử dụng Mersenne Twister engine
std::shuffle(vec.begin(), vec.end(), generator); // Hoán vị ngẫu nhiên các phần tử
std::cout << "Sau shuffle: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
3. Thuật toán sắp xếp và liên quan
3.1. `sort`, `stable_sort` và `partial_sort`
sort(begin, end): Sắp xếp nhanh các phần tử (không ổn định, độ phức tạp trung bình O(n log n)).stable_sort(begin, end): Sắp xếp ổn định (thứ tự tương đối của các phần tử bằng nhau được bảo toàn).partial_sort(begin, mid, end): Sắp xếp một phần, đảm bảo rằng phạm vi[begin, mid)chứa các phần tử nhỏ nhất trong toàn bộ phạm vi và chúng được sắp xếp.
#include <vector>
#include <algorithm>
#include <functional> // Cho std::greater
#include <iostream>
#include <utility> // Cho std::pair
int main() {
std::vector<int> vec_sort = {5, 3, 1, 4, 2};
std::sort(vec_sort.begin(), vec_sort.end()); // Sắp xếp tăng dần mặc định
std::cout << "Sort tang dan: ";
for(int val : vec_sort) std::cout << val << " "; // {1, 2, 3, 4, 5}
std::cout << std::endl;
std::sort(vec_sort.begin(), vec_sort.end(), std::greater<int>()); // Sắp xếp giảm dần
std::cout << "Sort giam dan: ";
for(int val : vec_sort) std::cout << val << " "; // {5, 4, 3, 2, 1}
std::cout << std::endl;
// Sắp xếp ổn định
std::vector<std::pair<int, int>> vec_stable = {{1, 2}, {2, 1}, {1, 1}, {2, 2}};
std::stable_sort(vec_stable.begin(), vec_stable.end(), [](const auto& a, const auto& b) {
return a.first < b.first; // Sắp xếp theo 'first', giữ nguyên thứ tự tương đối của các cặp có 'first' giống nhau
});
std::cout << "Stable Sort theo first: ";
for(const auto& p : vec_stable) std::cout << "(" << p.first << "," << p.second << ") "; // {(1,2), (1,1), (2,1), (2,2)}
std::cout << std::endl;
// Sắp xếp một phần
std::vector<int> vec_partial = {5, 3, 1, 4, 2, 6};
// Đặt 3 phần tử nhỏ nhất vào đầu và sắp xếp chúng
std::partial_sort(vec_partial.begin(), vec_partial.begin() + 3, vec_partial.end());
std::cout << "Partial Sort (3 phan tu nho nhat): ";
for(int val : vec_partial) std::cout << val << " "; // {1, 2, 3, ?, ?, ?} (4, 5, 6 có thể ở các vị trí khác nhau)
std::cout << std::endl;
return 0;
}
3.2. `nth_element`
Sắp xếp lại phạm vi sao cho phần tử tại vị trí chỉ định sẽ giống với phần tử ở vị trí đó nếu toàn bộ phạm vi được sắp xếp. Các phần tử bên trái của nó đều nhỏ hơn hoặc bằng, và các phần tử bên phải đều lớn hơn hoặc bằng nó.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {5, 3, 1, 4, 2, 6};
// Tìm phần tử nhỏ thứ 3 (tại index 2)
std::nth_element(vec.begin(), vec.begin() + 2, vec.end());
// Bây giờ vec[2] sẽ là 3. Các phần tử bên trái <= 3, bên phải >= 3.
std::cout << "Sau nth_element (index 2): ";
for(int val : vec) std::cout << val << " "; // Ví dụ: {1, 2, 3, 6, 5, 4} (thứ tự bên trái/phải không đảm bảo)
std::cout << std::endl;
return 0;
}
3.3. `binary_search`, `lower_bound`, `upper_bound`
Cần sử dụng trên các container **đã được sắp xếp**.
binary_search(begin, end, value): Kiểm tra xemvaluecó tồn tại trong phạm vi không (trả vềbool).lower_bound(begin, end, value): Trả về iterator đến phần tử đầu tiên **không nhỏ hơn** (>=)value.upper_bound(begin, end, value): Trả về iterator đến phần tử đầu tiên **lớn hơn** (>)value.
#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip> // Cho boolalpha
int main() {
std::vector<int> sorted_vec = {1, 3, 3, 5, 7}; // PHẢI được sắp xếp
// Kiểm tra sự tồn tại của số 3
bool exists = std::binary_search(sorted_vec.begin(), sorted_vec.end(), 3);
std::cout << "So 3 co ton tai? " << std::boolalpha << exists << std::endl; // true
// Tìm phần tử đầu tiên >= 3
auto lb_it = std::lower_bound(sorted_vec.begin(), sorted_vec.end(), 3);
if (lb_it != sorted_vec.end()) {
std::cout << "lower_bound cho 3 tai index: " << (lb_it - sorted_vec.begin()) << std::endl; // Xuất: 1
}
// Tìm phần tử đầu tiên > 3
auto ub_it = std::upper_bound(sorted_vec.begin(), sorted_vec.end(), 3);
if (ub_it != sorted_vec.end()) {
std::cout << "upper_bound cho 3 tai index: " << (ub_it - sorted_vec.begin()) << std::endl; // Xuất: 3
}
// Tìm phần tử đầu tiên >= 4
auto lb_it_4 = std::lower_bound(sorted_vec.begin(), sorted_vec.end(), 4);
if (lb_it_4 != sorted_vec.end()) {
std::cout << "lower_bound cho 4 tai index: " << (lb_it_4 - sorted_vec.begin()) << std::endl; // Xuất: 3 (phần tử 5)
}
return 0;
}
3.4. `merge`
Hợp nhất hai phạm vi **đã sắp xếp** vào một container mới (vẫn giữ thứ tự).
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec_a = {1, 3, 5};
std::vector<int> vec_b = {2, 4, 6};
std::vector<int> merged_vec(vec_a.size() + vec_b.size());
// Hợp nhất vec_a và vec_b (cả hai đều phải được sắp xếp)
std::merge(vec_a.begin(), vec_a.end(), vec_b.begin(), vec_b.end(), merged_vec.begin());
std::cout << "Sau merge: ";
for(int val : merged_vec) std::cout << val << " "; // merged_vec: [1,2,3,4,5,6]
std::cout << std::endl;
return 0;
}
4. Thuật toán Heap
STL cung cấp các thuật toán để thao tác một phạm vi như một heap, bao gồm make_heap, push_heap, pop_heap, sort_heap.
#include <vector>
#include <algorithm> // Cho các thuật toán heap
#include <iostream>
int main() {
std::vector<int> vec = {4, 1, 3, 2, 5};
// Xây dựng một max-heap
std::make_heap(vec.begin(), vec.end());
std::cout << "Sau make_heap: ";
for(int val : vec) std::cout << val << " "; // Ví dụ: {5, 4, 3, 2, 1} (cấu trúc heap)
std::cout << std::endl;
// Thêm một phần tử mới vào heap
vec.push_back(6);
std::push_heap(vec.begin(), vec.end()); // Đưa phần tử mới vào vị trí đúng trong heap
std::cout << "Sau push_back & push_heap: ";
for(int val : vec) std::cout << val << " "; // Ví dụ: {6, 4, 5, 2, 1, 3}
std::cout << std::endl;
// Di chuyển phần tử lớn nhất (root) ra cuối heap
std::pop_heap(vec.begin(), vec.end());
std::cout << "Sau pop_heap (root ra cuoi): ";
for(int val : vec) std::cout << val << " "; // Ví dụ: {5, 4, 3, 2, 1, 6} (6 là phần tử lớn nhất, giờ ở cuối)
std::cout << std::endl;
int max_val = vec.back(); // Lấy giá trị lớn nhất
vec.pop_back(); // Xóa phần tử lớn nhất khỏi vector
// Sắp xếp heap thành một dãy tăng dần
std::sort_heap(vec.begin(), vec.end());
std::cout << "Sau sort_heap: ";
for(int val : vec) std::cout << val << " "; // {1, 2, 3, 4, 5}
std::cout << std::endl;
return 0;
}
5. Thuật toán Min/Max
5.1. `min` và `max`
Trả về giá trị nhỏ nhất/lớn nhất từ hai giá trị hoặc một danh sách khởi tạo.
#include <algorithm>
#include <iostream>
#include <initializer_list> // Cho initializer_list
int main() {
int a = 5, b = 3;
int min_val = std::min(a, b); // 3
int max_val = std::max(a, b); // 5
std::cout << "Min(a, b): " << min_val << ", Max(a, b): " << max_val << std::endl;
// Sử dụng initializer_list (C++11)
auto min_of_list = std::min({4, 2, 8, 5, 1}); // 1
auto max_of_list = std::max({4, 2, 8, 5, 1}); // 8
std::cout << "Min cua danh sach: " << min_of_list << std::endl;
std::cout << "Max cua danh sach: " << max_of_list << std::endl;
return 0;
}
5.2. `min_element` và `max_element`
Trả về iterator đến phần tử nhỏ nhất/lớn nhất trong phạm vi.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {3, 1, 4, 2, 5};
auto min_it = std::min_element(vec.begin(), vec.end()); // Trỏ đến phần tử 1
auto max_it = std::max_element(vec.begin(), vec.end()); // Trỏ đến phần tử 5
if (min_it != vec.end()) {
std::cout << "Phan tu nho nhat: " << *min_it << std::endl;
}
if (max_it != vec.end()) {
std::cout << "Phan tu lon nhat: " << *max_it << std::endl;
}
return 0;
}
5.3. `minmax_element` (C++11)
Trả về một cặp iterator, chỉ đến phần tử nhỏ nhất và lớn nhất trong phạm vi.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec = {3, 1, 4, 2, 5};
auto minmax_pair = std::minmax_element(vec.begin(), vec.end());
// minmax_pair.first trỏ đến 1, minmax_pair.second trỏ đến 5
if (minmax_pair.first != vec.end()) {
std::cout << "Phan tu nho nhat: " << *(minmax_pair.first) << std::endl;
}
if (minmax_pair.second != vec.end()) {
std::cout << "Phan tu lon nhat: " << *(minmax_pair.second) << std::endl;
}
return 0;
}
6. Thuật toán số học (trong <numeric>)
6.1. `accumulate`
Tính tổng cộng dồn (hoặc phép toán tùy chỉnh) của các phần tử trong phạm vi.
#include <vector>
#include <numeric> // Cho accumulate, multiplies
#include <functional> // Cho multiplies
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// Tính tổng, giá trị khởi tạo là 0
int sum = std::accumulate(vec.begin(), vec.end(), 0); // Kết quả là 15
// Tính tích, giá trị khởi tạo là 1, sử dụng std::multiplies
int product = std::accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>()); // Kết quả là 120
std::cout << "Tong: " << sum << std::endl;
std::cout << "Tich: " << product << std::endl;
return 0;
}
6.2. `inner_product`
Tính tích vô hướng của hai phạm vi (hoặc phép toán tùy chỉnh).
#include <vector>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
// Tính tích vô hướng: (1*4) + (2*5) + (3*6)
int dot_product = std::inner_product(a.begin(), a.end(), b.begin(), 0); // Kết quả là 32
std::cout << "Tich vo huong: " << dot_product << std::endl;
return 0;
}
6.3. `iota`
Điền các giá trị tăng dần liên tiếp vào một phạm vi.
#include <vector>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> vec(5);
// Điền các giá trị bắt đầu từ 10
std::iota(vec.begin(), vec.end(), 10); // vec trở thành {10, 11, 12, 13, 14}
std::cout << "Sau iota: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
6.4. `partial_sum`
Tính tổng từng phần và lưu kết quả vào phạm vi đích.
#include <vector>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(source.size());
// Tính tổng từng phần
std::partial_sum(source.begin(), source.end(), destination.begin()); // destination trở thành {1, 3, 6, 10, 15}
std::cout << "Sau partial_sum: ";
for(int val : destination) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
6.5. `adjacent_difference`
Tính hiệu của các phần tử liền kề và lưu kết quả vào phạm vi đích.
#include <vector>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(source.size());
// Tính hiệu các phần tử liền kề
std::adjacent_difference(source.begin(), source.end(), destination.begin()); // destination trở thành {1, 1, 1, 1, 1}
// (1, 2-1, 3-2, 4-3, 5-4)
std::cout << "Sau adjacent_difference: ";
for(int val : destination) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
7. Khác
7.1. `generate`
Điền một phạm vi bằng các giá trị được tạo bởi một hàm.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec(5);
int current_val = 0;
// Sử dụng lambda để tạo giá trị
std::generate(vec.begin(), vec.end(), [¤t_val]() {
return current_val++; // Tạo các giá trị 0, 1, 2, 3, 4
});
std::cout << "Sau generate: ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
7.2. `generate_n`
Điền n phần tử đầu tiên của một phạm vi bằng các giá trị được tạo bởi một hàm.
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> vec(5); // Tạo vector với 5 phần tử (giá trị ban đầu không quan trọng)
int start_val = 10;
// Điền 3 phần tử đầu tiên với các giá trị tăng dần từ 10
std::generate_n(vec.begin(), 3, [&start_val]() {
return start_val++; // Tạo 10, 11, 12
});
std::cout << "Sau generate_n (3 phan tu): ";
for(int val : vec) std::cout << val << " "; // 3 phần tử đầu là 10, 11, 12. 2 phần tử cuối không đổi.
std::cout << std::endl;
return 0;
}
7.3. `includes`
Kiểm tra xem một phạm vi đã sắp xếp có chứa tất cả các phần tử của một phạm vi đã sắp xếp khác hay không.
#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip> // Cho boolalpha
int main() {
std::vector<int> main_range = {1, 2, 3, 4, 5}; // Phải sắp xếp
std::vector<int> sub_range = {2, 4}; // Phải sắp xếp
// Kiểm tra xem main_range có chứa tất cả các phần tử của sub_range không
bool result = std::includes(main_range.begin(), main_range.end(),
sub_range.begin(), sub_range.end());
std::cout << "main_range bao gom sub_range? " << std::boolalpha << result << std::endl; // true
std::vector<int> sub_range2 = {2, 6};
result = std::includes(main_range.begin(), main_range.end(),
sub_range2.begin(), sub_range2.end());
std::cout << "main_range bao gom sub_range2? " << std::boolalpha << result << std::endl; // false
return 0;
}
7.4. `set_union`, `set_intersection`, `set_difference`, `set_symmetric_difference`
Thực hiện các phép toán tập hợp: hợp, giao, hiệu và hiệu đối xứng.
#include <vector>
#include <algorithm>
#include <iterator> // Cho back_inserter
#include <iostream>
void print_vector(const std::vector<int>& vec, const std::string& label) {
std::cout << label << ": ";
for(int val : vec) std::cout << val << " ";
std::cout << std::endl;
}
int main() {
std::vector<int> v1 = {1, 2, 3, 4, 5}; // Phải sắp xếp
std::vector<int> v2 = {3, 4, 5, 6, 7}; // Phải sắp xếp
std::vector<int> result;
// Hợp (Union)
result.clear();
std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
print_vector(result, "Union (v1 | v2)"); // {1, 2, 3, 4, 5, 6, 7}
// Giao (Intersection)
result.clear();
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
print_vector(result, "Intersection (v1 & v2)"); // {3, 4, 5}
// Hiệu (Difference: v1 - v2)
result.clear();
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
print_vector(result, "Difference (v1 - v2)"); // {1, 2}
// Hiệu đối xứng (Symmetric Difference: (v1 U v2) - (v1 ∩ v2))
result.clear();
std::set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
print_vector(result, "Symmetric Difference"); // {1, 2, 6, 7}
return 0;
}
8. Câu hỏi thường gặp
- Sự khác biệt giữa
sortvàstable_sort?sortsử dụng thuật toán Introsort (kết hợp QuickSort, HeapSort, InsertionSort), không ổn định (thứ tự tương đối của các phần tử bằng nhau có thể thay đổi), độ phức tạp trung bình O(n log n).stable_sortsử dụng thuật toán MergeSort, ổn định (thứ tự tương đối của các phần tử bằng nhau được bảo toàn), độ phức tạp O(n log n), nhưng có thể tốn thêm bộ nhớ.
- Tại sao thuật toán
removecần được sử dụng kết hợp vớierase?removechỉ di chuyển các phần tử không mong muốn về cuối phạm vi và trả về một iterator mới đến vị trí logic cuối cùng. Nó không thay đổi kích thước thực tế của container. Thuật toánerasemới là cái thực sự loại bỏ các phần tử dựa trên một phạm vi iterator và thay đổi kích thước container. Do đó, cần kết hợp chúng theo mẫu `container.erase(remove(…), container.end())`. - Những thuật toán nào yêu cầu container phải được sắp xếp?
Các thuật toán thuộc nhóm tìm kiếm nhị phân (
binary_search,lower_bound,upper_bound), các thuật toán thao tác tập hợp (set_intersection,set_union, v.v.), vàmerge. Các thuật toán này dựa vào tính chất có thứ tự để hoạt động hiệu quả (ví dụ: tìm kiếm nhị phân có độ phức tạp O(log n)).