Chuyển tới nội dung chính

std::unique

#include <algorithm>

// So sánh bằng toán tử ==
template <class ForwardIterator>
ForwardIterator unique (ForwardIterator first, ForwardIterator last);

// Sử dụng hàm vị từ so sánh
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique (ForwardIterator first, ForwardIterator last,
BinaryPredicate pred);

Loại bỏ các phần tử trùng lặp liên tiếp ra khỏi một phạm vi (range). Lưu ý rằng unique() chỉ loại bỏ các phần tử trùng lặp liên tiếp, vì vậy nếu các phần tử trùng lặp không nằm cạnh nhau, chúng sẽ không bị loại bỏ. Để loại bỏ tất cả các phần tử trùng lặp, bạn nên sắp xếp (sort) phạm vi trước khi sử dụng unique().

Tham số

first

  • Forward Iterator trỏ đến phần tử đầu tiên trong phạm vi cần loại bỏ các phần tử trùng lặp liên tiếp.

last

  • Forward Iterator trỏ đến phần tử ngay sau phần tử cuối cùng trong phạm vi. Phạm vi là [first, last).

pred

  • Một hàm vị từ (binary predicate) nhận hai đối số (hai phần tử liên tiếp trong phạm vi) và trả về true nếu hai phần tử được coi là bằng nhau (trùng lặp), false nếu ngược lại. (Phiên bản 2)

Giá trị trả về

  • Forward Iterator trỏ đến phần tử ngay sau phần tử cuối cùng của phạm vi mới (phạm vi sau khi đã loại bỏ các phần tử trùng lặp liên tiếp). Các phần tử từ vị trí này đến cuối container ban đầu sẽ có giá trị không xác định.

Đặc điểm

  1. unique() không thay đổi kích thước của container. Nó chỉ di chuyển các phần tử.
  2. unique() chỉ loại bỏ các phần tử trùng lặp liên tiếp.
  3. Phạm vi [first, last) là "nửa mở", không bao gồm phần tử last.
  4. unique() sử dụng toán tử == (phiên bản 1) hoặc hàm vị từ pred (phiên bản 2) để so sánh các phần tử.
  5. unique() yêu cầu Forward Iterator, cho phép di chuyển một chiều về phía trước.
  6. Để xóa thực sự các phần tử, bạn cần sử dụng phương thức erase() của container (như trong ví dụ).
  7. Để loại bỏ tất cả các phần tử trùng lặp (không cần liên tiếp), hãy sắp xếp (sort) phạm vi trước khi gọi unique().
  8. unique() thường được sử dụng kết hợp với phương thức erase() của container để xóa các phần tử trùng lặp liên tiếp khỏi container (tương tự như erase-remove idiom). Ngoài ra, unique() cũng thường được sử dụng sau khi sort() để loại bỏ tất cả các phần tử trùng lặp (không nhất thiết phải liên tiếp).

Ví dụ

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

// Hàm vị từ so sánh hai chuỗi không phân biệt hoa thường
bool caseInsensitiveCompare(const std::string& a, const std::string& b) {
return std::equal(a.begin(), a.end(), b.begin(),
[](char a, char b){ return std::tolower(a) == std::tolower(b); });
}

int main() {
std::vector<int> numbers = {1, 2, 2, 2, 3, 3, 2, 2, 4, 4, 4, 4};

// Loại bỏ các phần tử trùng lặp liên tiếp
auto it = std::unique(numbers.begin(), numbers.end());

std::cout << "numbers after unique: ";
for (auto i = numbers.begin(); i != numbers.end(); ++i) {
std::cout << *i << " ";
}
std::cout << std::endl;
std::cout << "New logical end: " << (it - numbers.begin()) << std::endl;

// Xóa các phần tử từ it đến cuối vector
numbers.erase(it, numbers.end());

std::cout << "numbers after erase: ";
for (int x : numbers) {
std::cout << x << " ";
}
std::cout << std::endl;

std::vector<int> numbers2 = {1, 3, 5, 2, 1, 3, 5, 2, 6, 3, 7, 1};
std::cout << "numbers2 before sort and unique: ";
for (int x : numbers2) {
std::cout << x << " ";
}
std::cout << std::endl;

// Sắp xếp và loại bỏ tất cả phần tử trùng lặp
std::sort(numbers2.begin(), numbers2.end());
numbers2.erase(std::unique(numbers2.begin(), numbers2.end()), numbers2.end());

std::cout << "numbers2 after sort and unique: ";
for (int x : numbers2) {
std::cout << x << " ";
}
std::cout << std::endl;

std::vector<std::string> words = {"apple", "Banana", "apple", "orange", "banana", "Apple"};

// Loại bỏ các chuỗi trùng lặp liên tiếp (không phân biệt hoa thường)
auto it2 = std::unique(words.begin(), words.end(), caseInsensitiveCompare);
words.erase(it2, words.end());

std::cout << "words after unique (case-insensitive): ";
for (const std::string& w : words) {
std::cout << w << " ";
}
std::cout << std::endl;

return 0;
}

Các hàm liên quan

unique_copySao chép các phần tử từ một phạm vi (range) nguồn sang một phạm vi đích khác
adjacent_findTìm kiếm cặp phần tử liên tiếp đầu tiên trong một phạm vi mà hai phần tử đó thỏa mãn một điều kiện nhất định
removeDi chuyển các phần tử không bị "xóa" lên đầu phạm vi, và trả về một iterator trỏ đến phần tử ngay sau phần tử cuối cùng của dãy mới
remove_ifDi chuyển các phần tử không thỏa mãn điều kiện lên đầu phạm vi, và trả về một iterator trỏ đến phần tử ngay sau phần tử cuối cùng của dãy mới