删除数组中的偶数并移动元素

Deleting an even number in an array and shift the elements

我正在尝试编写一个代码,其中对偶数进行研究,然后删除偶数,然后移动所有其他元素。

i是偏移量,是数组中元素的实际位置。

k是偶数在数组中的位置

int k;
for(i=0; i < N; i++)
{
    if(Array[i] % 2 == 0)
    {
       for(k=i+1; k < N; k++)
       {
            Array[k-1] = Array[k];
       }
       N--;
    }
}

Array=[2,10,3,5,8,7,3,3,7,10]偶数应该去掉,但是一个10 留在 Array=[10,3,5,7,3,3,7].

现在我花了 3 个多小时来找出我的代码中的错误。

您可以使用 std::vector 和标准函数 std::erase_if + 向量 erase 函数来执行此操作:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> Array = {2, 10, 3, 5, 8, 7, 3, 3, 7, 10};

    auto it = std::remove_if(
        Array.begin(),
        Array.end(),
        [](int x) { return (x & 1) == 0 && x != 10; }
    );

    Array.erase(it, Array.end());

    for(int x : Array) {
        std::cout << x << "\n";
    }
}

输出:

10
3
5
7
3
3
7
10

编辑:困难的做法:

#include <iostream>

int main() {
    int Array[] = {2, 10, 3, 5, 8, 7, 3, 3, 7, 10};
    size_t N = sizeof(Array) / sizeof(int);

    for(size_t i = 0; i < N;) {
        if((Array[i] & 1) == 0 && Array[i] != 10) {
            for(size_t k = i + 1; k < N; ++k) {
                Array[k - 1] = Array[k];
            }
            --N;
        } else
            ++i; // only step i if you didn't shift the other values down
    }
    for(size_t i = 0; i < N; ++i) {
        std::cout << Array[i] << "\n";
    }
}

或更简单:

#include <iostream>

int main() {
    int Array[] = {2, 10, 3, 5, 8, 7, 3, 3, 7, 10};
    size_t N = sizeof(Array) / sizeof(int);
    size_t k = 0;

    for(size_t i = 0; i < N; ++i) {
        if((Array[i] & 1) || Array[i] == 10) {
            // step k after having saved this value 
            Array[k++] = Array[i];
        }
    }
    N = k;
    for(size_t i = 0; i < N; ++i) {
        std::cout << Array[i] << "\n";
    }
}

C++ 中惯用的解决方案是使用 STL 算法。

这个例子使用了 C 风格的数组。

int Array[100] = {2,10,3,5,8,7,3,3,7,10};
int N = 10;

// our remove_if predicate
auto removeEvenExceptFirst10 = [first10 = true](int const& num) mutable {
    if (num == 10 && first10) {
        first10 = false;
        return false;
    }

    return num % 2 == 0;
};

auto newN = std::remove_if(
    std::begin(Array), std::begin(Array) + N,
    removeEvenExceptFirst10
);

N = std::distance(std::begin(Array), newN);

Live demo

这似乎是某种家庭作业或学校作业。那么发布的代码的实际问题是什么?

就是当你在索引i处删除一个偶数时,你把原来在索引i + 1处的数字放到索引i中。然后继续外循环迭代,这将检查索引 i + 1,这是数组中原始 i + 2 位置的数字。所以从 Array[i + 1] 开始,现在在 Array[i] 的数字永远不会被检查。

解决此问题的一个简单方法是在递减 N 时递减 i

虽然已经回答了,但我看不出人们通过双 for 循环驱动它的原因,一遍又一遍地重复移动数据,每次减少。

我完全同意所有关于使用容器的建议。此外,算法解决方案不需要容器(您 可以 在本机数组上使用它),但容器仍然使它更容易和更清洁。也就是说...

我在上面的一般评论中描述了这个算法。你不需要嵌套循环。您需要一个读指针和一个写指针。 就是这样.

#include <iostream>

size_t remove_even(int *arr, size_t n)
{
    int *rptr = arr, *wptr = arr;

    while (n-- > 0)
    {
        if (*rptr % 2 != 0)
            *wptr++ = *rptr;
        ++rptr;
    }
    return (wptr - arr);
}

int main()
{
    int arr[] = { 2,10,3,5,8,7,3,3,7,10 };
    size_t n = remove_even(arr, sizeof arr / sizeof *arr);

    for (size_t i=0; i<n; ++i)
        std::cout << arr[i] << ' ';
    std::cout << '\n';
}

输出

3 5 7 3 3 7 

如果您认为这没有什么不同,我邀请您用一百万个随机整数填充一个数组,然后尝试两种解决方案(嵌套 for 循环方法与您在上面看到的方法)。


在原生数组上使用 std::remove_if

仅为了清楚起见,上面的代码基本上完成了标准算法 std::remove_if 所做的工作。我们需要做的就是提供迭代器(数组偏移量和大小会很好地工作),并知道如何解释结果。

#include <iostream>
#include <algorithm>

int main()
{
    int arr[] = { 2,10,3,5,8,7,3,3,7,10 };
    auto it = std::remove_if(std::begin(arr), std::end(arr),
                             [](int x){ return x%2 == 0; });

    for (size_t i=0; i<(it - arr); ++i)
        std::cout << arr[i] << ' ';
    std::cout << '\n';
}

相同的结果。