题目链接地址:
九度OJ-题目1516:调整数组顺序使奇数位于偶数前面
题目描述:
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
输入:
每个输入文件包含一组测试案例。
对于每个测试案例,第一行输入一个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 ****************************************************************/