题目链接地址:
题目描述:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
输入:
每个输入文件包含一组测试案例。
对于每个测试案例,第一行输入一个n,代表该数组中数字的个数。
接下来的一行输入n个整数。代表数组中的n个数。
输出:
对应每个测试案例,
输入一行n个数字,代表调整后的数组。注意,数字和数字之间用一个空格隔开,最后一个数字后面没有空格。
样例输入:
5
1 2 3 4 5
样例输出:
1 3 5 2 4
解题思路:
题目要求调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。这里有两个关键词:“调整顺序”和“相对位置不变”,于是利用深井冰人思维广的优势,我想到了插入排序的解法,但是这种算法超时了。因为我刚开始是用数组来做为存储结构,所以在执行插入排序算法时需要进行大量的移动数组元素的操作,我猜想这可能是程序超时的原因,于是我又将数据结构改成了双向链表,但还是超时了。。。ORZ。。。
解法一 采用归并排序
再想想排序算法中的其他稳定性排序:冒泡排序,基数排序,归并排序。在权衡了时间复杂度后,我选择了归并排序。
AC代码如下:
// 第一种算法:因为二路归并算法是稳定的,而且时间复杂度为O(logn*n)
// 所以采用模仿二路归并排序的算法,数据结构采用数组,归并算法采用非递归实现
#include<stdio.h>
#define MAX 1000000
int number[MAX]; // 用于存放原始数据
int temp[MAX]; // 用于模拟归并排序时所用的临时存储空间
/**
* 打印替换了奇偶后的数组
* @param n 数组的长度
* @return void
*/
void printResult(int n)
{
int i;
for(i = 0;i < n - 1;i++)
{
printf("%d ",number[i]);
}
printf("%d\n",number[i]);
}
/**
* 模仿归并排序 数据结构采用数组
* @param n 表示数组的长度
* @return void
*/
void getResultByMergeSort(int n)
{
int k = 2; // k表示进行一趟二路归并排序后,生成的子有序数组长度为k
int i,j,t,s;
int begin,middle,end; // 待排序的两个子序列为[begin,middle),[middle,end)
while(1)
{
/*因为k表示进行一趟二路归并排序后生成的子有序数组长度,
所以这里的条件不能是0 == n / k
否则会遗漏掉最后一趟归并排序*/
if(0 == (n * 2) / k)
break; // 表示二路归并排序完毕
else
{
begin = 0; // begin = 0表示新一趟归并排序的开始
t = 0; // t是temp数组的下标
while(begin < n) // 如果begin >= n 则表示一趟归并排序完毕
{
end = (begin + k) < n?(begin + k):n;//当(begin + k) < n,也就是最后两组待排序列长度之和小于k时,end = n;
/*注意middle = begin + k / 2,不是middle = (begin + end)/2;
因为当(begin + k)>n时,(begin + end)/2 != (begin + begin + k)/2;*/
middle = begin + k / 2;
i = begin; // i 是序列[begin,middle)的指针
j = middle; // j 是序列[middle,end)的指针
while((1 == number[i] % 2) && (i < middle)) //将"有序"子序列[begin,middle)中的奇数全部拷入到临时数组中
{
temp[t++] = number[i++];
}
while((1 == number[j] % 2) && (j < end)) // 将"有序"子序列[middle,end)中的奇数全部拷入到临时数组中
{
temp[t++] = number[j++];
}
while(i < middle) // 将[begin,middle)中的偶数拷入到临时数组中
{
temp[t++] = number[i++];
}
while(j < end) // 将[middle,end)中的偶数拷入到临时数组中
{
temp[t++] = number[j++];
}
begin += k; // 待数组[begin,begin + k)"排好序"后,紧接着就对数组[begin + k,begin + k + k)进行"排序"
}//while(begin < n)
// 每进行完一趟排序都要将临时数组中的数据拷贝到原始数组中
for(s = 0;s < n;s++)
{
number[s] = temp[s];
}
/*每次经过一趟排序后,原始序列中的[0,k),[k,k+k),...[k*(m-1),end)等子序列内部是"有序"的
采用二路归并,所以每次k的值都要乘以2*/
k = 2 * k;
}
}
printResult(n);
}
int main()
{
int i,n;
scanf("%d",&n);
for(i = 0;i < n;i++)
{
scanf("%d",&number[i]);
}
getResultByMergeSort(n);
return 0;
}
/**************************************************************
Problem: 1516
User: blueshell
Language: C
Result: Accepted
Time:80 ms
Memory:8724 kb
****************************************************************/
解法二 链表法
后来仔细想想,我看到“调整顺序”和“相对位置不变”这两个关键词就想到稳定性排序是陷入了思维定势,其实这道题根本用不着排序这么复杂。于是我又想到了以下算法:
(1) 先构建两个单链表,一个用来存放数组中奇数,一个用来存放数组中的偶数;
(2) 然后先将所有奇数拷回到原数组中,再将所有偶数拷回到原数组中。
这种算法的时间复杂度是O(n)。
AC代码如下:
// 第二种算法:构建两个单链表,一个用来存放数组中奇数,一个用来存放数组中的偶数
// 然后先将所有奇数拷回到原数组中,再将所有偶数拷回到原数组中
// 算法的时间复杂度O(n)
#include<stdio.h>
#include<malloc.h>
#define MAX 1000000
// 创建链表结点
typedef struct LNode
{
int data; // 数据域
LNode * next; // 指针域
}Linklist;
/**
* 输出调整好奇偶顺序后的数组
* @param number[] 调整好奇偶顺序后的数组
* @param n 数组的长度
* @return void
*/
void printResult(int number[],int n)
{
int i;
for(i = 0;i < n - 1;i++)
{
printf("%d ",number[i]);
}
printf("%d\n",number[i]);
}
/**
* 调整数组中数字的奇偶顺序
* @param number[] 待调整奇偶顺序的数组
* @param n 数组的长度
* @return void
*/
void adjustOddEven(int number[],int n)
{
int i;
Linklist * s = NULL; // s指向新构造的结点
Linklist * oddNumber = (Linklist *)malloc(sizeof(Linklist)); // 用于存放奇数的链表
Linklist * evenNumber = (Linklist *)malloc(sizeof(Linklist)); // 用于存放偶数的链表
oddNumber -> next = NULL;
evenNumber -> next = NULL;
Linklist * p1 = oddNumber; // p1始终指向奇数链表的最后一个结点
Linklist * p2 = evenNumber; // p2始终指向偶数链表的最后一个结点
for(i = 0;i < n;i++)
{
s = (Linklist *)malloc(sizeof(Linklist));
scanf("%d",&number[i]);
if(1 == (number[i] & 1)) // 如果输入的数据是奇数,则插入到奇数链表的末尾
{
s -> data = number[i];
s -> next = p1 -> next;
p1 -> next = s;
p1 = s;
}
else // 如果输入的数据是偶数,则插入到偶数链表的末尾
{
s -> data = number[i];
s -> next = p2 -> next;
p2 -> next = s;
p2 = s;
}
}
// 先将奇数链表中的元素全部放入到数组中,再将偶数链表中的元素全部放入数组中
i = 0;
p1 = oddNumber -> next;
p2 = evenNumber -> next;
while(p1 != NULL)
{
number[i] = p1 -> data;
p1 = p1 -> next;
i++;
}
while(p2 != NULL)
{
number[i] = p2 -> data;
p2 = p2 -> next;
i++;
}
printResult(number,n);
}
int main()
{
int n;
int number[MAX];
scanf("%d",&n);
adjustOddEven(number,n);
return 0;
}
/**************************************************************
Problem: 1516
User: blueshell
Language: C++
Result: Accepted
Time:80 ms
Memory:7884 kb
****************************************************************/