题目
给定一个非负整数数组 A,返回一个由 A 的所有偶数元素组成的数组,后面跟 A 的所有奇数元素。
你可以返回满足此条件的任何数组作为答案。
输入:[3,1,2,4]
输出:[2,4,3,1]
输出 [4,2,3,1],[2,4,1,3] 和 [4,2,1,3] 也会被接受。
解题思路
根据题目的描述,可以明确的知道这道题只有一个要求,那就是将数组中的偶数全部挪到奇数的前面,另外顺序不计。
解法一
我看到这个题的第一反应是可以使用"插入排序"来解决,比如我们要使元素从小到大排序,插入排序则是不断地将更小的元素往前面数组前面插入来排序的,而这个题则需要把遇到的偶数不断挪到数组的前面。这个解法主要使用两个for循环,一个用来遍历整条数组,另一个用来扫描已排好顺序的列,使用if语句来判断是否满足两个元素交换的条件,再将元素插入到合适的位置。
对于这道题两个元素交换的条件是当前索引位置的元素是偶数且它的前一个位置是奇数,于是有了第一种解法。
public static int[] SortArrayByParity(int[] A)
{
int length = A.Length;
for (int i = 1; i < length; i++)
{
for (int j = i; j > 0; j--)
{
if(A[j]%2==0&&A[j-1]%2==1) //当前索引位置元素是偶数且前一个位置是奇数
{
int temp = A[j];
A[j] = A[j - 1];
A[j - 1] = temp;
}
}
}
return A;
}
这种解法其实就是将简单插入排序的if
语句换了一下而已。因为代码中只使用一个temp
变量作为两个元素交换的‘中介’,所以它的空间复杂度为O(1),时间复杂度也就是简单插入排序的时间复杂度,即O(n^2),但是实际肯定要比插入排序快,因为题目并不要求有序。
解法二
第二种解法的思路是,创建一个和A数组长度一样的数组B,同时新建两个索引变量i
、j
。其中,i的起始位置指向数组的第一个位置,j起始位置指向数组的最后一个位置。
程序开始对A进行遍历,遍历的同时对每个元素进行奇偶判断。如果是偶数,就把当前元素放在B[i]
的位置,同时i
自增1;如果是奇数,就放在B[j]
的位置,同时j
自减1。
public static int[] SortArrayByParity(int[] A)
{
int length = A.Length;
int[] B = new int[length];
int i = 0, j = length-1;
foreach (var item in A)
{
if(item%2==0)
{
B[i] = item;
i++;
}
else
{
B[j] = item;
j--;
}
}
return B;
}
在第二种解法种,我们创建了和数组A一样长度的数组B,所以它的空间复杂度是O(n)。另外,我们只对数组A进行了一次遍历,所以时间复杂度是O(n)。
结语
在物理内存中,数组是一块连续的区域,元素是一个挨着一个的,就像火车车厢一样,我觉得这种对数组元素位置进行“重排”的题目,基本就两种解题思路:一种是挪动元素位置;另一种就是创建新数组,将旧数组中满足条件的元素重新插入新数组中。我在网上也有看到其他的解题方式,例如利用过滤器找出数组中的偶数和奇数,然后再将分解出来的偶数数组和奇数数据进行连接,我觉得本质上和第二种解法是一样的。