排列组合取数组中n个数的和等于m

算法是抽象的概念,但越是抽象的东西,其越具有清晰的特征。特征如下:

确定性: 算法的每一个步骤都是明确的、可行的、结果可预期的

有穷性: 算法要有一个终止条件

输入和输出: 算法是用来解决问题的,少不了输入和输出

算法设计

这一块儿其实是很庞大的知识体系,需要苦练内功根基。下面简要介绍下算法设计方面的知识。

顺序执行、循环和分支跳转是程序设计的三大基本结构。

算法也是程序,千姿百态的算法也是由这三大基础结构构成的。

算法和数据结构关系紧密,数据结构是算法设计的基础。

如果对诸如哈希表、队列、树、图等数据结构有深刻的认识,那在算法设计上将会事半功倍。

上面提到的知识,主要的目的是抛砖引玉。算法的设计与分析是无上神功的心法口诀和入门要领。无论多么精妙绝伦的算法实现,都是由一些最基础的模型和范式组装起来的。

关于算法设计,这里给大家推荐一门课程,很不错,小伙伴可以看看:

算法设计与分析-Design and Analysis of Algorithms

降维分析,化繁为简

现在,到了最关键的时刻。我们回到题目中,开始设计我们的算法。

题干信息很简单,核心问题在于:

如何从数组中选取 N 个数进行求和运算。

如何做,这里我们通常且正确的做法,是对问题进行降维分析,并且化繁为简。

下面开始降维分析,化繁为简:

假如 N = 2 ,也就是找出数组中两个数的和为 M 的话,你会怎么做?可能你会想到每次从数组中弹出一个数,然后与余下的每个数进行相加,最后做判断。

那么问题来了,当  N = 3 呢,N = 10 呢,会发现运算量越来越大,之前的方式已经不可行了。

不妨换一种思路:

数组中选取不固定数值 N ,我们可以尝试着使用标记的方式,我们把 1 表示成选取状态, 把 0 表示成未选取状态。

假设数组 constarr=[1,2,3,4] ,对应着每个元素都有标记 0 或者 1 。如果 N=4 ,也就是在这个数组中,需要选择 4 个元素,那么对应的标记就只有一种可能 1111 ,如果 N=3 ,那就有 4 种可能,分别是 1110 、 1101 、1011以及 0111 (也就是 C4取3->4 ) 种可能。

开始抽象

通过上面的层层叙述,我们现在的问题可以抽象为:

标记中有几个 1 就是代表选取了几个数,然后再去遍历这些 1 所有可能存在的排列方式,最后做一个判断,这个判断就是:每一种排列方式,都代表着数组中不同位置的被选中的数的组合,所以这里就是将选中的这些数字,进行求和运算,然后判断求出的和是不是等于 M 。

于是,问题开始变得简单了。

如何将数组和标记关联

0101 这样的数据一眼望上去第一反应就是二进制啊

对于 arr 来说,有 4 个元素,对应的选择方式就是从 0000( N = 0 )到 1111( N = 4 )的所有可能。

而 1111 就是 15 的二进制,也就是说这所有的可能其实对应的就是 0 - 15 中所有数对应的二进制。

这里的问题最终变成了如何从数组长度 4 推导出 0 - 15

这里采用了位运算--左移运算, 1 << 4 的结果是 16 。

java实现方法如下:

import org.apache.commons.lang3.StringUtils;

import org.junit.Test;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.stream.Collectors;

/**

* @author zhengjimin

* @date 2021/2/26 下午2:46

* @function 整形数组,n、m

* 满足n个数之和等于m的,n个数的所有组合

*/

public class ArithTest {

    @Test

    public void test1(){

        int[] arg = {1,2,3,4,5,6,7,8,9};

        int n = 3;

        int m = 12;

        int size = arg.length;

        List<String> listBinary = storeBinary(size);

        List<int[]> list = new ArrayList<>();

        List<String> sizeEqualN = listBinary.stream()

                .filter(param->countN(param,n))

                .collect(Collectors.toList());

        sizeEqualN.stream()

                .forEach(binary->{

                    int[] res = equalM(arg,binary,m);

                    if (res != null){

                        list.add(res);

                    }

                });

    }

    @Test

    public void test(){

        int[] arg = {1,2,3,4,5};

        int sum = Arrays.stream(arg).sum();

        System.out.println(sum);

        List<Integer> list = Arrays.stream(arg).boxed().collect(Collectors.toList());

        long count = list.stream().count();

        System.out.println(count);

    }

    private int[] equalM(int[] arg,String binary,int m){

        int sum = 0;

        List<Integer> list = new ArrayList<>();

        for (int i = 0; i < binary.length(); i++) {

            if (binary.charAt(i) == '1'){

                sum += arg[i];

                list.add(arg[i]);

            }

        }

        if (sum == m){

            System.out.println(list);

            Integer[] toArray = list.toArray(new Integer[list.size()]);

            return list.stream().mapToInt(Integer::intValue).toArray();

        }

        return null;

    }

    private boolean countN(String param,int n){

        String rep = param.replaceAll("1","");

        if (param.length() - rep.length() == n){

            return true;

        }

        return false;

    }

    private List<String> storeBinary(int size){

        List<String> listBinary = new ArrayList<>();

        int count = 1<<size;

        for (int i = 0; i < count; i++) {

            String binary = pad0Right(i,size);

            listBinary.add(binary);

        }

        return listBinary;

    }

    private String pad0Right(int i,int size){

        String param = Integer.toBinaryString(i);

        return StringUtils.leftPad(param,size,'0');

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342