国内某著名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 欢迎交流!
“`
相关文章推荐
- 输入一个数组,判断能否抽取三个数,余下的数正好以抽取数的位置将原始数组四等分
- 国内主流IT公司Field Sales销售薪资 来自著名猎头公司内部信息 给IT的朋友们知道个行情 - 天涯
- 国内著名IT公司(百度、搜狗、网易、新浪)2012校园招聘笔试、面试小结
- 国内著名IT公司官网log荟萃
- 阿里在线编程,去除三个元素,四等分数组问题!
- 求一个数组中三个最大值的下标
- 国内著名IT公司(百度、搜狗、网易、新浪)2012校园招聘笔试、面试小结
- 面试例题:输入n,求一个n x n矩阵,规定矩阵沿45度递增,形成一个zigzag数组(JPEG编码里取像素数据的排列顺序),请问如何用C++实现? (中国台湾著名硬件公司2007年11月面试题)
- 国内著名IT公司(百度、搜狗、网易、新浪)2012校园招聘笔试、面试小结
- 阿里在线编程,去除三个元素,四等分数组问题!
- 国内著名IT公司(百度、搜狗、网易、新浪)2012校园招聘笔试、面试小结
- 给一个整形数组,给出一个值,当这个值是数组某些数字的和,求出数组下标的值
- 针对国内著名、非著名的编程书籍作者我有话要说
- 获取一个整型数组前三个最大的值
- 看看您该挣多少 2005年国内各大IT公司的薪酬一览(二)
- 用一个数组实现三个栈
- 揭密:著名IT公司名子的来历
- 程序员面试金典: 9.3栈与队列 3.1描述如何只用一个数组来实现三个栈
- 给定一个整数数组,在该数组中,寻找三个数,分别代表三角形三条边的长度,问,可以寻找到多少组这样的三个数来组成三角形?
- [杂记]国内某两家IT公司面试经验