【问题标题】:How to Move certain elements of std::vector to a new index within the vector?如何将 std::vector 的某些元素移动到向量中的新索引?
【发布时间】:2018-01-08 21:12:29
【问题描述】:

我正在移植一些我写的旧的手卷数组处理类,现在使用标准库容器。我在移植时遇到问题的一种方法是我称之为“ChangeRecordOrder”的方法,因为没有更好的术语。我需要一个标准库替换。

它的定义是:

template <class T>
void ChangeRecordOrder( std::vector<T> IN OUT &inputVector, 
                        uint newInsertIndex, 
                        std::vector<uint> IN const &indexesToMoveToNewIndex );

例如(伪代码):

MyVector<uint> = {0,10,20,30,40,50,60,70,80,90}
IndexesToMove = {2,4}
NewIndex = 6

After call to ChangeRecordOrder( MyVector, NewIndex, IndexesToMove ):

MyVector<uint> == {0,10,30,50,20,40,60,70,80,90}

请注意,2 和 4(20 和 40)处的元素被移动到原始向量的索引 6(在 60 之前)。

当然我想在原地做这个,而不是使用另一个临时向量。我也不介意在调用之前需要对 IndexesToMove 向量进行排序的要求。

我找不到用于此的标准库算法。我之前在原始内存上使用的算法并没有使用 c++ 移动语义。

谢谢!

【问题讨论】:

  • 我在伪代码之后添加了一条注释,希望可以解决问题。如果不是,请告诉我,我会尽力澄清。
  • @ScottKemp 这是一个非常具体的操作。你可以通过一系列std::rotate来实现它。
  • 实际上,这可以通过单个 std::stable_partition 来完成。哪个更有效取决于所涉及的不同尺寸
  • @MooingDuck:就地?怎么样?
  • @ScottKemp:“请注意,2 和 4(20 和 40)处的元素已移至原始向量的索引 6”它们似乎位于索引 5。

标签: c++ algorithm sorting standard-library


【解决方案1】:

您正在寻找std::rotate

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

