面试题14:调整数组顺序使奇数位于偶数前面
一.题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
二.分析问题
解法1
这个题目要求把奇数放在数组的前半部分,偶数放在数组的后半部分,因此所有的奇数应该位于偶数的前面。也就是说我们在扫描这个数组的时候,如果发现有偶数出现在奇数的前面,我们可以交换它们的顺序,交换之后就符合要求了。
因此我们可以维护两个指针,第一个指针初始化时指向数组的第一个数字,它只向后移动;第二个指针初始化时指向数组的最后一个数字,它只向前移动。在两个指针相遇之前,第一个指针总是位于第二个指针的前面。如果第一个指针指向的数字是偶数,并且第二个指针指向的数字是奇数,我们就交换这两个数字。
下面以一个具体的例子比如输入数组{1,2,3,4,5}来分析这种思路。
(a)把第一个指针指向数组的第一个数字,第二个指针指向最后一个数字。
(b)向后移动第一个指针直至它指向偶数2,此时第二个指针指向奇数5,不需要移动。
(c)交换两个指针指向的数字。
(d)向后移动第一个指针直至它指向偶数4,向前移动第二个指针直至它指向奇数3。由于第二个指针移到了第一个指针的前 面,表明所有的奇数都位于偶数的前面。
解法1的改进
如果把题目改成把数组中的数按照大小分为两部分,所有负数都在非负数的前面,该怎么做?
如果再把题目改改,变成把数组中的数分为两部分,能被3整除的数都在不能被3整除的数的前面。怎么办?
我们发现要解决这两个新的问题,其实只需要修改函数ReorderOddEven中的两处判断的标准,而大的逻辑框架完全不需要改动。因此我们可以把这个逻辑框架抽象出来,而把判断的标准变成一个函数指针,也就是用一个单独的函数来判断数字是不是符合标准。这样我们就把整个函数解耦成两部分:一是判断数字应该在数组前半部分还是后半部分的标准,二是拆分数组的操作。
三.代码
解法1
#include<stdio.h>
#include<stdlib.h>
void ReorderOddEven(int* pData, unsigned int Length)
{
if (pData == NULL || Length == 0)
{
return;
}
int* p1 = pData;
int* p2 = pData + Length + 1;
while (p1 < p2)
{
/*向后移动p1,直到它指向偶数*/
while (p1 < p2 && (*p1 & 0x1) != 0)
{
p1++;
}
/*向前移动p2,直到它指向奇数*/
while (p1 < p2 && (*p2 & 0x1) == 0)
{
p2--;
}
if (p1 < p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
}
}
void Show(int* arr,int Length)
{
for (int i = 0; i < Length; ++i)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 1,2,3,4,5,9,0,3,44};
int len = sizeof(arr) / sizeof(int);
Show(arr, len);
ReorderOddEven(arr, len);
Show(arr, len);
return 0;
}
运行结果: