python - 给定一个2n长的数组将奇数放在偶数前面
PHPz
PHPz 2017-04-17 17:58:17
0
17
2503

给定一个2n长数组,其中n个奇数和n个偶数,对数组进行排序将奇数放在前半部分,偶数放在后半部分。要求:

  1. 不改变原来的奇偶各自的相对顺序

  2. 只申请常数的空间

  3. 时间复杂度为O(n)

举例:给出1 2 3 4 5 6
排序后为 1 3 5 2 4 6

PS:请一定仔细阅读3个条件,去掉其中任意一个都变得简单,并且网上我搜到的答案都是缺少其中某个条件的。因此我怀疑这题是否有解。


看了大家的回答,基本可以分为2种情况:

  1. 用链表可以轻易解决这道题,当然如果把数组转成链表因为需要申请2n长度的next数组,所以认为还是申请额外空间了

  2. 只管输出,如果只要求输出结果那遍历2遍就行了,但这样题目变得太过简单

因为这是一道面试题,我想可以从上面2方面和面试官沟通,我只是凭记忆写下这题,其中也许有自己的一些思维定势(比如没有强调一定是数组,或者没有强调必须要求数组排序只要求输出)。看了大家的讨论还是很有启发的。


找到了国外的链接,看了一圈讨论大部分认为没有O(n)时间O(1)空间的解法
https://www.careercup.com/question?id=5201559730257920


另外说下我自己对这题的一个思考,可以观察到,假如一个数组是符合最终条件的,那么发现当且仅当只交换相邻的两个奇偶数是不会破坏相对顺序的,那么只需给出一个策略找出从最终状态转到题目起始状态的就行了。
另外,不要纠结于奇偶分别的大小问题,比如4 5 3 1 2 6和2 1 3 5 4 6是等价的,只是一个简单的替换,只要将奇数按1 3 5……这样的顺序,偶数按2 4 6……这样的顺序排好就行了。

PHPz
PHPz

学习是最好的投资!

全部回复(17)
巴扎黑

很简单的
用计数排序
假定你的数字范围在0到65535范围之内,定义一个数组count[65536](这个空间是常量,和n无关,所以是O(1) ),初值全部为0。
那么假设有下面这些数字:
100
200
300
119
0
6
...
那么对于每个这个数字,都做在count中记录一下:
100 => count[100]++
200 => count[200]++
300 => count[300]++
119 => count[119]++
0 => count[0]++
6 => count[6]++
...
也就是说:
首先,遍历一遍所有这些数字就可得到0~65535每个数字的个数(在count数组中)
然后,再顺序遍历count数组,count[n] = m,则输出m个n,(比如说有count[3] = 2, 那么说明有2个数字3),依次输出,最后可得结果。
在输出时:
先输出奇数count[1],count[3]...
再输出偶数count[0],count[2]...
复杂度分析:
第一次遍历是O(n),第二次遍历是O(1),为常量,所以最后的时间复杂度为O(n),而空间复杂度为O(1)

PS:这个算法很简单,相信大家都会,只是这个题太过于变态了,一般会把面试者吓住

洪涛

O=orgin list

N=[None]*2n
for i in O:

odd=0
even=n
if int(i/2)==i/2:
    N[even]=i
    even+=1
else:
    N[odd]=i
    odd+=1

主要就是空间问题,不占用一个2n的空间应该是不能实现的,因为情况复杂,没法确定常数空间就够用!

Ty80
var l = [1, 2, 4, 6, 3, 5]
var i=0, item, last = l[l.length-1], count=0;
while(i<l.length){
  count++;
  item = l[i];
  if(item == last){
    break;
  }
  if(item % 2 == 0){
    l.splice(i, 1);
    l.push(item);
  }else{
    i++;
  }
}
console.log(count); //循环执行了多少次
console.log(l); //最后结果

js实现应该是这样的,偶数时释放一个,然后追加到末尾,当处理到初始数组最后一个时停止。 参考了前面moling3650的代码。。

黄舟

刚我赞了@白汀 马上有人踩
可能有人觉得申请了n-1个空间,空间复杂度不是常量。
我用php来输出来验证下,事实上增加的内存是常量,php5.4测试增加内存量为:160

<?php
$arr = range(1,20);  //生成1-20组成的数组
$n = count($arr); //统计数组个数
$old = memory_get_usage(); //统计页面占用内存

