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

std::unique_copy

#include <algorithm>

// So sánh bằng toán tử ==
template <class InputIterator, class OutputIterator>
OutputIterator unique_copy (InputIterator first, InputIterator last,
OutputIterator result);

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

Sao 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, bỏ qua (không sao chép) các phần tử trùng lặp liên tiếp. Nói cách khác, nó chỉ sao chép các phần tử duy nhất (unique) và liên tiếp từ phạm vi nguồn sang phạm vi đích.

Tham số

first

  • Input Iterator trỏ đến phần tử đầu tiên trong phạm vi nguồn cần sao chép.

last

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

result

  • Output Iterator trỏ đến phần tử đầu tiên trong phạm vi đích nơi các phần tử sẽ được sao chép đến.

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 nguồn) 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ề

  • Output Iterator trỏ đến phần tử ngay sau phần tử cuối cùng được sao chép trong phạm vi đích.

Đặc điểm

  1. Phạm vi nguồn [first, last) là "nửa mở", không bao gồm phần tử last.
  2. Phạm vi đích phải có đủ không gian để chứa các phần tử được sao chép. Sử dụng std::back_inserter là một cách an toàn khi sao chép vào container có thể thay đổi kích thước như std::vector.
  3. unique_copy() không thay đổi phạm vi nguồn.
  4. unique_copy() 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_copy() trả về iterator trỏ đến sau phần tử cuối cùng được sao chép trong phạm vi đích.
  6. unique_copy() chỉ loại bỏ các phần tử trùng lặp liên tiếp. Để loại bỏ tất cả phần tử trùng lặp, cần sort() trước.
  7. unique_copy() thường được sử dụng để:
    • Tạo một bản sao của một container, loại bỏ các phần tử trùng lặp liên tiếp.
    • Sao chép dữ liệu từ nguồn sang đích, lọc bỏ các phần tử trùng lặp liên tiếp.
    • Kết hợp việc sao chép và loại bỏ các phần tử trùng lặp liên tiếp trong một bước duy nhất.
Phân biệt với unique(), remove_copy(), và copy_if()
  • unique() loại bỏ các phần tử trùng lặp liên tiếp tại chỗ (trong phạm vi gốc), cần kết hợp với erase() để xóa hoàn toàn.
  • remove_copy() sao chép các phần tử không bằng với một giá trị cho trước sang phạm vi đích.
  • copy_if() chỉ sao chép các phần tử thỏa mãn điều kiện sang phạm vi đích.
  • unique_copy() sao chép các phần tử, bỏ qua các phần tử trùng lặp liên tiếp so với phần tử đứng trước nó.

Ví dụ

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#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> source = {1, 2, 2, 2, 3, 3, 2, 2, 4, 4, 4, 4};
std::vector<int> destination;

// Sao chép source sang destination, loại bỏ các phần tử trùng lặp liên tiếp
std::unique_copy(source.begin(), source.end(), std::back_inserter(destination));

std::cout << "destination after unique_copy: ";
for (int x : destination) {
std::cout << x << " ";
}
std::cout << std::endl;

std::string str = "aAbBcCdDeEFfGg";
std::string resultStr;

// Sao chép str sang resultStr, loại bỏ các ký tự trùng lặp liên tiếp không phân biệt hoa thường
std::unique_copy(str.begin(), str.end(), std::back_inserter(resultStr), caseInsensitiveCompare);

std::cout << "resultStr after unique_copy (case-insensitive): " << resultStr << std::endl;

return 0;
}

Các hàm liên quan

uniqueLoại bỏ các phần tử trùng lặp liên tiếp ra khỏi một phạm vi (range)
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