第一次写博客还有点小紧张。前几天在一个比赛群里看到一道很有意思的算法题,题目如下:
给定一个数组,要求在数组里扣掉3个数后,所得到的4部分字数组的和相等,要求时间复杂度O(n)
题目本身并不难,难在要控制时间复杂度在O(n),一开始想了很多算法,时间复杂度还是控制不住,经过半天的思考,最终完成了代码(自己简单的测试了一下,思路应该是没问题,没找到OJ,测试数据也懒的想,有兴趣的朋友可以自行测试)。首先声明几点:
1.题目没有说数字的正负,现在好多算法题不够严谨,后来得知全部为正整数,思路一下就有了,但时候后来倒回来想,负数的情况应该也可以满足。
2.不知道有多少人会看到这篇文章,如果有什么问题,欢迎讨论,接受各种意见(大神无情的嘲讽都可以),重在交流。
回到正题,算法大致的思路是,首先计算出所有数字的和total,通过total可以算出每部分和的可能性,比如total为39,等分为4部分,则每一部分的和可能为1~9(保证在扣掉3个数字以后的total可以被4整除),大致划分出范围,便于之后的计算。
分别从数组的左右两边开始推进,维护left和right指针,同时对俩边各自的和进行累加,当两边和出现相等的情况时,我们认为此时存在有解的可能,需要进行下一步的判断。一开始在这里想了很多办法,一旦使用循环,复杂度必定大于O(n)。
关键的一部在于利用HashMap把本来的线性复杂度降为O(1)。下面贴一部分代码捎带讲解:
//key为当前节点距0的距离,value为当前节点的序号
HashMap<Integer, Integer> axis = new HashMap<>();
int total = 0;
for(int i = 0;i < len; ++i) {
total += data[i];
axis.put(total, i + 1);
}
我们可以把整个数组抽象成一条延X方向的数轴,每个节点data[i]在数轴上对应的x坐标为data[0]~data[i]累加的和,把坐标作
为HashMap的key,对应的value是数组下标+1(稍后会看到value的作用)。
继续上面的思路,当然左右累加和相等时,我们需要进一步的计算得出是否满足四等分条件,这时通过计算可以得出第二和
第三部分对应的坐标(没错,就是HashMap),我们通过get()的返回值可以知道在假设有解的情况下,数组是否可以满足四等分。等
等,还差一步,即使我们通过计算得出,第二和第三部分可以被划分出来,我们只能扣掉3个值,二三中间是否有且仅有一个数字,
value发挥的作用时刻到了,value是每个节点的序号,可以直接判断得出结果。
以上就是所有的解题思路,蛮有意思的一道题(据说是阿里面试的时候给出的),比较符合校招的风格,题目不难,比较考验技
巧和平时对算法的练习程度,博主今年大三,很快面临实习,希望可以有个满意的结果,一起加油。最后贴上全部代码:
import java.util.HashMap;
/**
* Created by Administrator on 2017/3/3.
*/
public class Divide {
public static void main(String[] args) {
// int[] data = {1, 8, 5, 2, 7, 9, 3, 6, 12, 2, 2, 5};
// int[] data = {1, 8, 5, 2, 1, 3, 3, 2, 12, 2, 2, 5};
int[] data = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
System.out.println(divide(data));
}
public static boolean divide(int[] data) {
int len = data.length;
//key为当前节点距0的距离,value为当前节点的序号
HashMap<Integer, Integer> axis = new HashMap<>();
int total = 0;
for(int i = 0;i < len; ++i) {
total += data[i];
axis.put(total, i + 1);
}
int max = 0;
for(int i = total;i > 0; --i) {
if(i % 4 == 0) {
max = i / 4;
break;
}
}
System.out.println(total + " " + max);
int left = 1;
//左边开始累加
int addL = data[0];
int right = len - 2;
//右边开始累加
int addR = data[len - 1];
while(left < right) {
System.out.println(addL + " " + addR);
if(addL == addR && addL <= max) {
int low = 2 * addL + data[left];
int high = total - (2 * addR + data[right]);
System.err.println(low + " " + high);
if(axis.get(low) != null && axis.get(high) != null && axis.get(low) + 1 == axis.get(high)) {
return true;
}
}
if(addL < addR) {
addL += data[left];
++left;
}else {
addR += data[right];
--right;
}
}
return false;
}
}
晚安。