如何用O(n)实现四等分数组

来源:互联网 发布:mac 怎么使用语音朗读 编辑:程序博客网 时间:2024/05/21 10:20

第一次写博客还有点小紧张。前几天在一个比赛群里看到一道很有意思的算法题,题目如下:

给定一个数组,要求在数组里扣掉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;    }}

晚安。

4 0