面试题21:调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

 

1.要想保证原有次序,则只能顺次移动或相邻交换。

2.i从左向右遍历,找到第一个偶数。

3.j从i+1开始向后找,直到找到第一个奇数。

4.将[i,...,j-1]的元素整体后移一位,最后将找到的奇数放入i位置,然后i++。

5.終止條件:j向後遍歷查找失敗。

 

1.暴力解法

最简单的思路就是从头到尾扫描一遍数组,每遇见一个偶数时,就拿出这个数字,并把位于这个数字之后的所有数字往前挪动一位,然后把当前这个偶数放到最后。

这样每次遇到一个偶数就要挪动O(n)个数字,因此总的时间复杂度是O(n^2)

但是这种方法不仅暴力而且还需要复杂的挪动工作,因此我们对比一下冒泡排序,每次循环前偶后奇就交换

同时我们设立一个标识,来标识数组是否已经符合要求

当再次循环时,发现没有要交换的数据,说明数组已经符合要求

//两个思路吧,第一个思路:类似冒泡算法,前偶后奇数就交换:
class Solution {
public:
    void reOrderArray(vector<int> &array) {
 
         
        for (int i = 0; i < array.size();i++)
        {
            for (int j = array.size() - 1; j>i;j--)
            {
                if (array[j] % 2 == 1 && array[j - 1]%2 == 0) //前偶后奇交换
                {
                    swap(array[j], array[j-1]);
                }
            }
        }
    }
};

2.辅助数组完成

我们也可以利用一个辅助的数组空间来实现

在原来的数组中遇到偶数就放进新数组中,然后将该偶数从原来数组中删除,当整个遍历结束后,原来数组中只剩下奇数,新的数组总只剩下偶数,最后我们将新的数组追加在原来数组的末尾即可

//第二个思路:再创建一个数组
class Solution{
public:
    void reOrderArray(vector<int> &array) {
 
        vector<int> array_temp;
        vector<int>::iterator ib1, ie1;
        ib1 = array.begin();
 
 
        for (; ib1 != array.end();){
        //遇见偶数,就保存到新数组,同时从原数组中删除
            if (*ib1 % 2 == 0) {
                array_temp.push_back(*ib1);
                ib1 = array.erase(ib1);
            }
            else{
                ib1++;
            }
 
        }
        vector<int>::iterator ib2, ie2;
        ib2 = array_temp.begin();
        ie2 = array_temp.end();
 
        for (; ib2 != ie2; ib2++)             
        //将新数组的数添加到老数组
        {
            array.push_back(*ib2);
        }
    }
};

3.高效解法

由于题目中只要求记奇数在偶数之前,那么我们在扫描这个数组的时候,如果发现一个偶数出现在奇数之前就交换他们的位置,这样一趟后就满足要求了。

因此我们维护两个索引或者指针,一个指向数组的第一个元素,并向后移动,一个指向数组的最后一个元素,并向前移动。如果第一个指针指向的元素是偶数,而第二个指针指向的元素是奇数,说明偶数在奇数前面,那么就交换这两个数。直到两个指针相遇为止

 

剑指offer中的原答案是没有考虑到奇偶交换之后还要保持相对顺序不变的

以下是原答案:

//剑指offer上的解法,但是相对位置改变了
void reOrderArray(vector<int> &array) {
  if(array.size() <= 1)
    return;

  vector<int>::iterator left = array.begin();
  vector<int>::iterator right = array.end() - 1;

  // 三种顺序容器中只有deque和vector容器的迭代器支持大小的比较和算术运算。

  while(left < right)
  {
    while(left < right && (*left & 0x1) != 0)
      left++;
    while(left < right && (*right & 0x1) == 0)
      right--;
    if(left < right)
    {
      swap(*left, *right);
    }
  }
}

牛客网友的高赞解答(Java):

/**
 * 1.要想保证原有次序,则只能顺次移动或相邻交换。
 * 2.i从左向右遍历,找到第一个偶数。
 * 3.j从i+1开始向后找,直到找到第一个奇数。
 * 4.将[i,...,j-1]的元素整体后移一位,最后将找到的奇数放入i位置,然后i++。
 * 5.終止條件:j向後遍歷查找失敗。
 */
public void reOrderArray2(int [] a) {
    if(a==null||a.length==0)
        return;
    int i = 0,j;
    while(i<a.length){
        while(i<a.length&&!isEven(a[i]))
            i++;
        j = i+1;
        while(j<a.length&&isEven(a[j]))
            j++;
        if(j<a.length){
            int tmp = a[j];
            for (int j2 = j-1; j2 >=i; j2--) {
                a[j2+1] = a[j2];
            }
            a[i++] = tmp;
        }else{// 查找失敗
            break;
        }
    }
}
boolean isEven(int n){
    if(n%2==0)
        return true;
    return false;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值