Làm cách nào để xóa phần tử khỏi std :: vector <> theo chỉ mục?


508

Tôi có một std :: vector <int> và tôi muốn xóa phần tử thứ n. Làm thế nào để làm điều đó?

std::vector<int> vec;

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

vec.erase(???);

4
Xem xét sử dụng một std :: deque cung cấp chèn và xóa ở cả hai đầu.
Dario

40
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 tồi. Có rất nhiều 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ử khỏi một vectơ có thể tốn kém - đặc biệt nếu vectơ 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.

6
Ví dụ: nếu bạn có một ứng dụng đồ họa nơi bạn hiển thị một "danh sách" những thứ mà bạn chèn / xóa mọi thứ một cách 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 vài thứ lần mỗi phút. Vì vậy, thực hiện "danh sách" như một vectơ có lẽ là một lựa chọn tốt hơn về mặt hiệu quả tổng thể.
Michel Billaud

Câu trả lời:


705

Để xóa một yếu 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 phần 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);

50
Cũng lưu ý nhị phân operator+không nhất thiết phải định nghĩa cho vòng lặp trên các loại thùng chứa khác, như list<T>::iterator(bạn không thể làm list.begin() + 2trên một std::list, bạn phải sử dụng std::advancecho điều đó)
bobobobo

bạn có nói rằng "+1" là yếu tố đầu tiên myVector [0] hoặc vị trí thực tế myVector [1]
K - Độc tính trong SO đang tăng lên.

2
Với trước, bạn phải lưu iterator trong một biến. Nếu bạn sử dụng std :: next, bạn có thể thực hiện trong một dòng: vec.erase (next (started (vec), 123));
dani

8
Cảm ơn tất cả những người đã trả lời. Chúng ta nghĩ gì về một thiết kế lớp khi một thao tác đơn giản như xóa một phần tử, yêu cầu một phần tử phải đến StackOverflow?
Pierre

5
@Pierre vì chỉ số bằng số của một phần tử cụ thể không phải là mô hình chính của truy cập, iterator là. Tất cả các hàm xem xét các phần tử của bộ chứa đều sử dụng các trình lặp của bộ chứa. Ví dụ:std::find_if
Caleth

212

Phương thức xóa trên std :: vector bị quá tải, vì vậy có thể gọi nó rõ ràng hơn

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

khi bạn chỉ muốn xóa một yếu tố duy nhất.


3
Nhưng vấn đề đó xuất hiện bất kể bạn có bao nhiêu yếu tố.
Zyx 2000

15
nếu chỉ có một yếu tố, chỉ mục là 0 và do đó bạn nhận vec.begin()được giá trị nào là hợp lệ.
Anne Quinn

28
Tôi ước ai đó đã đề cập rằng vec.erase(0)nó không hoạt động, nhưng vec.erase(vec.begin()+0)(hoặc không có +0) thì không.
Mặt

@qrtLs vec.erase(0)thực sự có thể biên dịch nếu 0tình cờ được hiểu là hằng số con trỏ null ...
LF

57
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
Max, điều gì làm cho chức năng đó tốt hơn: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }Tôi không nói là tốt hơn, chỉ hỏi về lợi ích 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.

13
@JoeyvG: Vì a vector<T>::iteratorlà một trình vòng lặp truy cập ngẫu nhiên, phiên bản của bạn vẫn ổn và có thể rõ ràng hơn một chút. Nhưng phiên bản mà Max đã đăng sẽ hoạt động tốt nếu bạn thay đổi vùng chứa sang một phiên bản khác không hỗ trợ các trình vòng lặp truy cập ngẫu nhiên
Lily Ballard

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

Cách tiếp cận tốt hơn nhiều vì nó không phụ thuộc vào bên trong của container.
BartoszKP

std :: trước chỉ cần thiết nếu bạn nghĩ rằng đây sẽ không phải là một vectơ tức là một danh sách. Nhưng như bạn đã chỉ định rằng ở đây, toán tử + sẽ không đơn giản hơn? Theo stackoverflow.com/questions/1668088/ này, có khả năng đạt được hiệu suất với nhà điều hành +
Neil McGill

14

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

  1. Xóa phần tử đơn:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Xóa phạm vi của các yếu tố:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element

7
Đây là một câu trả lời trùng lặp gần 7 năm sau câu trả lời được chấp nhận. Xin đừng làm điều này.
AlastairG

10

Trên thực tế, erasechức năng hoạt động cho hai hồ sơ:

  • Loại bỏ một yếu tố

    iterator erase (iterator position);
  • Loại bỏ một loạt các yếu tố

    iterator erase (iterator first, iterator last);

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

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

Nếu bạn nhìn kỹ, vec.begin () chỉ là một con trỏ đến vị trí bắt đầu của vectơ của chúng ta và thêm giá trị của i vào nó sẽ tăng con trỏ đến vị trí i, vì vậy thay vào đó chúng ta có thể truy cập con trỏ đến phần tử thứ i bằng cách:

