假设有一个数组,我们想要找到奇数索引中的所有内容(索引从0开始),然后将其移到最后。 偶数索引中的所有内容都将其移至开头。 保留所有奇数索引项和所有偶数索引项的相对顺序。
即。如果数组是
a1 b1 a2 b2 ... an bn
操作后变为
a1 a2 a3 ... an b1 b2 ... bn
这可以在原地和O(n)时间内完成吗?
答案 0 :(得分:7)
这是可能的,但它非常复杂!一个更简单的O(nlogn)和O(1)空间解决方案可能更适合编码和缓存。
我们将解决与您不同的问题,但一旦我们解决了这个问题,您的问题就无法解决。
将数组视为
b1, a1, b2, a2, ..., bn, an
,你必须将其转换为
a1, a2, ..., an, b1, b2, ..., bn
使用索引1到2n,
我们看到这是由
给出的i -> (n+1)*i (mod 2n+1).
O(nlogn)时间O(1)空间解
我们可以如下使用分而治之。
首先是接近n / 2转换的一些m
b1, a1, ..., bn , an
到
a1,a2,...am, b1,b2, ..bm, a(m+1), ..., an, b(m+1), ... , bn
通过递归地应用于前2m个元素,然后是剩余的。
现在我们需要通过m个点对中间数组进行循环移位(这可以在O(n)时间和O(1)空间中完成)
给予
a1, a2, .., am , a(m+1), ..., an, b1, b2, ..., bm, b(m+1), ..., bn.
当然,正如IVlad指出的那样,这需要O(logn)堆栈空间。我们可以通过以下方式解决这个问题:
我们有:
b1 a1, b2 a2, .. bm am, b(m+1) a(m+1), ..., bn an
现在在数组的后半部分交换对以提供
b1 a1, b2 a2, .. bm am, a(m+1) b(m+1), ..., an bn
现在循环移位奇数位置的元素:b1, b2, .., bm, a(m+1), a(m+2) ..., a(n).
这就像
a(m+1) a1, a(m+2) a2, ..., a(2m) am, a(2m+1) b(m+1),...,an b(n-m), b1 b(n-m+1),...,bm bn
现在再次交换数组的后半部分以提供
a(m+1) a1, a(m+2) a2, ..., a(2m) am, b(m+1) a(2m+1),...,b(n-m) an,b(n-m+1) b1,..., bn bm
现在递归地解决第一部分和第二部分给出
[a1 a2 ... am][a(m+1) ... a(2m)] [a(2m+1) ...an b1 b2 .. bm][b(m+1) ... bn]
这适用于2m> = n或不是。
所以,这是O(nlogn)时间和O(1)空间算法。
O(n)时间O(1)空间解决方案。
使用的想法类似于以下论文中使用的想法: A simple in-place algorithm for Inshuffle
您需要阅读该论文以了解以下内容。我建议你也阅读:How to master in-place array modification algorithms?
这基本上是上述论文所解决的逆变换。
当2n + 1是3 =(3 ^ m)的幂时,这足以解决这个问题,因为我们可以使用除法和征服(如O(nlogn)解决方案)。
现在2n + 1和n + 1是相对素数,所以工作模3 ^ m,我们看到n + 1 必须是2的幂。(再看那篇论文,看看为什么:基本上任何数模3 ^ m,相对于3 ^ m的素数是2的幂,再次模3 ^ m)。
说n + 1 = 2 ^ k(我们还不知道k,注意这是模3 ^ m)。
找出k的方法,计算n + 1模3 ^ m的幂,直到它变为1.这给了我们k(并且最多是O(n)时间。)
现在我们可以看到排列的周期(见上文/ stackoverflow链接的内容)从
开始2 ^一个* 3 ^ B
其中0 <= a&lt; k,0&lt; = b&lt;米。
所以你从每个可能的对(a,b)开始并按照排列的循环,这给出了一个O(n)时间,就地算法,因为你触摸每个元素不超过一个常数次!
这有点简短(!)如果您需要更多信息,请告诉我。
答案 1 :(得分:3)
答案 2 :(得分:0)
好吧,让我们来看看这个例子:
0 1 2 3 4 5 6 7 8 9 10
0 2 4 6 8 10 1 3 5 7 9
结果的前半部分包含索引为原始索引i的i / 2的元素,而另一半为i - n / 2 + 1,其中n是数组中的项目数。
这基本上是排序算法的特例,只有设置了特殊的顺序。
因此,这是一个使用bubblesort算法对整数数组进行排序的想法。我们需要的是将偶数值拉到开头,留下奇数值:
0 1 2 3 4 5 6 7 8 9 10
* *
0 2 1 3 4 5 6 7 8 9 10
* *
0 2 1 4 3 5 6 7 8 9 10
* *
0 2 4 1 3 5 6 7 8 9 10
* *
0 2 4 1 3 6 5 7 8 9 10
* *
...
0 2 4 6 1 3 5 7 8 9 10
* *
...
0 2 4 6 8 1 3 5 7 9 10
* *
这只有在你可以保留索引时才会起作用(例如,在这种情况下,索引与数组值相关)。而bubblesort的性能为O(n ^ 2),内存使用率为1。
无法在O(n)时间和1个内存中完成[编辑:仅使用通用排序算法!],最佳通用排序算法在O(nlog n)中执行:
http://en.wikipedia.org/wiki/Sorting_algorithm
要解决您的问题,最好的方法是生成与您的标准匹配的索引数组:
size_t* indices = new size_t[n];
// n is the number of items in the original array
for (int i = 0; i < n; i++)
{
if (i < n / 2)
{
indices[i] = i * 2;
}
else
{
indices[i] = i - n / 2 + 1;
}
}
然后只需使用它将原始数组中的项目映射到新位置:
// i is the new index here, which makes it appear as if ValuesArray has been sorted
Type* getOddEvenValue(size_t newIndex)
{
// ValuesArray and indices should be available in this scope, of course
return ValuesArray[indices[newIndex]];
}
这个东西会在O(n)中运行,但也需要O(n)内存。但是如果sizeof大小比sizeof大得多,那么它仍然是内存使用量的增加(与复制Type对象相反)。
[EDIT2:]
而不是那些漂亮的OOP代码,而且还需要你将原始矢量复制到你的类实例中,你可以简单地编写一个函数:
size_t permuted_index(size_t i, size_t vecSize)
{
return ( i < vecSize/2 ? i * 2 : 2 * (i - vecSize/2) + 1);
}
并在需要获取permutted值时使用它。实际上,这只需要O(n)时间,因为对于给定的向量,permutted_index将被评估不超过n次(至少在每个“循环”中)。
答案 3 :(得分:0)
O(1)时间,O(1)额外空间
#include <vector>
#include <iostream>
#include <ctime>
#include <boost/random.hpp>
template <typename T>
class pvec
{
private:
// Stores values to permute
std::vector<T> v;
// Flag; true when permuted; false when not
bool permuted;
public:
pvec(size_t size) : v(size), permuted(false) {}
// Set the permutation flag
void set_p(bool p) { permuted = p; }
// When the permuted flag is set, return the permuted element, otherwise
// return the non-permuted element
T& operator[](size_t i) { return (permuted ? v[p_ind(i)] : v[i]); }
// Pass through the size of the vector used
size_t size() const { return v.size(); }
private:
// Used to determine the permuted index of a given index
size_t p_ind(size_t i) const
{
return ( i < v.size()/2 ? i * 2 : 2 * (i - v.size()/2) + 1);
}
};
// Used to display a pvec instance
template <typename T>
std::ostream& operator<<(std::ostream& os, pvec<T>& v)
{
for (size_t i = 0; i < v.size(); ++i)
os << (i == 0 ? "<" : ", ") << i << ":" << v[i];
os << ">";
return os;
}
int main(int argc, char** argv)
{
const size_t size = 8;
pvec<uint32_t> v(size); // Array containing test values
// Boost Random Number Library used to generate random values in the test
// array
boost::mt19937 rng(std::time(0));
boost::uniform_int<> dist(0,65536);
boost::variate_generator<boost::mt19937&, boost::uniform_int<> >
var(rng, dist);
// Generate random test values
for (size_t i = 0; i < size; ++i)
v[i] = var();
// Observer original vector
std::cout << v << std::endl;
// Set the permutation flag O(1) time
v.set_p(true);
// Observe the permuted vector
std::cout << v << std::endl;
return 0;
}
输出:
<0:44961, 1:246, 2:54618, 3:41468, 4:12646, 5:21691, 6:26697, 7:36409> <0:44961, 1:54618, 2:12646, 3:26697, 4:246, 5:41468, 6:21691, 7:36409>