数据结构与算法|数组之元素奇偶移动专题|leetcode905按奇偶排序数组(cpp、Java实现)

44 阅读2分钟

905 按奇偶排序数组

leetcode.cn/problems/so…

按奇偶排序数组。给定一个非负整数数组 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数字移动到数组的后面时,可以使用双指针法解决。

具体步骤如下:

  1. 初始化左指针(left)和右指针(right),分别指向数组的两端。
  2. 当left指针小于等于right指针时,执行以下操作:
    • 如果nums[left]为0,表示遇到了0,将其与右指针指向的元素交换,并将右指针向前移动一步。
    • 如果nums[left]不为0,表示遇到了非0数字,将left指针向前移动一步。
  1. 扫描结束后,所有的非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数字的相对顺序不变。