&vec[i]

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

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

6
-1 Dòng cuối cùng không biên dịch (ít nhất là trong VS2017). Mã này giả định rằng vector :: iterator hoàn toàn có thể xây dựng được từ một con trỏ thô, mà tiêu chuẩn không yêu cầu.
CuriousGeorge

1
Điều này đặc biệt đúng đối với các trình lặp gỡ lỗi
Nishant Singh

9

Nếu bạn có một vectơ không 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ứ 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 thành vấn đề, chỉ cần lấy phần tử cuối cùng trong danh sách và sao chép nó lên trên cùng của mục bạn muốn xóa, sau đó bật và xóa mục cuối cùng.


Tôi nghĩ rằng đây là câu trả lời tốt nhất nếu vector không có thứ tự. Nó không dựa vào giả định iterator + indexsẽ thực sự đưa bạn trở lại vị trí lặp ở chỉ số đó, điều này không đúng với tất cả các container lặp. Nó cũng phức tạp liên tục thay vì tuyến tính bằng cách tận dụng con trỏ trở lại.
theferrit32

1
Điều này hoàn toàn cần phải được thêm vào lib std unordered_removeunordered_remove_iftrừ khi nó đã xảy ra và tôi đã bỏ lỡ nó, điều này xảy ra ngày càng thường xuyên hơn trong những ngày này :)
Will Crawford

Nếu muốn đề xuất sử dụng chuyển nhượng hoặc trao đổi thay vì chuyển nhượng sao chép.
Carsten S

std::removesắp xếp lại vùng chứa sao cho tất cả các thành phần cần loại bỏ ở cuối, không cần thực hiện thủ công như thế này nếu bạn đang sử dụng C ++ 17.
keith

@keith std::removegiúp thế nào? cppreference tuyên bố rằng ngay cả trong C ++ 17, tất cả các removetình trạng quá tải đều yêu cầu một vị từ và không có chỉ số nào.
Paul Du Bois

4

Nếu bạn làm việc với các vectơ lớn (kích thước> 100.000) và muốn xóa nhiều phần tử, tôi 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 lấy mỗi số trong vec không thể chia cho 3 và sao chép nó vào vec2. Sau đó, nó sao chép vec2 trong vec. Nó là khá nhanh. Để xử lý 20.000.000 phần 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 phương pháp xóa, và nó mất 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)

6
Làm thế nào để trả lời câu hỏi này?
Regis Portalez

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

Không phải là một thuật toán tại chỗ nhanh hơn?
dùng202729

2
đó là std :: remove_if (+ erase)
Rịa


3

Tôi đề nghị đọc nó vì tôi tin rằng đó là những gì bạn đang tìm kiếm. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Nếu bạn sử dụng ví dụ

 vec.erase(vec.begin() + 1, vec.begin() + 3);

bạn sẽ xóa phần tử thứ n của vectơ nhưng khi bạn xóa phần tử thứ hai, tất cả các phần tử khác của vectơ sẽ bị dịch chuyển và kích thước vectơ sẽ là -1. Đây có thể là vấn đề nếu bạn lặp qua vectơ vì kích thước vectơ () đang giảm. Nếu bạn gặp vấn đề như liên kết được cung cấp này, hãy đề xuất sử dụng thuật toán hiện có trong thư viện C ++ tiêu chuẩn. và "xóa" hoặc "remove_if".

Hy vọng rằng điều này đã giúp


0

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

Để xóa:

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);
}

Để:

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;
}

0

Đây là một cách khác để làm điều này nếu bạn muốn xóa một phần tử bằng cách tìm giá trị này với giá trị của nó trong vector, bạn chỉ cần thực hiện điều này trên vector.

vector<int> ar(n);
ar.erase(remove(ar.begin(), ar.end()), (place your value here from vector array));

nó sẽ loại bỏ giá trị của bạn từ đây. cảm ơn


0

Còn cái này thì sao?

void squeeze(vector<int> &v)
{
    int j = 0;
    for (int i = 1; i < v.size(); i++)
        if (v[i] != v[j] && ++j != i)
            v[j] = v[i];
    v.resize(j + 1);
}

-4

cách nhanh nhất (đối với các cuộc thi lập trình theo độ phức tạp thời gian () = hằng số)

có thể xóa mục 100M trong 1 giây;

    vector<int> it = (vector<int>::iterator) &vec[pos];
    vec.erase(it);

và cách dễ đọc nhất: vec.erase(vec.begin() + pos);


2
Điều này rất không di động; nó sẽ hoạt động với libstdc ++, nhưng không phải libc ++ và không phải với MSVC. vector<int>::iteratorkhông nhất thiết phải giống nhưint *
Marshall Clow

2
Thật kinh tởm, tôi nghĩ rằng tôi sẽ thay đổi libstdc ++ để ngăn nó hoạt động.
Jonathan Wakely
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.