移动数组中的元素 C++
Posted
技术标签:
【中文标题】移动数组中的元素 C++【英文标题】:Shifting elements in an array C++ 【发布时间】:2009-10-23 21:47:31 【问题描述】:我为我的堆栈对象类开发了一种称为“旋转”的方法。我所做的是,如果堆栈包含元素: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;
【问题讨论】:
你打算怎么用这个?您是一次访问一个结果,还是想要传递整个数组? 这是一个控制台应用程序。我只是逐个打印每个元素。 使用链表会让事情变得容易很多。 是否考虑过使用 STL 算法 std::rotate ? 【参考方案1】:您可以更改“开始”的定义,而不是移动堆栈中的所有项目。有一个表示堆栈中第一项的索引,开始时为 0,当您想要旋转堆栈时,您可以使用模运算对其进行加减运算。
请注意,如果您采用这种方法,则不应让您的类的用户访问底层数组(不是您真的无论如何都应该...)。
【讨论】:
有时你只需要有一个更高的观点,而不是直接去实现。【参考方案2】:好吧,由于这是对数组的抽象,您可以将“零”索引存储为抽象的成员,并根据第一个元素的这个抽象概念对数组进行索引。大概……
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;
【讨论】:
【参考方案3】:你已经得到了几个合理的答案,但也许再有一个不会有什么坏处。我的第一反应是让你的堆栈成为一个 std::deque 的包装器,在这种情况下,将一个元素从一端移动到另一端很便宜 (O(1))。
【讨论】:
或者您可以使用 valarray 并使用内置的 cshift 成员函数。valarray
适用于旋转部分,但不适用于添加或删除项目。 valarray::resize()
覆盖当前数组中的所有值...【参考方案4】:
您在此处所追求的是一个循环列表。 如果您坚持将项目存储在数组中,只需使用顶部偏移量和大小进行访问。这种方法使得在达到分配大小后插入元素变得昂贵(重新分配、复制)。这可以通过使用双向链表(ala std::list)和迭代器来解决,但是对堆栈的任意访问将是 O(n)。
【讨论】:
【参考方案5】:下面的函数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
当然,如果您可以按照其他答案中的描述更改数据结构,您可以获得更有效的解决方案。
【讨论】:
本特利的书中称其为“杂耍方法”。根据这本书,这种方法虽然在理论上是最优的,但在实践中却是效率最低的。此方法的性能受到其不良缓存行为的负面影响。性能结果可能已经过时,但仍然值得考虑。 是的,你是对的。我提供这种方法基于以下原因: 1. 灯罩询问基于mod的方法。 2. 我假设数据结构不能改变。 3.提供易于使用的答案(现成的代码,不需要修改任何东西或找书看)。实际上不同的答案提供了不同的方法来解决原始问题。【参考方案6】:现在,通常的“它已经在 Boost 中”回答:有一个 Boost.CircularBuffer
【讨论】:
我很惊讶地看到std::rotate()
在算法中可以与任何具有ForwardIterator
s 的东西一起使用(尽管可能效率不高)。我需要复习<algorithm>
...【参考方案7】:
如果出于某种原因您希望对数组元素执行实际的物理旋转,您可能会在 Jon Bentley 的“Programming Pearls”中找到几个替代解决方案(第 2 列,2.3 原始的力量 )。实际上,网络搜索Rotating Algorithms 'Programming Pearls'
会告诉你一切。您现在使用的文字方法几乎没有实际价值。
如果您想尝试自己解决问题,尝试以不同的方式看待问题可能会有所帮助。您会看到,“旋转数组”实际上与“交换数组的两个不相等部分”是一回事。用后者的术语思考这个问题可能会导致您找到新的解决方案:)
例如,
反转方法。反转整个数组中元素的顺序。然后将两个部分独立反转。你完成了。例如,假设我们要将abcdefg
向右旋转 2
abcdefg
-> 反转整体 -> gf
edcba -> 反转两部分 -> fg
abcde
附: Slides 用于“编程珍珠”的那一章。请注意,在 Bentley 的实验中,上述算法被证明是非常有效的(在三个测试中)。
【讨论】:
【参考方案8】:我不明白变量 front
和 back
是什么意思,以及为什么需要 .n
。无论如何,这是我所知道的用于旋转数组元素的最短代码,也可以在 Bentley 的书中找到。
#include <algorithm>
std::reverse(array , array + r );
std::reverse(array + r, array + size);
std::reverse(array , array + size);
【讨论】:
在 MSVC++ 实现中,std::rotate
专门用于双向迭代器。以上是关于移动数组中的元素 C++的主要内容,如果未能解决你的问题,请参考以下文章