【问题标题】:How can I shift an array to the right in c++?如何在 C++ 中将数组向右移动?
【发布时间】:2012-10-03 07:22:04
【问题描述】:

我正在尝试实现一个将对象数组移动到数组右侧的函数。我在互联网上发现的只是循环轮班的实现,但这不是我想要的。如果右侧实际上有空白空间,我想将元素向右移动。 假设您创建了一个对象 Packet 数组,其大小为 10

Packet* a[] = { p4 , p3 , p2 , p1, null, null, null, null, null, null }

移位功能只会将所有内容向右移动

{ null ,p4 , p3 , p2 , p1, null, null, null, null, null }

如果数组末尾有一个元素

{ p10, p9, p8, p7 ,p6 ,p5 ,p4 , p3 , p2 , p1}

这个函数不会改变任何东西。

 { p4 , p3 , p2 , p1, null, null, null, null, null, null }

我的实现想法是将数组复制到一个临时数组中, 擦除原始数组上的所有内容,然后复制到其中,但从位置 [1] 而不是位置 [0] 开始。但这似乎效率不高。

还有其他想法吗?

【问题讨论】:

  • 那么如果最后一个元素被填满了,就不要移位了?为什么不单独检查,然后快速std::rotate
  • 问题的标题是 C,但标签是 C++。克里斯评论中的建议仅是 C++。 Eduardo 在下面的回答在 C++ 中可能是一个非常糟糕的主意,我只认为它是 C。请具体说明语言。
  • 是的,我同意 Nathan,只在 C 中使用它
  • 你关心指向数组中间的指针/引用/迭代器会发生什么,当你做转变时?这在以下答案之间有所不同,其中一些答案的引用值发生了变化,其中一些答案的引用值没有改变,其中一个答案(在撰写本文时)引用本身无效。

标签: c++ arrays shift


【解决方案1】:

假设数组有 n 个元素:

if(a[n-1]==null){
   memmove(a+1, a, (n-1)*sizeof(Packet*));
   a[0]=null;
}

另一种方法是不移动数组的元素,而是移动用于访问它的索引。基本上,你要做的就是加 1 模 n。

【讨论】:

  • memcpy 如果对象重叠(如这里的情况),则具有未定义的行为。当然,您可以使用memmove。但是,如果他使用 C++,copy_backward 将是更好的选择。
  • 好点,詹姆斯。我已编辑解决方案以纳入您的建议。
  • 正如@JamesKanze 所说,memmove 是最安全的,并通过首先制作临时副本来防止 memcpy 未定义行为。感谢您修改您的答案。
  • @sgroh memmove 不会复制。它所做的(通常)是比较源地址和目标地址,如果目标地址高于源地址,则从最后一个移动到第一个。 (换句话说,它会根据最合适的那个自动在copycopy_backwards 之间进行选择。)
  • 另外:问题有一个指针数组,可以用memmove复制。此答案使用sizeof(Packet) 而不是sizeof(Packet*),因此它对Packet 对象数组而不是指针数组进行操作。您可以在 C++ 中的用户定义对象上使用 memmove,但前提是它们是 POD(在 C++03 中)或可简单复制(在 C++11 中) .
【解决方案2】:

从右到左迭代数组,将元素 n-1 分配给元素 n。当您到达第一个元素时,将null 分配给它。 (不管那是什么意思)

【讨论】:

  • 如果需要向右移动更长的时间,比如 10 个位置,则可以多次应用此循环。如果是这样的话,这仍然是最好的方法吗?
【解决方案3】:

在 C++ 中显而易见的答案是使用std::vector,在这种情况下,它 变得像这样简单:

if ( a.back() == NULL ) {
    a.insert( a.begin(), NULL );
    a.pop_back();
}

您也可以考虑std::deque,这将允许push_front, 而不是insert。对于这么小的数组简单复制 对象。 std::vector 的简单性通常胜出。

如果你必须使用 C 风格的数组,比如:

if ( *(end( a ) - 1) == NULL ) {
    std::copy_backwards( begin( a ), end( a ) - 1, end( a ) );
    a[0] = NULL;
}

应该可以解决问题。

