Part1. 我的思路和代码
1-我认为是投机的思路
定义一个与原数组等长的数组,然后从头到尾扫描原数组,依次将奇数填入新数组,再第二次从头到尾扫描原数组,依次将偶数填入新数组。从头到尾扫描原数组、从前向后填充新数组,保证了调整前后奇数、偶数的相对位置不变
。
扫描两遍数组
,每遍时间复杂度都是O(n),总的时间复杂度也为O(n);使用了辅助空间定义了与原数组等长的新数组,空间复杂度为O(n)。
vector<int> reOrderArray(vector<int>& array)
{
vector<int> ans;
int idx;
ans = array;
idx = 0;
for(int i=0; i<array.size(); i++){
if(array[i]%2 != 0){ //奇数
ans[idx] = array[i];
++idx;
}
}
for(int i=0; i<array.size(); i++){
if(array[i]%2 == 0){ //偶数
ans[idx] = array[i];
++idx;
}
}
return ans;
}
2-片段调整法(不知道起什么标题就这样称了)
遍历数组的每个元素,如果是奇数,则把之前的偶数部分的每个元素都向后移动一个位置
,再把该奇数填入“前面空出的那个位置
”,如下图所示:
外层循环遍历数组的每个元素,时间复杂度O(n),内层循环移动一段元素的位置,时间复杂度O(n),总的时间复杂度为O(n^2);使用的额外空间和输入规模无关,空间复杂度为O(1)。
vector<int> reOrderArray(vector<int>& array)
{
int idx;
idx = 0; // 指向下一个奇数应该调整到的位置
for(int i=0; i<array.size(); i++){
if(array[i]%2 != 0){
int temp = array[i];
for(int j=i-1; j>=idx; j--){
array[j+1] = array[j];
}
array[idx] = temp;
idx++;
}
}
return array;
}
Part2. 其他做法
双指针
定义两个指针
p1、p2,初始时分别指向数组第一个、最后一个元素。p1向后移动,遇到偶数停止,p2则向前移动,遇到奇数停止,当p1、p2均停止移动后,交换二者指向的元素,重复该过程,直到p1移动到p2之后。如下图所示:
该做法至多遍历一次数组,调整操作也是常数时间,时间复杂度为O(n);用到的辅助空间与输入规模无关,空间复杂度为O(1)。
然而,若题目要求调整后奇数、偶数的相对位置不变,则该双指针的做法是不能满足要求的
,例如"1 2 3 4 5",第一次调整后2会位于4之后,相对位置发生变化。
Part3. 心得体会
双指针
是一个很经典的思路;- 调整数组元素需要注意题目
是否要求相对位置不变
,该要求不同所用的思路也不同; - (从书中所学)该题目调整依据是数字的奇偶,若改为其他调整条件则需要修改相应的循环条件,若考虑到
代码的扩展性
,可以将循环条件抽象为一个函数func(),在函数内部具体实现判断条件,这样当条件变化后只需修改func()即可,无需改动主体代码。