如何在不制作数组副本的情况下从C ++数组中删除每一个值?

时间:2016-12-07 12:43:51

标签: c++ arrays

问题:我希望数组A[6] = {6, 5, 4, 3, 2, 1}A[6] = {5, 3, 1, 1, 1, 1}。换句话说 - "删除"每隔一个值从0开始,并将所有其他值移到左侧。

我的尝试:

为此,我将使用此代码,其中a - 数组A的相关部分的长度(具有未删除元素的部分),ind - 我想要删除的值的索引。

for (int j = ind; j < n; j++)
    A[j] = A[j+1];

但是,我无法使用这样的代码来实现这一点:

void deleting(int A[], int& a, int ind){
    for (int j = ind; j < a; j++)
        A[j] = A[j+1];

    a--;
}

int A[6] = {6, 5, 4, 3, 2, 1};
a = 6

for (int i = 0; i < a; i+=2)
    deleting(A, a, i);

运行此代码后,我得到了A[6] = {5, 4, 2, 1, 1507485184, 1507485184}。因此,它删除了索引0,3处的元素。为什么删除第3个索引?

8 个答案:

答案 0 :(得分:8)

有两种方法可以做到这一点:

  1. 遍历数组,将每个偶数n-i的最后i个元素前移一个位置,或者

  2. 弄清楚最终的状态,然后直截了当地说。最终状态是第一个n/2个地方array[i]=array[2*i + 1],最后n/2个地方只是最后一个元素的副本。

  3. 第一种方法是您要求的方法,但它会执行多次冗余复制操作,第二种方法可以避免。

    关于您的实现问题,请检查j=n-1时发生的情况,并记住A[n]不是数组的有效元素。 我建议无论如何都要使copy-everything-forward操作成为自己的函数(或者你可以使用memcpy)

答案 1 :(得分:4)

对于这些类型的问题(就地数组操作),将一个索引或指针保存到数组中是个好主意,因为&#34;读取&#34;另一个你在写作的地方。&#34;例如:

void odds(int* a, size_t len) {
  int* writep = a;
  int* readp = a + 1;
  while (readp < a + len) { // copy odd elements forward
    *writep++ = *readp;
    readp += 2;
  }
  while (writep < a + len - 1) { // replace rest with last
    *writep++ = a[len - 1];
  }
}

答案 2 :(得分:2)

只是为了踢,这是一个不使用循环的版本:

#include <algorithm>
#include <cstddef>
#include <iostream>
#include <iterator>
#include <utility>
#include <initializer_list>

template <typename T, std::size_t Size>
std::ostream& print(std::ostream& out, T const (&array)[Size]) {
    out << "[";
    std::copy(std::begin(array), std::end(array) -1,
              std::ostream_iterator<T>(out, ", "));
    return out << std::end(array)[-1] << "]";
}

template <std::size_t TI, std::size_t FI, typename T, std::size_t Size>
bool assign(T (&array)[Size]) {
    array[TI] = array[FI];
    return true;
}

template <typename T, std::size_t Size,
          std::size_t... T0>
void remove_even_aux(T (&array)[Size],
                     std::index_sequence<T0...>) {
    bool aux0[] = { assign<T0, 2 * T0 + 1>(array)... };
    bool aux1[] = { assign<Size / 2 + T0, Size - 1>(array)... };
}

template <typename T, std::size_t Size>
void remove_even(T (&array)[Size]) {
    remove_even_aux(array, std::make_index_sequence<Size / 2>());
}

int main() {
    int array[] = { 6, 5, 4, 3, 2, 1 };
    print(std::cout, array) << "\n";
    remove_even(array);
    print(std::cout, array) << "\n";
}

答案 3 :(得分:1)

如果C ++算法是一个选项,我倾向于默认选择它们:

auto *const end_A = A + (sizeof(A)/sizeof(*A));
auto *new_end = std::remove_if(
    A, end_A,
    [&A](int const& i) { return (&i - A) % 2 == 0; });

// Now "erase" the remaining elements.
std::fill(new_end, end_A, 0);

std::remove_if算法只移动与谓词不匹配的元素(在我们的例子中,测试地址是MOD(2)= 0),并std::move将它们移到最后。这是到位的。新的&#34;结束&#34;是return,然后我将其索引并将元素设置为0。

答案 4 :(得分:0)

因此,如果它必须是一个数组,解决方案将是这样的:

{{1}}

你首先遍历数组的前半部分,每隔一个数字“移动”到前面,然后你循环通过其余数字填充最后一个数字。

答案 5 :(得分:0)

对于其他答案的更多功能版本:

#include <iostream>

template<typename InputIt, typename T>
void filter(InputIt begin, InputIt end, T const& defaultvalue)
{
    InputIt fastforward = begin;
    InputIt slowforward = begin;
    fastforward++; // starts at [1], not [0]
    while (fastforward < end)
    {
        *slowforward = *fastforward;
        ++slowforward;
        ++ ++fastforward;
    }
    while (slowforward < end) // fill with default value
    {
        *slowforward++ = defaultvalue;
    }
}

int main()
{
    int A[6] = {6, 5, 4, 3, 2, 1};

    std::cout << "before: ";
    for (auto n : A)
        std::cout << n << ", ";
    std::cout << std::endl;

    filter(A, A+6, 1);

    std::cout << "after: ";
    for (auto n : A)
        std::cout << n << ", ";
    std::cout << std::endl;
}

输出:

before: 6, 5, 4, 3, 2, 1, 
after: 5, 3, 1, 1, 1, 1, 

这适用于std::array<bool>std::vector<std::string>std::unordered_set<void*>::iterator等。

答案 6 :(得分:0)

执行此操作的常用方法是保留两个索引:一个用于您正在修改的条目,另一个用于您打算处理的条目

const auto size = sizeof(A) / sizeof(int);

// N.b. if size == 1 entire array is garbage
int i = 0;
for (int nv = 1; nv < size; ++i, nv += 2)
  A[i] = A[nv];
--i;
// Significative array is now [0;N/2[, fill with last now
for (int j = i + 1; j < size; ++j)
  A[j] = A[i];

这授予了就地修改方式。

答案 7 :(得分:0)

您可以合并std::remove_ifstd::fill来执行此操作

示例代码:

#include <algorithm>
#include <iostream>
#include <iterator>

int main()
{
   int A[6] = {6, 5, 4, 3, 2, 1};

   auto endX = std::remove_if(std::begin(A),std::end(A),[&A](const int& i){return (&i-A)%2==0;});
   if(endX!=std::begin(A))//in case nothing remained, although not possible in this case
      std::fill(endX,std::end(A),*(endX-1));
   //else /*something to do if nothing remained*/

   for(auto a : A)std::cout<<a<<' ';
}