int main() {
    std::vector<int> values{0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
    std::vector<size_t> indexes_to_move{2,4};
    size_t destination_index = 6;
    if(destination_index > values.size()) throw std::runtime_error("Come on, bro.");

    for(auto const& val : values) std::cout << val << ',';
    std::cout << std::endl;

    for(size_t _index = 0; _index < indexes_to_move.size(); _index++) {
        size_t index = indexes_to_move[indexes_to_move.size() - _index - 1]; //We need to iterate in reverse.
        if(index >= values.size()) throw std::runtime_error("We dun goofed.");
        if(index >= destination_index) throw std::runtime_error("We goofed in a different way.");

        std::rotate(values.begin() + index, values.begin() + index + 1, values.begin() + destination_index);
        destination_index--;
    }

    for(auto const& val : values) std::cout << val << ',';
    std::cout << std::endl;
    return 0;
}

根据ideone.com,这会产生以下输出:

0,10,20,30,40,50,60,70,80,90,
0,10,30,50,20,40,60,70,80,90,

【讨论】:

  • 只是说,这会导致比理想解决方案更多的复制。
  • @MikeMB 也许您可以教育我们并提供更好的解决方案。
  • @FrançoisAndrieux:好的,我提供了不同的解决方案。你可以自己决定是否更好。我只是想指出所提供解决方案的一个缺点。
  • @Xirema,感谢您的努力。您的代码也传递了给定的输入,但由于以下输入而失败:Indexs To Move={5} destination_index = 1
【解决方案2】:

这是一个解决方案,它最多将每个未选择的元素移动一次:

#include <vector>
#include <algorithm>

using namespace std;
int main() {
    vector<int> values{0, 10, 20, 30, 40, 50, 60, 70, 80, 90};
    vector<size_t> IndexesToMove{2,4};
    size_t NewIndex = 6;
    //check that your indices are sorted, non-empty, in the correct range, etc.

    // move one element in front of the next element to move
    // then move those two elements in front of the next element to move
    // ...
    auto it_next = values.begin() + IndexesToMove.front();
    for(size_t i = 0; i < IndexesToMove.size() -1; i++) {
        auto it_first = it_next - i;
        it_next = values.begin() + IndexesToMove[i+1];
        rotate(it_first, it_first + i + 1 , it_next);
    }

    // move the collected elements at the target position
    rotate(it_next - IndexesToMove.size() + 1, it_next + 1, values.begin() + NewIndex);
}

不可否认,可读性不太好。它可能会通过更好的变量名称和/或将其中一些放入单独的函数来改进

【讨论】:

  • 感谢您的贡献,它通过了给定输入的测试,但最终旋转与以下输入存在范围错误... Indexs To Move={5} NewIndex = 1
【解决方案3】:

以上两个示例在输入不同时存在问题(如上所述)。我制定了处理不同情况的算法,它通过了我的单元测试。它可能可以提高速度。

template <class CONTAINER_TYPE>
void ChangeRecordOrder( CONTAINER_TYPE IN OUT &values,
                        uint newIndex,
                        std::vector<uint> IN const &indexesToMove )
   {
   // Make a copy of the indexesToMove as we need to do fixups to them in certain cases    
   std::vector<uint> temporaryIndexes = indexesToMove;

   for ( uint i=0; i<temporaryIndexes.size(); i++ )
      {
      uint indexToMove = temporaryIndexes[i];

      if ( indexToMove < newIndex )
         {
         uint leftIndex  = indexToMove;
         uint rightIndex = newIndex -1;
         uint newFirst   = leftIndex + 1;
         std::rotate( values.begin() + leftIndex, values.begin() + newFirst,  values.begin() + rightIndex +1);

         // fix up indexes
         for( uint j=i+1; j<temporaryIndexes.size(); j++ )
            {
            uint &futureIndex = temporaryIndexes[j];
            if ( futureIndex > leftIndex && futureIndex <=rightIndex )
               --futureIndex;
            }
         }
      else if ( indexToMove > newIndex )
         {
         uint leftIndex  = newIndex;
         uint rightIndex = indexToMove;
         uint newFirst   = indexToMove;
         std::rotate( values.begin() + leftIndex, values.begin() + newFirst,  values.begin() + rightIndex +1);

         // fix up indexes
         for( uint j=i+1; j<temporaryIndexes.size(); j++ )
            {
            uint &futureIndex = temporaryIndexes[j];
            if ( futureIndex > leftIndex && futureIndex <=rightIndex )
               ++futureIndex;
            }

         ++newIndex;
         }

      }
   }

【讨论】:

  • 记住“以上两个”不再是这个答案所指的。
【解决方案4】:
template <typename t> void move(std::vector<t>& v, size_t oldIndex, size_t newIndex)
{
    if (oldIndex > newIndex)
        std::rotate(v.rend() - oldIndex - 1, v.rend() - oldIndex, v.rend() - newIndex);
    else        
        std::rotate(v.begin() + oldIndex, v.begin() + oldIndex + 1, v.begin() + newIndex + 1);
}

测试:https://coliru.stacked-crooked.com/a/5c31007000b9eeba

int main()
{
    std::vector<int> v{ 3, 4, 5, 6, 7, 8, 9  };  

    move(v, 1, 4);
    move(v, 4, 1);
    move(v, 3, 3);
}

输出:

move 1 to 4:  3   [4]   5    6    7    8    9  
     result:  3    5    6    7   [4]   8    9  

move 4 to 1:  3    5    6    7   [4]   8    9  
     result:  3   [4]   5    6    7    8    9  

move 3 to 3:  3    4    5   [6]   7    8    9  
     result:  3    4    5   [6]   7    8    9  

【讨论】:

  • 非常好的代码,非常感谢!应该是答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-26
  • 2013-03-13
  • 2021-10-17
相关资源
最近更新 更多