您的位置:首页 > 编程语言

国内某著名IT公司编程测验: 以数组内的三个下标四等分一个数组

2017-05-20 08:33 302 查看

国内某著名IT公司编程测验: 以数组内的三个下标四等分一个数组

论坛原贴>

http://bbs.csdn.net/topics/392114261

刚刚参加了国内某著名IT公司的编程测验,感觉有点怀疑人生了

题目如下:写一个函数,输入是一个整数数组,输出是一个布尔值,判断这个整数数组能不能四等分。什么是四等分呢?简单说,就是在这个数组里面找到三个下标,这三个下标对应的数把数组分成四个部分,这四个部分和是相等的,那么这个数组就是可以四等分的。例如:1,1,1,2,0,2,5,-1,3,找到三个下标2,4,6分成的四个部分是{1,1},{2},{2},{-1,3},这四个部分和都是2,所以这个数组是可以四等分的,暂且认为如果四个部分有一个部分是空的话和就是0,比如可以认为{1,1,1}这个数组可以四等分(这一个要求我记得不是很清楚了,其实不是很重要)。

数组长度十万以内,数组里的每个数范围正负十万之间,要求:时间复杂度O(N)以内,空间复杂度O(N)以内,N是数组长度

首先说明:如果按照题目的要求,四等分不能包括边界下标的话,这是一道动态规划的算法题,而且需要考虑比较多的因素,因为每段区间的和是不能通过总和来计算的,必须通过不断的动态规划,才能找出或不能找出这个“四等分”值来。本人才疏学浅,不知道怎样才能将动态规划问题的复杂度变为O(N)。

以下是用php实现的方法:复杂度O(M*N)

首先来讲讲思路:比如有如下满足条件的数组$arr =[1,3,1,3,1,3,1,3,4,4],长度为10,默认数组两头之外为0,首先我们把这个数组分成3份(左边,中间,右边),其中,左边 = 右边,比如这样[0], 1, [3,1,3,1,3,1,3,4,],4, [0] 又或者是这样[1,3] ,1,[3,1,3,1,3],4,[4],加粗的数字是从数组抽离的隔离点,是不纳入区间求和范围的,这点要注意。我们分成左中右后呢,接下来就是把中间那部分也分成两部分,比如以上的例子,可以是

[0], 1, [3,1],3,[1,3,]1,[3,4,],4, [0] ,

也可以是这样的:

[1,3] ,1,[3,1,],3,[1,3],4,[4]。其中只有后者才能满足条件。废话不多,首先我们来写一个分割中间那部分的方法,如以下的

div2Pairs(),首先我们得把中间这一段攫取下来,传入左右两个分界点,两个分解点往中间挤一挤,比如上面的

1,[3,1,3,1,3],4 ,挤掉分解点后,就只剩下[3,1,3,1,3]。现在要把[3,1,3,1,3]分成两等份(如果可以等份),因为我们必须保留一个分解点,所以不能通过求总和折半的方式,为了保证尽可能的分成两等份,我们可以分别从两头开始,假设从左边开始的和为lsum,右边开始的为rsum ,那么我们应该确保每一步操作中总有|lsum−rsum|是最小的,以避免错过任何一个可能的lsum==rsum,以下为这个方法具体实现:

<?php
/**
* [div2pairs divide the array into two group ]
* @param  int    $avg        [dynamic average]
* @param  int    $ldeliIndex [the left delimeter]
* @param  array  &$array     [the array to divide by lsum compared rsum]
* @param  int    $rdeliIndex [the right delimeter]
* @return [mixed]             [if true return the middle delimeter,or false]
*/
public function div2pairs(int $avg,int $ldeliIndex,array&$array,int $rdeliIndex){
$lstart = $ldeliIndex+1;  //the index where begin to calculate the sum from left;
$rstart = $rdeliIndex-1;  //the index where begin to calculate the sum from right;
$lsum = $array[$lstart]; //init the $lsum
$rsum = $array[$rstart]; //init the $rsum
$gap = $lsum-$rsum;
while($lstart<$rstart){   //although within two 'while' but its time complexity is truely o(n);
while($lsum>$rsum && $lstart<$rstart-2){ //here the number 2 means that there must be
$rsum+=$array[--$rstart];                 //a delimeter index can't not put into calculate
if($lsum-$rsum>$gap && $lstart<$rstart-2){ //avoid the gap being bigger;
$lsum+=$array[++$lstart];
}
$gap = $lsum-$rsum;
}
while ($lsum<$rsum && $lstart<$rstart-2) {
$lsum+=$array[++$lstart];
if($lsum-$rsum<$gap && $lstart<$rstart-2){
$rsum+=$array[--$rstart];
}
$gap = $lsum-$rsum;
}
if($rsum==$lsum){
if($rstart ==$lstart+2){
if($rsum ==$avg) //true return the dividing delimeter
return $lstart+($rstart-$lstart)/2;
else
return false;
}else{
++$lstart; //you can also change these two lines like this:
$lsum+=$array[$lstart];  //--$rstart;$rsum+=$array[$rstart]
}
}else{
if($lsum<$rsum){ //here and 'while' is used to make the $lsum and $rsum more close;
++$lstart;
$lsum+=$array[$lstart];
}else{
--$rstart;
$rsum +=$array[$rstart];
}
}
$gap = $lsum-$rsum;  //upgrade the gap between $lsum and $rsum;
}
return false;
}

