九度OJ-题目1516:调整数组顺序使奇数位于偶数前面

题目链接地址:

九度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
****************************************************************/


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值