905 按奇偶排序数组
按奇偶排序数组。给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。你可以返回满足此条件的任何数组作为答案。
方法一:临时数组
牺牲空间,保全时间。
最直接的方式是使用一个临时数组,第一遍查找并将所有的偶数复制到新数组的前部分,第二遍查找并复制所有的奇数到数组后部分。
cpp代码:
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
int n=nums.size();
vector <int> arr;
for(int i=0;i<n;i++)
{
if(nums[i]%2==0){
arr.push_back(nums[i]);
}
}
for(int i=0;i<n;i++){
if(nums[i]%2==1){
arr.push_back(nums[i]);
}
}
return arr;
}
};
push_back()函数的用法
函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
push_back() 在Vector最后添加一个元素(参数为要插入的值)
Java代码:
class Solution {
public int[] sortArrayByParity(int[] nums) {
int n=nums.length,index=0;
int[] arr=new int[n];
for(int i=0;i<n;i++){
if(nums[i]%2==0)
arr[index++]=nums[i];
}
for(int i=0;i<n;i++){
if(nums[i]%2==1)
arr[index++]=nums[i];
}
return arr;
}
}
这种方式实现比较简单,但是可能不会讨面试官欢心。面试官可能会问有没有空间复杂度为O(1)的解法。
方法二:对撞双指针
这种解法中,我们定义两个指针:一个指针left从数组的起始位置开始,另一个指针right从数组的末尾位置开始。然后,我们遍历数组,left从0开始逐个检查每个位置是否为偶数,如果是偶数则指针left向后移动,如果是奇数则停下来。然后right从右向左检查,如果是奇数则指针right向前移动,偶数则停下来。然后交换array[left]和array[right]。之后再继续循环,重复这个过程,直到指针left和指针right相遇。最后,就可得到一个按照偶奇排序好的数组。
cpp代码:
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left < right) {
if (nums[left] % 2 > nums[right] % 2) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
if (nums[left] % 2 == 0) left++;
if (nums[right] % 2 == 1) right--;
}
return nums;
}
};
代码优化:
利用swap函数来进行交换。
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
int left=0,right=nums.size()-1;
while(left<right){
if(nums[left]%2>nums[right]%2){
swap(nums[left],nums[right]);
}
if(nums[left]%2==0)left++;
if(nums[right]%2==1)right--;
}
return nums;
}
};
Java代码:
class Solution {
public int[] sortArrayByParity(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
if (nums[left] % 2 > nums[right] % 2) {
int tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
}
if (nums[left] % 2 == 0) left++;
if (nums[right] % 2 == 1) right--;
}
return nums;
}
}
方法三:快慢指针法
在这种解法中,我们使用两个指针fast和slow。fast指针用于遍历数组中的元素,slow指针用于记录已经排好序的偶数的位置。当fast指针找到一个偶数时,将其与slow指针所指向的元素交换位置,然后slow指针向后移动一位。最后,就可得到一个按照偶奇排序好的数组。
cpp代码:
class Solution {
public:
vector<int> sortArrayByParity(vector<int>& nums) {
int slow = 0;
for (int fast = 0; fast < nums.size(); fast++) {
if (nums[fast] % 2 == 0) {
swap(nums[fast], nums[slow]);
slow++;
}
}
return nums;
}
};
Java代码:
class Solution {
public int[] sortArrayByParity(int[] nums) {
int slow = 0;
for (int fast = 0; fast < A.length; fast++) {
if (A[fast] % 2 == 0) {
int temp = A[fast];
A[fast] = A[slow];
A[slow] = temp;
slow++;
}
}
return A;
}
}
数组元素奇偶移动专题的总结
这类题目多使用双指针法,首先将左右指针分别指向数组的两端,然后依据题目要求进行元素交换。或者可以另辟蹊径,另外开辟一个数组空间,不过这种方法牺牲了空间。常见的情况有:
1.移动偶数到数组前面,奇数到数组后面
在这种情况下,可以以0为基准,左指针从头到尾扫描原始数组,当遇到一个偶数时,将其与基准(第一个元素)交换。在扫描结束后,所有的偶数都被移动到了基准的左侧。接下来,再以最后一个元素为基准,右指针从尾到头扫描原始数组,当遇到一个奇数时,将其与基准(最后一个元素)交换。在扫描结束后,所有的奇数都被移动到了基准的右侧。
2.移动0到数组前面,非0数字到数组后面
在这种情况下,可以使用双指针法,左指针从头到尾扫描原始数组,当遇到一个非0数时,将其与基准(第一个元素)交换。在扫描结束后,所有的非0数字都被移动到了基准的右侧,0位于其左侧。
在使用双指针法求解这种问题时,不仅要注意指针移动的条件,还要注意应该如何移动指针,即在什么情况下移动指针,以及移动多少步等。
情况1已经讲过了。例题为leetcode905。
情况2具体情境为:当给定一个数组,要求将所有的0移动到数组的前面,非0数字移动到数组的后面时,可以使用双指针法解决。
具体步骤如下:
- 初始化左指针(left)和右指针(right),分别指向数组的两端。
- 当left指针小于等于right指针时,执行以下操作:
-
- 如果nums[left]为0,表示遇到了0,将其与右指针指向的元素交换,并将右指针向前移动一步。
- 如果nums[left]不为0,表示遇到了非0数字,将left指针向前移动一步。
- 扫描结束后,所有的非0数字都被移动到了右指针的左侧,0位于其右侧。
以下是一个例题的示例代码:(cpp实现)
vector<int> moveZeroes(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
if (nums[left] == 0) {
swap(nums[left], nums[right]);
right--;
}
else {
left++;
}
}
return nums;
}
例如,给定输入数组为[0, 1, 0, 3, 12],执行上述代码后,输出为[1, 3, 12, 0, 0]。可以看到,所有的0被移动到数组的前面,非0数字移动到了数组的后面。
这种方法是高效的,因为每次只需进行一次交换操作,并通过双指针的移动实现了线性时间复杂度。同时,该方法可以保持非0数字的相对顺序不变。