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

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

给定一个数组,要求在数组里扣掉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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值