2009-05-17 69 views
344

Tôi có một tiêu chuẩn :: vector < int> và tôi muốn xóa phần tử thứ n. Làm thế nào để làm điều đó?Làm cách nào để xóa một phần tử khỏi std :: vector <> theo chỉ mục?

std::vector<int> vec; 

vec.push_back(6); 
vec.push_back(-17); 
vec.push_back(12); 

vec.erase(???); 
+4

Cân nhắc sử dụng std :: deque cung cấp chèn và xóa ở cả hai đầu. – Dario

+11

Không, đừng xem xét sử dụng deque chỉ vì bạn có thể muốn xóa một yếu tố, đó thực sự là lời khuyên nghèo nàn. Có một tải toàn bộ lý do tại sao bạn có thể muốn sử dụng deque hoặc vector. Đúng là việc xóa một phần tử từ một véc tơ có thể tốn kém - đặc biệt nếu véc tơ lớn, nhưng không có lý do gì để nghĩ rằng một deque sẽ tốt hơn một vectơ từ ví dụ mã bạn vừa đăng. – Owl

+2

Ví dụ: nếu bạn có ứng dụng đồ họa nơi bạn hiển thị "danh sách" những thứ bạn chèn/xóa mọi thứ tương tác, hãy xem xét bạn chạy qua danh sách 50-100 lần mỗi giây để hiển thị chúng và bạn thêm/xóa mọi thứ vài lần mỗi phút. Vì vậy, việc thực hiện "danh sách" dưới dạng vectơ có lẽ là một lựa chọn tốt hơn về tổng hiệu quả. –

Trả lời

460

Để xóa một yếu tố duy nhất, bạn có thể làm:

std::vector<int> vec; 

vec.push_back(6); 
vec.push_back(-17); 
vec.push_back(12); 

// Deletes the second element (vec[1]) 
vec.erase(vec.begin() + 1); 

Hoặc, để xóa nhiều hơn một yếu tố cùng một lúc:

// Deletes the second through third elements (vec[1], vec[2]) 
vec.erase(vec.begin() + 1, vec.begin() + 3); 
+15

