题目:输入一个整数数组,实现一个函数来调整数组中数字的顺序,使得奇数位于数组的前半部分,偶数位于数组的后半部分。
咋一看,没有限定空间复杂度,直接想到声明两个数组,一个存奇数,一个存偶数。再合并。时间复杂度O(n),空间复杂度O(n),代码如下:
class Solution {
public:
void reOrderArray(vector<int> &array) {
vector<int> oddArray;
vector<int> evenArray;
int i;
for(i=0;i<array.size();i++)
{
if(array[i]%2==1)
{
oddArray.push_back(array[i]);
}
else
evenArray.push_back(array[i]);
}
for(i=0;i<oddArray.size();i++)
{
array[i]=oddArray[i];
}
for(i=0;i<evenArray.size();i++)
{
array[oddArray.size()+i]=evenArray[i];
}
return ;
}
};
面试时感觉这种肯定过不了。考虑下节省空间复杂度。
再看一下题目,发现我一开始就跑偏了,我开始想的是数组以前元素的相对顺序不变,结果看本题好像没有这个意思,那就更简单了。
采用两个指针,一个指向数组的开头,一个指向数组的结尾。当开头的指针遇到奇数,跳过,遇到偶数,停止,指向数组末尾的指针遇到偶数,跳过,遇到奇数,停止。然后两个指针交换元素。这种思想类似于快速排序中的partition思想。算法原地调整,时间复杂度O(n),空间复杂度O(1).
先试着写个。
class Solution {
public:
void reOrderArray(vector<int> &array) {
int i=0,j=array.size()-1;
while(i<=j)
{
while(i<array.size()&&array[i]%2==1) i++;
while(j>=0&&array[j]%2==0) j--;
if(i<=j)
swap(array[i],array[j]);
}
return;
}
};
不过快速排序本身就是不稳定的排序方法,这种方法虽然奇数在前,偶数在后,但是肯定数组的元素相对顺序和以前不同。如1,2,3,4,5肯定变成了1,5,3,4,2.
看网上有人为了使算法具有稳定性,采用归并排序,可是归并排序要的空间复杂度就是O(n),有那个O(n)还不如直接像开始一样声明两个数组呢。
想到腾讯今年的实习笔试,有一道类似的题目,但是是要求空间复杂度是O(1),那没办法,只有牺牲时间复杂度了。
代码如下:
class Solution {
public:
void reOrderArray(vector<int> &array) {
int len=array.size(),i,j,k,l;
for(i=len-1,j=len-1;i>=0&&j>=0;) //i,j分别代表最前的一个偶数,奇数指针
{
if(array[i]%2==0) //当前为偶数,直接跳过
{
i--;
j--;
}
else
{
for(k=j-1;k>=0;) //当前array[i]是奇数,寻找下一个偶数,再将偶数和后面所有的不是偶数的都交换
{
if(array[k]%2==1)
k--;
else
{
int temp=array[k];
memcpy((char *)(&array[k]),(char *)(&array[k+1]),sizeof(int)*(i-k));
array[i]=temp;
break;
/* for(l=k;l<i;l++)
{
swap(array[l],array[l+1]);
}
break;*/
}
}
i=i-1; //更新偶数指针
j=k; //更新奇数指针
}
}
}
};
本算法理论上时间复杂度O(n*n*n),但是实际肯定不到。思考用内存拷贝,会快点,理论时间复杂度为O(n*n).