2008-12-07 129 views
77

Tôi muốn xóa phần tử khỏi véc-tơ bằng phương pháp xóa. Nhưng vấn đề ở đây là phần tử không được bảo đảm chỉ xảy ra một lần trong vectơ. Nó có thể được trình bày nhiều lần và tôi cần phải xóa tất cả chúng. Mã của tôi là một cái gì đó như thế này:Xóa các phần tử khỏi một vector

void erase(std::vector<int>& myNumbers_in, int number_in) 
{ 
    std::vector<int>::iterator iter = myNumbers_in.begin(); 
    std::vector<int>::iterator endIter = myNumbers_in.end(); 
    for(; iter != endIter; ++iter) 
    { 
     if(*iter == number_in) 
     { 
      myNumbers_in.erase(iter); 
     } 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    std::vector<int> myNmbers; 
    for(int i = 0; i < 2; ++i) 
    { 
     myNmbers.push_back(i); 
     myNmbers.push_back(i); 
    } 

    erase(myNmbers, 1); 

    return 0; 
} 

Mã này rõ ràng là bị treo vì tôi đang thay đổi phần cuối của vector trong khi iterating qua nó. cách tốt nhất để đạt được điều này là gì? I E. là có cách nào để làm điều này mà không cần lặp qua vector nhiều lần hoặc tạo thêm một bản sao của véc-tơ?

Trả lời

129

Sử dụng remove/erase idiom:

std::vector<int>& vec = myNumbers; // use shorter name 
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end()); 

gì xảy ra là remove máy ảnh compact các yếu tố khác với giá trị phải được loại bỏ (number_in) vào đầu của vector và trả về iterator đến phần tử đầu tiên sau phạm vi đó. Sau đó, erase xóa các phần tử này (giá trị của ai không được chỉ định).

+2

'std :: remove()' thay đổi các phần tử sao cho các phần tử cần xóa sẽ bị ghi đè. Thuật toán không thay đổi kích thước của vùng chứa, và nếu các phần tử 'n' bị loại bỏ thì nó không được xác định là các phần tử' n' cuối cùng là gì. – wilhelmtell

+17

thành phần xóa-xóa được mô tả trong khoản 32 trong sách "Hiệu quả STL: 50 cách cụ thể để cải thiện việc sử dụng thư viện mẫu chuẩn" của Scott Meyers. –

+0

Làm thế nào tôi có thể cập nhật điều này để xóa một đối tượng tùy chỉnh, thay vì một đối tượng nguyên thủy? Tôi muốn xóa nội dung nào đó khi đang duyệt qua một vectơ . – Benjin

-1

Bạn có thể bắt đầu từ cuối hoặc cập nhật kết thúc khi một phần tử bị xóa.

Nó sẽ giống như thế này:

void erase(std::vector<int>& myNumbers_in, int number_in) 
    { 
     std::vector<int>::iterator iter = myNumbers_in.begin(); 
     std::vector<int>::iterator endIter = myNumbers_in.end(); 
     for(; iter != endIter; ++iter) 
     { 
       if(*iter == number_in) 
       { 
         myNumbers_in.erase(iter); 
         endIter = myNumbers_in.end(); 
       } 
     } 

    } 
+0

Tôi đã thử các mảnh trên mã. Nó làm việc cho trường hợp ban đầu của tôi, nhưng khi tôi tạo ra một vector với 0,0,0,1 như các giá trị và cố gắng để xóa 0 nó đã không hoạt động đúng. Sau khi thoát khỏi vòng lặp, tôi thấy rằng kích thước của vector là 2 thay vì 1. – Naveen

+1

Đây là trường hợp xấu nhất O (N^2). Các thuật toán O (N) tồn tại. Bạn có thể làm tốt hơn. Ngoài ra, xóa (lặp) tiếp theo là + + lặp lại có thể, tùy thuộc vào việc thực hiện vector STL <>, bỏ qua mục sau đây. Xem xét "xóa v [i = 2]; i ++;" - bạn không bao giờ kiểm tra mục nhập i = 3 (bây giờ i = 2) ban đầu trong v []. –

