代码问题.使用矢量删除相同的数字,除了它的最后一个入口

Problem with code. Using vector delete same numbers except the last entrance of it

本文关键字:数字 入口 最后一个 问题 删除 代码      更新时间:2023-10-16

程序不起作用,但没有错误。我需要删除最后一个条目以外的数字。我们有 1 2 3 4 1 2 5 7 8 0 0 我们需要得到 3 4 1 2 5 8 8 0。

包含的库:vector,iostream。

'

vector<int> MyVector = {1,2,3,4,1,2,5,7,8,0,0};
vector<int>::iterator it;

for (int i = MyVector.size() - 1; i > 0; ++i)
{
  int x = MyVector.at(i);
    for ( it = MyVector.end() - 1; it >= MyVector.begin(); it--)
    {
        if (*it == x)
        {
           MyVector.erase(it);
        }
    }
}

从这个erase参考:

在擦除点之后使迭代器和引用失效

[强调我的]

这意味着您传递给erase的迭代器本身将失效,并且无法再使用。您需要使用erase返回的迭代器:

it = MyVector.erase(it);

对于初学者来说,这个循环

for (int i = MyVector.size() - 1; i > 0; ++i)
                                         ^^^

没有意义。至少你应该写

for (int i = MyVector.size() - 1; i > 0; --i)
                                         ^^^ 

使用该方法后erase当前的迭代器it变得无效。

您可以使用反向迭代器和标准算法std::find

这是一个演示程序

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
    std::vector<int> v = { 1, 2, 3, 4, 1, 2, 5, 7, 8, 0, 0 };
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
    for ( auto it = std::rbegin( v ); it != std::rend( v );  )
    {
        if ( std::find( it.base(), std::end( v ), *it ) != std::end( v ) )
        {
            it = std::reverse_iterator( v.erase( std::prev( it.base() ) ) );
        }
        else
        {
            ++it;
        }
    }
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
}

程序输出为

1 2 3 4 1 2 5 7 8 0 0 
3 4 1 2 5 7 8 0 

我认为这是你将要获得的结果。

您可以为此类任务编写通用算法。例如

template <typename ForwardIterator>
ForwardIterator remove_duplicates( ForwardIterator first, ForwardIterator last )
{
    for ( ; first != last; ++first )
    {
        last = std::remove( std::next( first ), last, *first );
    }
    return last;
}

并使用向量的反向迭代器调用它。

这是一个演示程序。

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
template <typename ForwardIterator>
ForwardIterator remove_duplicates( ForwardIterator first, ForwardIterator last )
{
    for ( ; first != last; ++first )
    {
        last = std::remove( std::next( first ), last, *first );
    }
    return last;
}
int main()
{
    std::vector<int> v = { 1, 2, 3, 4, 5, 4, 3, 2, 1 };
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
    v.erase( remove_duplicates( std::begin( v ), std::end( v ) ), std::end( v ) );
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
    std::cout << 'n';
    v.assign( { 1, 2, 3, 4, 5, 4, 3, 2, 1 } );
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
    v.erase( std::begin( v ), remove_duplicates( std::rbegin( v ), std::rend( v ) ).base() );
    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << 'n';
}

它的输出是

1 2 3 4 5 4 3 2 1 
1 2 3 4 5 
1 2 3 4 5 4 3 2 1 
5 4 3 2 1 

首先展示了如何删除除第一个条目之外的重复值。然后展示了如何删除除最后一个条目之外的重复值。

从您的代码中,我知道您想从向量中删除重复项。有很多方法可以做到这一点,例如,您可以通过以下方式创建std::set

std::set my_set(my_vector.begin(),my_vector.end());

该集合将不包含重复项,即基本上与您希望保留在向量中的元素相同。从 cpp 首选项

范围构造函数。使用以下内容构造容器 范围 [第一个,最后一个(。如果范围内的多个元素具有 比较等效,未指定插入哪个元素 (待LWG2844(。

如果你需要它,你可以把set变回一个vector,尽管一个不重复和排序的容器无论如何都是一个集合。如果不需要对结果进行排序,可以使用 std::unordered_set

>erase在移除的位置或之后使迭代器失效。这是使其工作的一种方法:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
int main()
{
    std::vector<int> MyVector{ 1, 2, 3, 4, 1, 2, 5, 7, 8, 0, 0 };
    auto beg{ MyVector.begin() };
    for (auto it{ MyVector.rbegin() }, rend{ MyVector.rend() }; it != rend; /* empty */)
    {
        it = std::reverse_iterator(std::remove(beg, std::next(it).base(), *it));
    }
    MyVector.erase(std::unique(beg, MyVector.end()), MyVector.end());
}

您的算法具有很大的复杂性:O(N^2( 用于检测重复项,以及擦除元素的复杂性。在最坏的情况下,它可能是 O(N^3(。

其他答案表明如何使用 std::vector 和迭代器的校正获得良好的结果,但没有解决复杂性问题

这个答案提供了一个O(nlogn(方法,使用std::vector。

方法:

1. sort indices according to array values
2. detect positions where to erase
3. build a new vector with kept values

重要的一点是使用稳定的排序,以确保只保留最后一个副本。

计划:

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
//  1. sort indices according to array values
//  2. detect positions where to erase
//  3. build a new vector with kept values
std::vector<int> remove_duplicate (const std::vector<int>& x) {
    std::vector<int> index (x.size());
    std::iota (index.begin(), index.end(), 0);
    std::stable_sort (index.begin(), index.end(), [&] (int i, int j) {return x[i] < x[j];});
    std::vector<bool> erase (x.size());
    for (int i = 0; i < x.size() - 1; ++i) {
        erase[index[i]] = x[index[i]] == x[index[i+1]];
    }
    erase[index[x.size()-1]] = false;
    std::vector<int> result;
    for (int i = 0; i < x.size() - 1; ++i) {
        if (!erase[i]) result.push_back(x[i]);
    }
    return result;
}
int main() {
    std::vector<int> MyVector = {1,2,3,4,1,2,5,7,8,0,0};
    auto result = remove_duplicate (MyVector);
    for (auto i: result) {
        std::cout << i << " ";
    }
    std::cout << "n";
    return 0;
}