首页 > 代码库 > 剑指offer (14) 重组数组使得 奇数在偶数前

剑指offer (14) 重组数组使得 奇数在偶数前

题目描述:

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分

解题分析:

其实就是快速排序的思想. 回想一下快速排序的Partition划分函数,每执行一次划分操作,我们就可以 确定中轴值的最终位置,

也就是 <= 中轴值的所有元素都在 其左边,所有 > 中轴值的元素都在 其右边

 

我们可以从此获得启发,现在题目要求是 所有奇数在左边,所有偶数在右边, 模拟快速排序的划分操作,我们选取一个 偶数,然后执行划分操作,

执行完划分操作之后,我们可以让 所有奇数都在 左边,所有偶数都在 右边 

需要注意:我们选取的中轴值一定要是偶数

 

 1 bool IsEven(int a) 
 2 {
 3     return (a % 2 == 0);
 4 }
 5 
 6 typedef bool (*pfunc)(int a, int b);
 7 
 8 bool func(int a, int b)
 9 {
10     return (!IsEven(a) && IsEven(b));
11 }
12 
13 
14 
15 void Partition(std::vector<int>& vec, pfunc func)
16 {
17     if (vec.size() <= 1) return;
18     int midIndex = 0;
19     auto iter = std::find_if(vec.begin(), vec.end(), IsEven);
20     if (iter == vec.end()) {
21         return;
22     } else {
23         std::swap(vec.at(0), *iter);
24     }
25     for (int i = 1; i < vec.size(); ++i) {
26         if (func(vec.at(i), vec.at(0))) {
27             std::swap(vec.at(++midIndex), vec.at(i));
28         }
29     }
30     std::swap(vec.at(midIndex), vec.at(0));
31 }

扩展:

1. 需要重排数组,所有负数在前,正数在后.  这个解题思路完全相同,只需要 将 IsEven函数稍稍修改一下  return (a > 0) 即可

1. 需要重排数组,所有能整除3的数在前,不能整除的在后

 这个解题思路也是完全相同,只需要 将 IsEven函数稍稍修改一下  return (a % 3 != 0) 即可

我们可以看到 我们需要的仿函数必须满足 处于数组后面的元素的条件要求

 

相关题:

给定大小为n的数组A,A中的元素有正有负。请给出方法,对其排序,保证:

  • 负数在前面,正数在后面

  • 正数之间相对位置不变

  • 负数之间相对位置不变

能够做到时间复杂度为O(n),空间复杂度为O(1)么?

 

解题分析:

我们在上一题中,改变了 数组元素之间的相对位置

首先,暴力法可行:从左到右扫描数组,遇到第一个负数,与其前面的每一个元素进行交换,直到第一个位置,这里并不能直接交换,

因为这样就改变了正数的相对位置了。后面的继续扫描,第二个负数,依次交换到第二个位置。依次类推。算法总的时间复杂度为O(n^2)。(其实是插入排序的思想)

上面这个方法,大多数同学,都可以给出。那么,是否有更快的方法呢?大家请看下面的方法:

  • 统计负数的个数,设为M

  • 找到索引下标 k>= M的第一个负数

  • 使用i和j两个索引,i从0开始,直到遇到第一个正数,j从k开始,直到遇到第一个负数。交换i,j位置上的数,然后符号取反

  • 对于A[0,M]和A[M, n]分别执行上面三步

  • 修正符号:前面的M个为负数,后面的为正数。

下面举例来说明,对于数组{-1,1,3,-2,2},根据描述,有M=2,k=3。i遇到第一个正数为A[1]=1,j遇到第一个负数为A[3]=-2。

然后交换i和j位置上的值, 数组变为{-1, -2, 3, 1, 2},  然后改变符号,得到{-1,2,3,-1,2}。

然后递归处理{-1,2},{3,-1,2},最终得到{-1,2,1,-3,2}。

进行最后一步,修正符号, 得到{-1,-2,1,3,2}。即为最终答案。

 

这个方法是nlog(n)的,比上面的提高了一些。

但是上面的方法,仍旧不是O(n)的。那么O(n)的方法,是否存在呢?我们认为,接近O(n)的方法是有的。但是,方法过于复杂。这类问题,可以统称为 stable 0-1 sorting。

大家感兴趣的话,可以参考下面的文章:

http://www.diku.dk/hjemmesider/ansatte/jyrki/Paper/KP92b.pdf

 

注意:

1. 本题空间复杂度是 O(1),如果没有空间复杂度限制,可以用空间换时间,使用O(n)空间,可以非常容易的在O(n)时间内解决

2. 本题在O(n)时间 O(1)空间的限制下是没有真正的解决方法的,如果存在的话,那么 快速排序就可以是稳定的了