45

Calling xóa sẽ làm mất hiệu lực lặp, bạn có thể sử dụng:

void erase(std::vector<int>& myNumbers_in, int number_in) 
{ 
    std::vector<int>::iterator iter = myNumbers_in.begin(); 
    while (iter != myNumbers_in.end()) 
    { 
     if (*iter == number_in) 
     { 
      iter = myNumbers_in.erase(iter); 
     } 
     else 
     { 
      ++iter; 
     } 
    } 

} 

Hoặc bạn có thể sử dụng std::remove_if cùng với một functor và std :: vector: : xóa:

struct Eraser 
{ 
    Eraser(int number_in) : number_in(number_in) {} 
    int number_in; 
    bool operator()(int i) const 
    { 
     return i == number_in; 
    } 
}; 

std::vector<int> myNumbers; 
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end()); 

Thay vì viết functor của riêng bạn trong trường hợp này bạn đồng ULD sử dụng std::remove:

std::vector<int> myNumbers; 
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end()); 
+1

Tại sao sử dụng hàm functor của riêng bạn khi bạn có thể sử dụng equal_to? :-P http://www.sgi.com/tech/stl/equal_to.html –

+2

Nhân tiện, gọi 'xóa' bằng 'xóa' là cách kinh điển để thực hiện việc này. –

+0

Tôi nghĩ rằng chính xác điều đó. nhưng anh ta nên sử dụng remove_if nếu sử dụng một firctor riêng của iirc. hoặc chỉ sử dụng loại bỏ mà không có functor –

3

Tùy thuộc vào lý do bạn thực hiện việc này, sử dụng std::set có thể là một ý tưởng tốt hơn so với std :: vector.

Nó cho phép mỗi phần tử chỉ xảy ra một lần. Nếu bạn thêm nó nhiều lần, sẽ chỉ có một ví dụ để xóa. Điều này sẽ làm cho hoạt động xóa không đáng kể. Hoạt động xóa cũng sẽ có độ phức tạp thời gian thấp hơn so với trên vectơ, tuy nhiên, việc thêm các phần tử chậm hơn trên tập hợp để nó có thể không có nhiều lợi thế.

Điều này tất nhiên sẽ không hoạt động nếu bạn quan tâm đến số lần một phần tử đã được thêm vào vectơ của bạn hoặc thứ tự các phần tử được thêm vào.

11
  1. Bạn có thể lặp lại bằng cách sử dụng truy cập chỉ mục,

  2. Để tránh O (n^2) phức tạp bạn có thể sử dụng hai chỉ số, i - hiện tại chỉ số xét nghiệm, j - chỉ số để cửa hàng mục tiếp theo và ở cuối chu kỳ mới của vectơ.

mã:

void erase(std::vector<int>& v, int num) 
{ 
    size_t j = 0; 
    for (size_t i = 0; i < v.size(); ++i) { 
    if (v[i] != num) v[j++] = v[i]; 
    } 
    // trim vector to new size 
    v.resize(j); 
} 

Trong trường hợp này bạn không có hủy bỏ hiệu lực của vòng lặp, độ phức tạp là O (n), và mã là rất súc tích và bạn không cần phải viết một số lớp helper, mặc dù trong một số trường hợp sử dụng các lớp trợ giúp có thể được hưởng lợi trong mã linh hoạt hơn.

Mã này không sử dụng phương thức erase nhưng giải quyết được tác vụ của bạn.

Sử dụng STL tinh khiết bạn có thể làm điều này theo cách sau (điều này cũng tương tự như câu trả lời của Motti):

#include <algorithm> 

void erase(std::vector<int>& v, int num) { 
    vector<int>::iterator it = remove(v.begin(), v.end(), num); 
    v.erase(it, v.end()); 
} 
Các vấn đề liên quan