【问题标题】:How to keep removing values at odd places from a vector until we're just left with one?如何继续从向量中删除奇数位置的值,直到我们只剩下一个?
【发布时间】:2019-09-17 09:40:49
【问题描述】:

令 J 为具有 (0, 1, 1, 2, 3, 5, 8, 3, 1) 值的向量

我想继续从向量中删除奇数位置的所有值,直到 J 只剩下一个元素。

(0, 1, 1, 2, 3, 5, 8, 3, 1) => (1, 2, 5, 3) => (2,3) => (3)

我如何做到这一点?

我的做法:

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

using namespace std;

int main()
{
  int J[] = {0, 1, 1, 2, 3, 5, 8, 3, 1};
  int len=sizeof(J)/sizeof(J[0]);
  vector<int> Jvec;
  for (int i=0; i<len; i++) {  //Putting the values of array into Jvec
    Jvec.push_back(J[i]);
  }

  while (Jvec.size()!=1) {
    vector<int> oddJ;
    for (int k=0; k<Jvec.size(); k=k+2) {  //Storing values of odd places
      oddJ.push_back(Jvec[k]);
    }

    for (int i=0; i<oddJ.size(); i++) {
      Jvec.erase(remove(Jvec.begin(), Jvec.end(), oddJ[i]), Jvec.end()); //removing values from vector by value, not index
    }
  }
  cout << Jvec[0];
  return 0;
}

我得到的是 5 而不是 3。我的逻辑有问题吗?我哪里错了?

【问题讨论】:

  • 您是否尝试使用调试器?每次擦除调用后向量的值是多少?
  • 我可以简单地说答案将是倒数第二个数字吗
  • 奇数地方实际上是指从偶数索引?在 C++ 中,索引从 0 开始。
  • 另一种方法是找到 2 的最大幂,小于或等于该位置的元素和打印元素的数量。

标签: c++ arrays algorithm vector lambda


【解决方案1】:

其实,你的问题有一个非常简洁快速的解决方案。

由于您希望在每个步骤中删除第 1、第 3、第 5 等索引,因此数组是 基于 0 的,您实际上应该删除第 0、第 2、第 4 个索引每一步。

这里有一些观察结果,对寻找答案很有帮助:

  • 在每一步中,数组都会减半,这意味着在O(logN) 步骤中,您将获得大小为1 的数组。

  • 在最后一步,搜索到的值有索引0

  • 在每一步(但最后一步)中,搜索到的值都有一个奇数索引,因此它会被保留,直到数组大小变为1

所以,在最后一步,搜索值的索引是0,在上一步它有索引1,在前一步它有索引3等等。

我们可以清楚地看到,从0开始,索引加倍,然后1被添加,所以它是奇数而不是被删除。这恰好发生log n 次。

所以,为了跟踪我们最终应该打印的数字的索引,我们可以这样做:

int[] values = {0, 1, 1, 2, 3, 5, 8, 3, 1};
int n = values.length, index = 0;
while (n > 1) {
  index = 2 * index + 1;
  n /= 2;
}
System.out.println(values[index]);

我用 Java 编写了代码,但您可以轻松地将其调整为您选择的语言。

这给出了O(logN) 的整体复杂度,其中Nvalues 数组的大小。

【讨论】:

  • 这启发了我:必须有一个找到索引的数学公式......它是:2^floor(log2(n)) - 1,或者更适合计算机使用的1U &lt;&lt; log2(n) - 1。太糟糕了,我也没有找到better than O(log(n)) 来计算log2(n)(除了查找表和硬件内在函数......)。
【解决方案2】:

虽然已经提供了正确的解决方案,但可以使用更简单的代码来实现相同的结果:

#include <iostream>
#include <vector>

using std::cout;
using std::vector;

int main()
{
   vector<int> v = { 0, 1, 1, 2, 3, 5, 8, 3, 1 };

   while (v.size() > 1)
   {
        for(size_t i = 0; i < v.size(); i++)
        {
            v.erase(v.begin() + i);
        }
        for (const auto &i : v) cout << i << ' ';
        cout << '\n';
   }
   return 0;
}

【讨论】:

  • 我没有看到在您的代码中删除偶数索引的参考
  • @P.Pawar 这就是让它变得更简单的原因。注意这一点:一旦你删除了元素#1,向量就缩小了,现在原来是#3的元素已经移动到了#2的位置。因此,您所要做的就是顺序删除索引 i 处的元素(每次迭代递增 1),直到到达向量的末尾。运行代码,你会看到。
  • @P.Pawar 这很容易理解,但是效率不高:每当您删除其中一个元素时,所有后续元素都会移动一个位置到前面。将这一事实纳入计算,内循环将得到O(n²) 复杂度,因此与外循环一起,我们将得到O(log(n)*n²) 复杂度。在这方面,其他解决方案更胜一筹……
【解决方案3】:

在 C++ 中,数组和标准容器的索引从 0 开始。所以说起来会更正确

如何继续从向量中删除 even 位置的值,直到 我们只剩下一个?

这是一个演示程序,展示了它是如何完成的。

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

int main() 
{
    std::vector<int> v = { 0, 1, 1, 2, 3, 5, 8, 3, 1 };

    while ( v.size() > 1 )
    {
        // comment these two statements in the release version
        for ( const auto &item : v ) std::cout << item << ' ';
        std::cout << '\n';

        std::vector<int>::size_type i = 0;

        auto even_pos = [&i] ( const auto & ) { return i++ % 2 == 0; };

        v.erase( std::remove_if( std::begin( v ), std::end( v ), even_pos ),
                 std::end( v ) );
    }

    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

程序输出是

0 1 1 2 3 5 8 3 1 
1 2 5 3 
2 3 
3 

至于你的代码然后这个调用

Jvec.erase(remove(Jvec.begin(), Jvec.end(), oddJ[i]), Jvec.end());

从向量Jvec 中不仅删除一个元素,而且删除所有等于oddJ[i] 的元素。

例如,如果oddJ[i] 等于3,那么47 位置上的两个元素将从向量Jvec 中删除。

【讨论】:

  • 你能解释一下“auto even_pos = [&i] ( const auto & ) { return i++ % 2 == 0; }; ”在做什么吗?
  • @P.Pawar 这是一个 lambda 表达式。它检查向量的当前索引是否为偶数。
  • 我收到一个错误:'even_pos' 没有命名类型
  • @P.Pawar 您是否复制并粘贴了演示程序而不进行自己的更改?
  • 我只复制了 while 循环的内容。我目前是学习 STL 和指针的初学者。所以我在尝试理解它时遇到了一些困难
【解决方案4】:

回答你的问题,你哪里出错了:std::remove 删除给定值的 all 元素,所以如果你第二次在“1”上应用它,它实际上会删除两个元素,与后面的“3”相同。见Erase-remove idiom。要了解如何解决您的问题,请参阅 Renat 的评论:How to remove odd positions from a list?

【讨论】:

    猜你喜欢
    • 2019-06-10
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多