但是在拆分中间那部分时,我们需要先确保,左边 = 右边($lsum ==$rsum),
确保动态的可能是最终结果的区间和$avg,我们使用与div2Pairs()里相类似
的方式,两头开工,获取$lsum ==$rsum==$avg使他们相对应的两个分解点($ldelimeter 和 $rdelimeter),
然后代入div2Pair()就可以了。
但是由于所有的m个可能的div2Pair()都需要用到,所以我们不能每次都去
求一边,我们可以先用一个数组$lsumArray 先把$lsum所有下标都村起来,
以方便$rsum动态调用。以下的div4Pairs()将数组四等分返回左中右三个
分界点,或不能等分返回false

/**
* [div4pairs divide the array into 4 pairs with delimeter]
* @param  array  $array [array to divide]
* @return [mixed]        [if true then return an array cluding all delimeter]
*/
public function div4pairs(array$array){
$length = count($array); //get the lengt of the array;
if($length<5) return false; //because divide into 4 equals with delimeter
$lsumArray = []; //array to store the lsum which calculate from 0;
$lsumArray[0][] = -1; //$lsumArray[$lsum] = index, if index<0 ,default -1
$end = $length-2; //the 4th pair must be >=0,default $array[$length] = 0 as well as the
//$array[-1] = 0,thought they do not exist
for ($i = 0,$lsum = 0; $i < $end; ++$i) {
$lsum+=$array[$i];
$lsumArray[$lsum][] = $i;  //store the index for each lsum;
}
for($j = $length-1,$rsum = 0;$j>3;--$j){ //from right to left ,calculate the rsum;
if(isset($lsumArray[$rsum])){         //
while(false!==current($lsumArray[$rsum])){
$ldeliIndex = current($lsumArray[$rsum])+1;
if($ldeliIndex>$j-4) break;   //3 elements between ldelimeter and rdelimeter($j);
if(($mid=self::div2pairs($rsum,$ldeliIndex,$array,$j))) //try to divide array into 4 equals
return [$ldeliIndex,$mid,$j]; //true then return these three delimeters index;
next($lsumArray[$rsum]);
}
}
$rsum+=$array[$j];
}
return false;
}

以下这个是最常规的方法,简单一目了然。空间复杂度O(N) 时间复杂度O(N^2)
/**
* [div4pairsF another simple method to divide array into 4 equals with excluding delimeter index]
* @param  array  $array [array to divide]
* @return [mixed]        [if true return delimeters index,or false]
*/
public function div4pairsF(array$array){
$length = count($array);
$ilength = $length-4;
$jlength = $length-2;
for ($i=0,$isum = 0;$i < $ilength; ++$i) {
for ($j=$i+1,$jsum = 0; $j < $jlength; ++$j) {
$jsum+=$array[$j];
if($jsum==$isum){
++$j;
for($k = $j+1,$ksum = 0;$k<$length;++$k){
$ksum+=$array[$k];
if($ksum==$isum){
++$k;
if($k+1<$length){
for ($n=$k+1,$nsum=0; $n < $length; ++$n) {
$nsum+=$array[$n];
}
if($nsum==$isum)
return [$i,$j,$k];
}
}
}
}
}
$isum+=$array[$i];
}
return false;
}

public function createTestingData(int $m){
for ($i=0,$data=[]; $i < $m; ++$i) {
$in = mt_rand(-$m,$m);
$data[] = $in;
}
return $data;
}

check:
$all = [1,2,5,4,-1];  // false
$all = [1,-1,2,-1,4,-3,4,2,1];  //array(3) { [0]=> int(1) [1]=> int(4) [2]=> int(7) }
$all = [1,3,1,3,1,3,1,3,4,4];  //Array ( [0] => 2 [1] => 5 [2] => 8 )
// $res = AlgorithmicTest::div4pairsF($all);
$res = AlgorithmicTest::div4pairs($all);
print_r($res);

测试两种方法

$n = 100;//1000,10000,100000,1000000
$all = AlgorithmicTest::createTestingData($n);
echo memory_get_usage(),'<br>';
$s = microtime(true);
$res = AlgorithmicTest::div4pairs($all);
// $res = AlgorithmicTest::div4pairsF($all);
echo 'times:      ',microtime(true)-$s,'<br>';
echo memory_get_peak_usage(),'<br>';

结果:
memory:byte times:s

div4pairsF($all) n=100
memory used before processing:447216
times: 0.00018191337585449
memory used_peak in processin: 612104

div4pairs($all)n=100
memory used before processing:447176
times: 6.9141387939453E-5
memory used_peak in processin: 612104

div4pairsF($all) n=1000
memory used before processing:475888
times: 0.010547161102295
memory used_peak in processin: 612104

div4pairs($all) n=1000
memory used before processing:475848
times: 0.00043797492980957
memory used_peak in processin: 880960

div4pairsF($all) n=10000
memory used before processing:967408
times: 0.97418403625488
memory used_peak in processin: 967664

div4pairs($all)n=10000
memory used before processing:967368
times: 0.0051088333129883
memory used_peak in processin: 5293064

div4pairsF($all) n=100000 (已经阵亡!)
memory used before processing:4637448
(30s内无法完成)
Fatal error: Maximum execution time of 30 seconds exceeded in /usr/local/nginx/html/algorithmic/algorithmic_test/AlgorithmicTest.php on line 741

div4pairs($all) n=100000
memory used before processing:4637408
times: 0.089447975158691
memory used_peak in processin: 46877568

div4pairs($all) n=1000000
memory used before processing:33997536
times: 3.2110240459442
memory used_peak in processin: 447408504

欢迎交流!


“`
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