题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
1.要想保证原有次序,则只能顺次移动或相邻交换。
2.i从左向右遍历,找到第一个偶数。
3.j从i+1开始向后找,直到找到第一个奇数。
4.将[i,...,j-1]的元素整体后移一位,最后将找到的奇数放入i位置,然后i++。
5.終止條件:j向後遍歷查找失敗。
1.暴力解法
最简单的思路就是从头到尾扫描一遍数组,每遇见一个偶数时,就拿出这个数字,并把位于这个数字之后的所有数字往前挪动一位,然后把当前这个偶数放到最后。
这样每次遇到一个偶数就要挪动O(n)个数字,因此总的时间复杂度是O(n^2)
但是这种方法不仅暴力而且还需要复杂的挪动工作,因此我们对比一下冒泡排序,每次循环前偶后奇就交换
同时我们设立一个标识,来标识数组是否已经符合要求
当再次循环时,发现没有要交换的数据,说明数组已经符合要求
//两个思路吧,第一个思路:类似冒泡算法,前偶后奇数就交换:
class Solution {
public:
void reOrderArray(vector<int> &array) {
for (int i = 0; i < array.size();i++)
{
for (int j = array.size() - 1; j>i;j--)
{
if (array[j] % 2 == 1 && array[j - 1]%2 == 0) //前偶后奇交换
{
swap(array[j], array[j-1]);
}
}
}
}
};
2.辅助数组完成
我们也可以利用一个辅助的数组空间来实现
在原来的数组中遇到偶数就放进新数组中,然后将该偶数从原来数组中删除,当整个遍历结束后,原来数组中只剩下奇数,新的数组总只剩下偶数,最后我们将新的数组追加在原来数组的末尾即可
//第二个思路:再创建一个数组
class Solution{
public:
void reOrderArray(vector<int> &array) {
vector<int> array_temp;
vector<int>::iterator ib1, ie1;
ib1 = array.begin();
for (; ib1 != array.end();){
//遇见偶数,就保存到新数组,同时从原数组中删除
if (*ib1 % 2 == 0) {
array_temp.push_back(*ib1);
ib1 = array.erase(ib1);
}
else{
ib1++;
}
}
vector<int>::iterator ib2, ie2;
ib2 = array_temp.begin();
ie2 = array_temp.end();
for (; ib2 != ie2; ib2++)
//将新数组的数添加到老数组
{
array.push_back(*ib2);
}
}
};
3.高效解法
由于题目中只要求记奇数在偶数之前,那么我们在扫描这个数组的时候,如果发现一个偶数出现在奇数之前就交换他们的位置,这样一趟后就满足要求了。
因此我们维护两个索引或者指针,一个指向数组的第一个元素,并向后移动,一个指向数组的最后一个元素,并向前移动。如果第一个指针指向的元素是偶数,而第二个指针指向的元素是奇数,说明偶数在奇数前面,那么就交换这两个数。直到两个指针相遇为止
剑指offer中的原答案是没有考虑到奇偶交换之后还要保持相对顺序不变的
以下是原答案:
//剑指offer上的解法,但是相对位置改变了
void reOrderArray(vector<int> &array) {
if(array.size() <= 1)
return;
vector<int>::iterator left = array.begin();
vector<int>::iterator right = array.end() - 1;
// 三种顺序容器中只有deque和vector容器的迭代器支持大小的比较和算术运算。
while(left < right)
{
while(left < right && (*left & 0x1) != 0)
left++;
while(left < right && (*right & 0x1) == 0)
right--;
if(left < right)
{
swap(*left, *right);
}
}
}
牛客网友的高赞解答(Java):
/**
* 1.要想保证原有次序,则只能顺次移动或相邻交换。
* 2.i从左向右遍历,找到第一个偶数。
* 3.j从i+1开始向后找,直到找到第一个奇数。
* 4.将[i,...,j-1]的元素整体后移一位,最后将找到的奇数放入i位置,然后i++。
* 5.終止條件:j向後遍歷查找失敗。
*/
public void reOrderArray2(int [] a) {
if(a==null||a.length==0)
return;
int i = 0,j;
while(i<a.length){
while(i<a.length&&!isEven(a[i]))
i++;
j = i+1;
while(j<a.length&&isEven(a[j]))
j++;
if(j<a.length){
int tmp = a[j];
for (int j2 = j-1; j2 >=i; j2--) {
a[j2+1] = a[j2];
}
a[i++] = tmp;
}else{// 查找失敗
break;
}
}
}
boolean isEven(int n){
if(n%2==0)
return true;
return false;
}