做题的目的是为了强化解题思路和方法的实现,其次是编程能力的提高。不论题目难易,对于每道题,其背后的思想和逻辑都值得我们探究。
剑指offer21:调整数组顺序使奇数位于偶数前面
示例:
输入:nums = [1,2,3,4]
输出:[1,3,2,4]
注:[3,1,2,4] 也是正确的答案之一。
这道题是一道简单题,用一句名言就是“这道题我拿到手就会做~”然后,用自己的方法做完之后,我们是不是得思考下有没有其他的方法和这道题的内在逻辑,以后遇到这种题目从哪里下手等等。多思考多总结。
这道题目:本质就是交换数据,判断数据是奇数还是偶数,如果是奇数就放到数组前面,偶数放到数组后面。
方法一:暴力解法,建立一个额外的数组用来存数据,然后遍历数组即可。
class Solution {
public int[] exchange(int[] nums) {
int[] arr = new int[nums.length];
int left = 0;
int right = nums.length-1;
for(int i=0;i<nums.length;i++){
if(nums[i]%2 != 0){
arr[left++] = nums[i];
}else{
arr[right--] = nums[i];
}
}
return arr;
}
}
显然这个方法是最笨的,用时也比较长。那我们能不能不用另外建立一个新的数组,直接从原数组下手呢?显然是可以的,第二种方法,我们使用双指针来解决问题。其实方法一,也是用了两个指针,不过这两个指针只是用来只是存放的位置,对于原数组数据的判断没有起到一点作用。
方法二:使用双指针,定义一个left指针执行最左边的位置;定义一个right指针指向最右边的位置。之后左右指针同时移动,由于题目要求是数组前面放奇数,后面放偶数;因此,当左(left)指针遇到偶数时停下来,当右(right)指针遇到奇数时停下来,之后交换左右指针的位置继续移动。
class Solution{
public int[] exchange(int[] nums){
int left = 0;
int right = nums.length-1;
while(left < right){
//当left遇到偶数的时候停,奇数的时候一直走【(nums[left] & 1) == 1 奇数】
while(left<right && (nums[left] & 1) == 1){
left++;
}
//当right遇到奇数的时候停止,偶数的时候一直走【(nums[length & 1] == 0)偶数】
while(left<right && (nums[right] & 1) == 0){
right--;
}
//交换
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
return nums;
}
}
当然了,我们还有第三种思路去解决这个问题,使用快慢指针,基本思路是快指针fast去寻找奇数,找到奇数时就停下来,和slow指针进行交换(slow指针指向的是偶数)
方法三:快慢指针
class Solution{
public int[] exchange(int[] nums){
int slow = 0;
int fast = 0;
//用fast指针去找奇数
while(fast<nums.length){
if((nums[fast] & 1) == 1){
//fast找到奇数
if(slow != fast){
int temp =nums[slow];
nums[slow] = nums[fast];
nums[fast] = temp;
}
slow++;
}
fast++;
}
return nums;
}
}
在执行效率上,双指针和快慢指针是差不多的,暴力解法很慢。
快慢指针和双指针用的还是挺多的,他们之间的区别是:双指针是两边开始;快慢指针是同一边开始,两个指针快慢不一样。快慢指针还可以用于找数组的中间位置元素,比如慢指针走一步,快指针都两步,当快指针走到最后一个元素时,慢指针正好指向中间位置的元素。