【讨论】:

  • “std::vector 的简单性通常胜出”——我想知道人们多久重新测试一次这种经验法则。除非你找出哪个更慢并选择那个,否则有人会指责你过早优化,但我仍然相信gotw.ca/gotw/054.htmdeque 有更多功能,所以除非你需要vector 来实现连续性或operator[] / 迭代的速度。尽管对此深信不疑,但我仍然没有真正遵循建议,我通常使用vector,除非我要使用该功能(如在本例中插入在前面)。
  • @SteveJessop 在实践中,我几乎只使用std::vector,除非存在与迭代器生命周期有关的问题(只能通过std::list 解决)。或者探查器另有说明。但是,在这种情况下,它确实取决于您的习惯或约定。人们很容易争辩说deque 更自然,因为你想要push_front。 “通常胜出”确实与我的习惯有关(std::deque 表示有特殊限制)。
【解决方案4】:

如果您要经常这样做(并且数组不小),请考虑使用std::deque,因为它允许在两端高效插入和移除。然后可以将N 位置的右移替换为从后面弹出N 空值并在前面推N 空值。你也可以使用std::rotate

【讨论】:

    【解决方案5】:

    对于 POD 和非 POD 类型的任何容器(包括原始数组),请使用以下内容:

    template <class Iterator, class Distance>
    void shiftRight(Iterator begin, Iterator end, Distance dist)
    {
        typedef typename std::iterator_traits<Iterator>::value_type value_type;
        Iterator newBegin = begin, oldEnd = end;
        std::advance(newBegin, dist);
        std::advance(oldEnd, -dist);
        std::copy_backward(begin, oldEnd, end);
        std::fill(begin, newBegin, value_type());
    }
    

    它适用于 POD 和非 POD 类型,因为 copy_backward 负责值类别,如果它是 POD,则它使用 memmove(至少在 gcc 使用的 std 库中)。

    std::advance 用于随机访问迭代器正在使用简单的加法/减法。

    std::fill 也像 std::copy* 一样负责 POD。

    value_type() 对于指针类型只是 NULL,对于 bool false,对于整数类型 0 等等。

    数组的用法:

      int* a[] = { 0, new int(1), new int(2), 0, 0, new int(3) };
    
      std::for_each(a, a + sizeof(a) / sizeof(*a), [](int* a) { !a ? (std::cout << "null\n") : (std::cout << *a << "\n"); });
    
      shiftRight(a, a + sizeof(a) / sizeof(*a), 3);
      std::cout << "-----------------------------------------------------\n";
    
      std::for_each(a, a + sizeof(a) / sizeof(*a), [](int* a) { !a ? (std::cout << "null\n") : (std::cout << *a << "\n"); });
    

    按预期输出:

    null
    1
    2
    null
    null
    3
    -----------------------------------------------------
    null
    null
    null
    null
    1
    2
    

    【讨论】:

      【解决方案6】:

      您可以尝试实现一个链表,它允许您移动/取消移动或推送/弹出元素(在这种情况下将释放空间)。

      Circular 的做法有什么问题?它非常有效。例如:

      bool insert(Packet *queue[], int *rear, int front, Packet *value)
      {
        int tmp = (*rear+1) % MAX;
        if (tmp == front) 
          return false;
      
        *rear = tmp;
        queue[*rear] = value;
        return true;
      }
      
      bool delete(Packet *queue[], int rear, int *front, Packet **value) 
      {
        if (*front == rear) 
          return false;
      
        *front = (*front+1) % MAX;
        *value = queue[*front];
        return true;
      }
      

      【讨论】:

      • 我的理解是问题仅与 C 有关。在 C++ 中有更简单的解决方案(见上面的帖子)
      【解决方案7】:

      在 C++11 中,我们有 std::rotate

      int [] values = {1, 2, 3, 4, 5};
      std::rotate(
          std::begin(values),
          std::next(std::begin(values)), // the new 'top'
          std::end(values));
      *std::rbegin(values) = 0;
      
      assert(values[0] == 2);
      assert(values[1] == 3);
      assert(values[2] == 4);
      assert(values[3] == 5);
      assert(values[4] == 0);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 1970-01-01
        • 2021-11-23
        • 1970-01-01
        • 2022-01-05
        • 1970-01-01
        相关资源
        最近更新 更多