算法-数组元素位置调整
这类题目有点特别,不要求我们对数组元素进行像正常排序那样从大到小或者从小到大,而是要求我们使用O(N)的方法将符合条件的元素排在前面或者后面。其实这是排序的一种变形,不需要我们消灭逆序数。
解决思路有两类:
1、利用快排思想,将符合条件数据放在前面或后面,一遍遍历即可完成
2、指针的思想
2、利用冒泡排序思想,我们将符合条件的数据冒在前面,而且我们不需要消灭逆序数,因此每个元素访问2次,所以时间复杂度仍然是O(N)
1、给定一个数组,将奇数(正数…etc)排在左边,偶数排在右边
以奇数为例,使用快排思想,快排中我们选择的枢纽元其实是作为一个条件出现的,而在这里,只不过这个“枢纽元”换成了奇数偶数而已。
/**
* 利用快排思想,复杂度O(n)
* @param nums
*/
public static void reOrder(int[] nums){
int left=0,right=nums.length-1;
while(left<right){
while (left<right&&(nums[left]&1)==1){left++;}//找到第一个偶数
while(left<right&&(nums[right]&1)==0){right--;}//找到第一个奇数
if(left<right){
swap(nums,left,right);//交换数据
}
}
}
我们还可以有另一种操作: 指针法
使用一个指针指向当前第一个不符合条件的位置,然后遍历数组,每次遇到符合条件的,我们就可以将其交换,这样就可以实现题目要求。而且这个方法写起来非常简洁
public static void reOrder(int[] nums){
int ptr=0;
for(int i=0;i<nums.length;i++){
if((nums[i]&1)==1){//判断奇数偶数
swap(nums,ptr++,i);
}
}
}
2、给定一个数组,将奇数(正数…etc)排在左边,偶数排在右边(保证相对位置不变)
这类题目和上面相比,稍微复杂了点,这里要求我们维持相对顺序不变。冒泡法给我们带来了希望。已经冒过的位置不在冒泡,说明每个元素被访问了两次,所以时间复杂度为O(N)
我们也用一个指针指向第一个不符合条件的位置,然后遍历数组,冒泡冒到那个位置
public static void reOrder(int[] nums){
int ptr=0;
for(int i=0;i<nums.length;i++){
if((nums[i]&1)==1){//判断奇偶数
int curr=i;
while(curr>ptr){//冒到第一个不符合条件的那个位置
swap(nums,curr,--curr);//前后交换
}
ptr++;
}
}
}
2中的代码可以直接放在1中使用
//交换元素
public static void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
3、将数组中0移动到数组尾部
题目很简单,直接套壳就好了。。。
public void moveZeroes(int[] nums) {
int ptr=nums.length-1;
for(int i=nums.length-1;i>=0;i--){
if(nums[i]==0){
int curr=i;
while(curr<ptr){
int temp=nums[curr];
nums[curr]=nums[curr+1];
nums[curr+1]=temp;
curr++;
}
ptr--;
}
}
}
不过,我们其实可以针对本题进行优化。因为我们知道特定的数据为0,那么我们就可以统计非零个数,然后将后面置零。
public void moveZeroes(int[] nums) {
int noZeroIndex=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
nums[noZeroIndex++]=nums[i];
}
}
for(int i=noZeroIndex;i<nums.length;i++){
nums[i]=0;
}
}