/* 开始处理数组 */
for($i=0;$i<$n;$i++){
    if($arr[$i]%2==0){
        $arr[] = $arr[$i];
        unset($arr[$i]);
    }
}
/* 处理结束 */

echo memory_get_usage()-$old; //输出当前占用内存减去原来内存,修改前面数组个数range(1,20),range(1,20000)等,这里输出值固定为:160

var_dump($arr); //输出数组,数组下标到了3n-1,可能会觉得申请了n-1的空间
阿神

有时间复杂度为O(n)的排序吗

左手右手慢动作

看了评论,开始我也以为这个题目无解,但是今天下班路上突然灵机一动,想到一个方法,简单验证了一下,发现确实可以。
这个算法只需要O(1)的空间,O(n)的时间,并且不会改变原数组中奇数和偶数之间的相对顺序,完全符合题目要求。今天先说一下算法思路,明天抽空把程序补上。O(1)的空间,O(n)的时间,并且不会改变原数组中奇数和偶数之间的相对顺序,完全符合题目要求。今天先说一下算法思路,明天抽空把程序补上。

简单来说就是从前到后扫描各个元素,扫描时使用2个指针,一个用来进行常规遍历(就是平时我们遍历数组使用的i);另一个始终指向数组中的第一个偶数,这里称为偶数指针,初始化为-1

遍历开始,对于每一个元素,分为以下几种情况:

  1. 该元素为奇数,并且它之前有偶数(偶数指针大于等于0),交换这两个数

  2. 该元素为奇数,并且它之前没有偶数(偶数指针为-1),不做任何操作,继续下一轮遍历

  3. 该元素为偶数,并且它的前一个元素为偶数(注意是前一个元素,不是偶数指针指向的那个元素),交换这两个数

  4. 该元素为偶数,并且它的前一个元素为奇数,此时偶数指针一定是-1,将其赋值为当前元素的索引(即i

如此一直重复到倒数第二个元素,而对于最后一个元素需要特殊处理:如果它是奇数则按照上面1的步骤来处理;如果它是偶数,则不做任何处理,遍历完成。

举个例子,1 2 3 4 5 6,下面是每一轮遍历之后数组的状态([]

简单来说就是从前到后扫描各个元素,扫描时使用2个指针,一个用来进行常规遍历(就是平时我们遍历数组使用的i);另一个始终指向数组中的第一个偶数,这里称为偶数指针,初始化为-1


遍历开始,对于每一个元素,分为以下几种情况:


  1. 该元素为奇数,并且它之前有偶数(偶数指针大于等于0),交换这两个数

  2. 该元素为奇数,并且它之前没有偶数(偶数指针为-1),不做任何操作,继续下一轮遍历
  3. 该元素为偶数,并且它的前一个元素为偶数(注意是前一个元素,不是偶数指针指向的那个元素),交换这两个数

  4. 该元素为偶数,并且它的前一个元素为奇数,此时偶数指针一定是-1,将其赋值为当前元素的索引(即i


如此一直重复到倒数第二个元素,而对于最后一个元素需要特殊处理:如果它是奇数则按照上面1的步骤来处理;如果它是偶数,则不做任何处理,遍历完成。

举个例子,1 2 3 4 5 6,下面是每一轮遍历之后数组的状态([]表示当前遍历到的元素和偶数指针指向的元素):

[1] 2 3 4 5 6
1 [2] 3 4 5 61 3 [2] 4 5 6🎜1 3 [4] [2] 5 6🎜1 3 5 [2] [4] 6🎜1 3 5 [2] 4 [6] // 完成🎜 🎜再举个例子,1 2 4 6 8 5 7 3:🎜 🎜[1] 2 4 6 8 5 7 3🎜1 [2] 4 6 8 5 7 3🎜1 [4] [2] 6 8 5 7 3🎜1 [4] 6 [2] 8 5 7 3🎜1 [4] 6 8 [2] 5 7 3🎜1 5 [6] 8 2 [4] 7 3🎜1 5 7 [8] 2 4 [6] 3🎜1 5 7 3 [2] 4 6 [8] // 完成🎜
阿神

卧槽,瞬间体现 JavaScript 是世界上最好的语言了

数组即链表,支持各种操作,屌吧

补:别睬我了,PHP 是世界上最好的语言,我错了

再补:说我不懂数据结构的大概都没 get 到我调侃的语气吧...

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板