在数组C ++中移位元素

时间:2009-10-23 21:47:32

标签: c++ algorithm

我为我的堆栈对象类开发了一个名为“rotate”的方法。我所做的是,如果堆栈包含元素:{0,2,3,4,5,6,7}我需要向前和向后旋转元素。

如果我需要向前旋转2个元素,那么我们在数组中会有{3,4,5,6,7,0,2}。如果我需要向后旋转,或者-3个元素,那么,查看原始数组,{5,6,7,0,2,3,4}

所以我开发的方法运行正常。它只是非常无效的IMO。我想知道我是否可以使用mod运算符包围数组?或者如果它们是无用的代码,我还没有意识到,等等。

我想我的问题是,我该如何简化这种方法?例如使用较少的代码。 : - )

void stack::rotate(int r)
{
    int i = 0;
    while ( r > 0 ) // rotate postively.
    {
        front.n = items[top+1].n;
        for ( int j = 0; j < bottom; j++ )
        {
            items[j] = items[j+1];                                  
        }
        items[count-1].n = front.n;
        r--;
    }

    while ( r < 0 )  // rotate negatively.
    {
        if ( i == top+1 )
        {
            front.n = items[top+1].n;  
            items[top+1].n = items[count-1].n; // switch last with first
        }

        back.n = items[++i].n; // second element is the new back
        items[i].n = front.n; 
        if ( i == bottom )
        {
            items[count-1].n = front.n; // last is first
            i = 0;  
            r++;   
            continue;
        }
        else
        {
            front.n = items[++i].n;
            items[i].n  = back.n;
            if ( i == bottom )
            {
                i = 0;
                r++; 
                continue;
            }
        }
    }
}

8 个答案:

答案 0 :(得分:14)

您可以更改“开始”的定义,而不是移动堆栈中的所有项目。有一个索引代表堆栈中的第一个项目,在开始时为0,您可以在需要旋转堆栈时添加和减去使用模运算。

请注意,如果你采用这种方法,你不应该让你的类用户访问底层数组(不管你真的应该......)。

答案 1 :(得分:5)

好吧,因为这是一个数组的抽象,你可以将“零”索引存储为抽象的一个成员,并根据第一个元素的抽象概念索引到数组中。大致...

class WrappedArray
{
  int length;
  int first;
  T *array;

  T get(int index)
  {
    return array[(first + index) % length];
  }

  int rotateForwards()
  {
    first++;
    if (first == length)
      first = 0;
  }
}

答案 2 :(得分:5)

你已经得到了几个合理的答案,但也许还有一个不会受到伤害。我的第一反应是让你的堆栈成为std :: deque的包装器,在这种情况下,将元素从一端移动到另一端是便宜的(O(1))。

答案 3 :(得分:3)

你在这里的内容是一份循环清单 如果您坚持在数组中存储项目,只需使用顶部偏移和大小进行访问。这种方法使得在达到分配大小之后插入元素虽然昂贵(重新分配,复制)。这可以通过使用双向链表(ala std :: list)和迭代器来解决,但对堆栈的任意访问将是O(n)。

答案 4 :(得分:2)

现在,通常的“它已经在Boost中”回答:有一个Boost.CircularBuffer

答案 5 :(得分:2)

如果出于某种原因你更喜欢执行数组元素的实际物理旋转,你可能会在Jon Bentley的“Programming Pearls”中找到几种替代解决方案(第2列,2.3 原始力量 )。实际上,Rotating Algorithms 'Programming Pearls'的网络搜索会告诉你一切。您现在使用的文字方法几乎没有实际价值。

如果您希望自己尝试解决问题,那么尝试以不同方式查看问题可能会有所帮助。你看,“旋转数组”与“交换数组中两个不相等的部分”实际上是一回事。在后面的术语中考虑这个问题可能会引导您找到新的解决方案:)

例如,

  • 逆转方法。反转整个数组中元素的顺序。然后独立地反转这两个部分。你完成了。

例如,假设我们想要将abcdefg向右旋转2

abcde fg - &gt; 扭转整个 - &gt; gf edcba - &gt; 颠倒两部分 - &gt; fg ABCDE

P.S。 “{珍珠编程”的那一章Slides。请注意,在Bentley的实验中,上述算法证明非常有效(在三个测试中)。

答案 6 :(得分:2)

下面的函数rotate基于提醒(你的意思是'mod'操作吗?)

它也非常有效。

// Helper function.
// Finds GCD. 
// See http://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations
int gcd(int a, int b) {return b == 0 ? a : gcd(b, a % b);}

// Number of assignments of elements in algo is
// equal to (items.size() + gcd(items.size(),r)).
void rotate(std::vector<int>& items, int r) {
  int size = (int)items.size();
  if (size <= 1) return; // nothing to do
  r = (r % size + size) % size; // fits r into [0..size)
  int num_cycles = gcd(size, r);
  for (int first_index = 0; first_index < num_cycles; ++first_index) {
    int mem = items[first_index]; // assignment of items elements
    int index = (first_index + r) % size, index_prev = first_index;
    while (index != first_index) {
      items[index_prev] = items[index]; // assignment of items elements
      index_prev = index;
      index = (index + r) % size;
    };
    items[index_prev] = mem; // assignment of items elements
  }
}

当然,如果您更改其他答案中所述的数据结构,则可以获得更有效的解决方案。

答案 7 :(得分:2)

我不明白变量frontback是什么意思,以及为什么需要.n。无论如何,这是我知道旋转数组元素的最短代码,也可以在Bentley的书中找到。

#include <algorithm>

std::reverse(array    , array + r   );
std::reverse(array + r, array + size);
std::reverse(array    , array + size);