Lưu ý rằng toán tử nhị phân '+' là __not__ nhất thiết được xác định cho vòng lặp trên các loại vùng chứa khác, như 'danh sách :: iterator' (bạn không thể làm' list.begin() + 2' trên danh sách 'std ::', bạn có để sử dụng ['std :: advance'] (http://www.cplusplus.com/reference/iterator/advance/) cho điều đó) – bobobobo

+0

bạn có nói rằng" +1 "là phần tử đầu tiên myVector [0] hay không vị trí thực tế myVector [1] –

+0

nó + 1 là phần tử có id 1, ví dụ như vùng chứa [1]. phần tử đầu tiên là +0. Xem chú thích dưới đây ... – Nick

159

Phương pháp xóa ngay khi std :: vector bị quá tải, do đó, có thể rõ ràng hơn để gọi

vec.erase(vec.begin() + index); 

khi bạn chỉ muốn xóa một phần tử duy nhất.

+0

Điều này không tốt nếu bạn chỉ có một phần tử ... bạn không kiểm tra chỉ mục ... – alap

+2

Nhưng vấn đề đó xuất hiện cho dù bạn có bao nhiêu phần tử. –

+11

nếu chỉ có một phần tử, chỉ mục là 0 và vì vậy bạn nhận được 'vec.begin()' hợp lệ. –

39
template <typename T> 
void remove(std::vector<T>& vec, size_t pos) 
{ 
    std::vector<T>::iterator it = vec.begin(); 
    std::advance(it, pos); 
    vec.erase(it); 
} 
+2

Tối đa, điều làm cho chức năng đó tốt hơn: 'mẫu void remove (std :: vector & vec, size_t pos) {vec.erase (vec.begin + pos); } 'Tôi không nói hoặc là tốt hơn, chỉ đơn thuần là yêu cầu sự quan tâm cá nhân và trả lại kết quả tốt nhất mà câu hỏi này có thể nhận được. –

+9

@JoeyvG: Kể từ khi một 'vector :: iterator' là một trình vòng lặp truy cập ngẫu nhiên, phiên bản của bạn là tốt và có thể rõ ràng hơn một chút. Tuy nhiên, phiên bản Max đăng tải sẽ hoạt động tốt nếu bạn thay đổi vùng chứa sang vùng chứa khác không hỗ trợ trình vòng lặp truy cập ngẫu nhiên –

+1

Đây là câu trả lời hay hơn vì nó cũng áp dụng cho các định dạng vùng chứa khác. Bạn cũng có thể sử dụng std :: next(). – Bim

8

Phương pháp erase sẽ được sử dụng theo hai cách:

  1. Tẩy xoá yếu tố duy nhất:

    vector.erase(vector.begin() + 3); // Deleting the third element 
    
  2. Tẩy xoá hàng loạt các yếu tố:

    vector.erase(vector.begin() + 3, vector.begin() + 5); // Deleting from third element to fifth element 
    
1

Nếu bạn làm việc với vectơ lớn (kích thước> 100.000) và muốn xóa nhiều yếu tố, tôi sẽ khuyên bạn nên làm một cái gì đó như thế này:

int main(int argc, char** argv) { 

    vector <int> vec; 
    vector <int> vec2; 

    for (int i = 0; i < 20000000; i++){ 
     vec.push_back(i);} 

    for (int i = 0; i < vec.size(); i++) 
    { 
     if(vec.at(i) %3 != 0) 
      vec2.push_back(i); 
    } 

    vec = vec2; 
    cout << vec.size() << endl; 
} 

Mã này tận dụng mọi số trong vec mà không thể chia cho 3 và sao chép nó vào vec2. Sau đó nó sao chép vec2 trong vec. Nó khá nhanh. Để xử lý 20.000.000 yếu tố thuật toán này chỉ mất 0.8 giây!

tôi đã làm điều tương tự với các erase-phương pháp, và phải mất rất nhiều và rất nhiều thời gian:

Erase-Version (10k elements) : 0.04 sec 
Erase-Version (100k elements) : 0.6 sec 
Erase-Version (1000k elements): 56 sec 
Erase-Version (10000k elements): ...still calculating (>30 min) 
+5

cách này trả lời câu hỏi? –

+3

Thú vị, nhưng không liên quan đến câu hỏi! – Roddy

+0

Sẽ không một thuật toán tại chỗ nhanh hơn? – user202729

3

Trên thực tế, erase chức năng làm việc cho hai hồ sơ:

  • Loại bỏ một phần tử đơn lẻ

    iterator erase (iterator position); 
    
  • Xóa một loạt các thành phần

    iterator erase (iterator first, iterator last); 
    

Kể từ std :: vec.begin() đánh dấu sự khởi đầu của container và nếu chúng ta muốn xóa các yếu tố thứ i trong vector của chúng tôi, chúng tôi có thể sử dụng:

vec.erase(vec.begin() + index); 

Nếu bạn nhìn kỹ, vec.bắt đầu() chỉ là một con trỏ đến vị trí bắt đầu của vector của chúng tôi và thêm giá trị của i để nó increments con trỏ để tôi vị trí, vì vậy bạn có thể truy cập vào con trỏ tới phần tử thứ i theo:

&vec[i] 

Vì vậy, chúng ta có thể viết:

vec.erase(&vec[i]); // To delete the ith element 
2

để xóa một yếu tố sử dụng theo cách sau:

 1 // declaring and assigning array1 
    2 std:vector<int> array1 {0,2,3,4}; 
    3 
    4 // erasing the value in the array 
    5 array1.erase(array1.begin()+n); 

cho tầm nhìn rộng hơn bạn có thể ghé thăm: - http://www.cplusplus.com/reference/vector/vector/erase/

0

Câu trả lời trước cho rằng bạn luôn là có chỉ mục đã ký. Đáng buồn thay, std::vector sử dụng size_type để lập chỉ mục và difference_type cho số học của vòng lặp, do đó chúng không hoạt động cùng nhau nếu bạn đã bật "-Wconversion" và bạn bè. Đây là một cách khác để trả lời câu hỏi, trong khi vẫn có thể xử lý cả hai chữ ký và unsigned:

Để loại bỏ:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type> 
void remove(std::vector<T> &v, I index) 
{ 
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index); 
    v.erase(iter); 
} 

Chịu:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type> 
T take(std::vector<T> &v, I index) 
{ 
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index); 

    auto val = *iter; 
    v.erase(iter); 

    return val; 
} 
2

Nếu bạn có một vector có thứ tự bạn có thể tận dụng thực tế là nó không có thứ tự và sử dụng thứ mà tôi đã thấy từ Dan Higgins tại CPPCON

template< typename TContainer > 
static bool EraseFromUnorderedByIndex(TContainer& inContainer, size_t inIndex) 
{ 
    if (inIndex < inContainer.size()) 
    { 
     if (inIndex != inContainer.size() - 1) 
      inContainer[inIndex] = inContainer.back(); 
     inContainer.pop_back(); 
     return true; 
    } 
    return false; 
} 

Vì thứ tự danh sách không quan trọng, chỉ cần lấy phần tử cuối cùng trong danh sách và sao chép nó trên đầu mục bạn muốn xóa, sau đó bật và xóa mục cuối cùng.

Các vấn đề liên quan