数组[a1,a2,a3,a4,b1,b2,b3,b4],洗牌后变成[b1,a1,b2,a2,b3,a3,b4,a4]。
不得使用额外的空间,即空间复杂度要求为O(1)。因为如果用线性空间,直接变成2个链表归并就行,特别简单。
数组长度为2n,下标i从1开始计数,且i的范围[1,2n]。
假设2m=3^k-1。则洗牌后前一半(前n个元素)中的前m个元素与后一半(后n个元素)中的前m个元素占据着前2m个槽位。那么,我们可以在洗牌之前提前先将这2m个元素放在一起。即预处理后A1区块与A2区块是挨着的,B1区块与B2区块是挨着的,因为我们知道洗牌后A1与A2共同占用前2m个槽位,B1与B2共同占用后2n-2m个槽位。预处理后排列如下图所示。
有了这个前提之后,我们就可以先将整个数组重新排一下,排成上图所示的样子,其中A1与A2组成的部分就可以直接用圈代换算法,将所有的元素都洗牌好。对于剩下的B1与B2组成的部分,再递归调用前面的算法。
用循环称位即可将B1按原来的顺序移动到A2前面,且A2与B1均保持原来的内部顺序。具体就是将A2B1视为一个整体,然后循环右移B1区块的长度即可。
循环移位算法,直接移位效率比较低,可以用两次局部逆序与一次整体逆序来实现。即
这个算法,主要也就是圈头位置,恰好被分割为若干个圈的数组长度两部分内容,国外专门有一篇论文讲这个just in-place代换,有一些数学推理与证明在里面,具体感兴趣的可以去研究下。:)此处就不多提。
#ifndef SHUFFLE_H
#define SHUFFLE_H
#include
#include
#include "common.h"
template
class Shuffle
{
public:
Shuffle(const std::vector& vec);
/**
* @brief rightCycleMove right shift a array
* @param start start position,included
* @param stop stop position,not include
* @param num right shift number
*/
static void rightCycleMove(typename std::vector::iterator start,
typename std::vector::iterator stop,unsigned int num);
/**
* @brief reverse reverse the span
* @param start start position
* @param stop stop position, not include
*/
static void reverse(typename std::vector::iterator start,typename std::vector::iterator stop);
/**
* @brief findRightShiftPos find the length (2^n)=log3(K)-1
* @param start start position
* @param stop stop position
* @param pos length (2^n)
* @return find or not
*/
static bool findRightShiftPos (typename std::vector::iterator start,typename std::vector::iterator stop,unsigned int & pos);
/**
* @brief splitInto2Part split array into 2 part,one is in perfect length,the remain we can recursivie
* @param start
* @param stop
* @param middle split position
* @return
*/
static bool splitInto2Part(typename std::vector::iterator start, typename std::vector::iterator stop, typename std::vector::iterator &middle);
/**
* @brief shuffle recursive function,split into 2 part, exchange perfect part, and recursive
* @param start
* @param stop
* @return
*/
static bool shuffle(typename std::vector::iterator start,typename std::vector::iterator stop);
/**
* @brief shuffle shuffle function for object
* @return
*/
bool shuffle();
std::vector getData() const{return m_vecData__;}
private:
std::vector m_vecData__; ///< data tobe shuffle
};
template
Shuffle::Shuffle(const std::vector &vec)
{
m_vecData__=vec;
}
template
void Shuffle::reverse(typename std::vector::iterator start, typename std::vector::iterator stop)
{
typename std::vector::iterator l,r;
l=start;
r=stop;
r--;
for(;l
bool Shuffle::findRightShiftPos(typename std::vector::iterator start, typename std::vector::iterator stop, unsigned int &pos)
{
int num=stop-start;
if (num==0) return true;
if(num<0 || (num%2)!=0) return false;
double dlExp=log(double(num+1))/log(double(3));
int nFloor=floor(dlExp);
pos=pow(3,nFloor)-1;
return true;
}
template
bool Shuffle::splitInto2Part(typename std::vector::iterator start, typename std::vector::iterator stop, typename std::vector::iterator &middle)
{
int num = stop-start;
if(num == 0) return true;
unsigned int pos;
if(!findRightShiftPos(start,stop,pos)) return false;
rightCycleMove(start+pos/2,start+num/2+pos/2,pos/2);
middle=start+pos;
return true;
}
template
bool Shuffle::shuffle(typename std::vector::iterator start, typename std::vector::iterator stop)
{
typename std::vector::iterator middle=stop;
if(!splitInto2Part(start,stop,middle)) return false;
int num = middle-start;
if(num==0) return true;
else if(num <0) return false;
int nLog=log(num+1)/log(3);
if(pow(3,nLog)-1 !=num) return false;
for(int i=0;i
bool Shuffle::shuffle()
{
return shuffle(m_vecData__.begin(),m_vecData__.end());
}
template
void Shuffle::rightCycleMove( typename std::vector::iterator start, typename std::vector::iterator stop, unsigned int num)
{
if(num==0) return;
typename std::vector::iterator split= stop-num%(stop-start);
reverse(start,split);
reverse(split,stop);
reverse(start,stop);
}
#endif // SHUFFLE_H
具体的代码工程见github:https://github.com/junbujianwpl/LeetcodePro/blob/master/Shuffle.h
1
2:num 2 1
1
4:num 3 1 4 2
1
6:num 4 1 5 2 6 3
1
8:num 5 1 6 2 7 3 8 4
1
10:num 6 1 7 2 8 3 9 4 10 5
1
12:num 7 1 8 2 9 3 10 4 11 5 12 6
1
14:num 8 1 9 2 10 3 11 4 12 5 13 6 14 7
1
16:num 9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8
1
18:num 10 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9
1
20:num 11 1 12 2 13 3 14 4 15 5 16 6 17 7 18 8 19 9 20 10
1
22:num 12 1 13 2 14 3 15 4 16 5 17 6 18 7 19 8 20 9 21 10 22 11
1
24:num 13 1 14 2 15 3 16 4 17 5 18 6 19 7 20 8 21 9 22 10 23 11 24 12
1
26:num 14 1 15 2 16 3 17 4 18 5 19 6 20 7 21 8 22 9 23 10 24 11 25 12 26 13
1
28:num 15 1 16 2 17 3 18 4 19 5 20 6 21 7 22 8 23 9 24 10 25 11 26 12 27 13 28 14
1
30:num 16 1 17 2 18 3 19 4 20 5 21 6 22 7 23 8 24 9 25 10 26 11 27 12 28 13 29 14 30 15
1
32:num 17 1 18 2 19 3 20 4 21 5 22 6 23 7 24 8 25 9 26 10 27 11 28 12 29 13 30 14 31 15 32 16
1
34:num 18 1 19 2 20 3 21 4 22 5 23 6 24 7 25 8 26 9 27 10 28 11 29 12 30 13 31 14 32 15 33 16 34 17
1
36:num 19 1 20 2 21 3 22 4 23 5 24 6 25 7 26 8 27 9 28 10 29 11 30 12 31 13 32 14 33 15 34 16 35 17 36 18
1
38:num 20 1 21 2 22 3 23 4 24 5 25 6 26 7 27 8 28 9 29 10 30 11 31 12 32 13 33 14 34 15 35 16 36 17 37 18 38 19
有一个很不错的在线画图网站 http://draw.io
文中前面2幅图就是在这个网站上画的。:)