首页 > 编程语言 >面试算法:在整形数组中构建元素之和能整除数组长度的子集

面试算法:在整形数组中构建元素之和能整除数组长度的子集

时间:2023-06-14 11:06:16浏览次数:40  
标签:subSet int sum 元素 子集 数组 集合 整除 mod


更详细的讲解和代码调试演示过程,请参看视频
如何进入google,算法面试技能全面提升指南

假设A是一个整数数组,长度为n,数组中的元素可能是重复的。设计一个算法,找到一系列下标的集合I = {i(0), i(1), i(2)….i(n)}. 使得(A[i(0)] + A[i(1)] + … A[i(n)] ) mod n = 0.例如假定A = {711, 704, 427, 995, 334, 62, 763, 98, 733, 721}, 于是I = {0,1,3}, 因为(A[0] + A[1] + A[3] ) mod 10 = 0.
请给出一个有效的算法找到满足条件的集合I, 无论A的元素如何取值,这样的集合总是存在的。

这是一道较为复杂的算法题,能在一个小时内完成绝非易事。我们需要解决两个问题,第一需要证明,为何这样的集合肯定存在,第二,集合存在,如何把它给挖掘出来。我们先证明第一个问题。

1:如果数组A中含有元素它的值是0,那么满足条件的集合存在,这个集合就只包含元素0.

2:如果数组A只有一个元素,也就是A的长度只有1,那么A本身就是满足条件的集合。

3:如果A包含不只一个元素,我们用归纳法来证明。假设n=k时,我们能从A中找到给定集合,使得(A[i(0)] + A[i(1)] + … A[i(n)] ) mod n = 0,那么当n=k+1时,我们从数组A中任意取出一个元素,如果被拿走的这个元素值是1,那么根据归纳法,我们可以从剩下的k个元素中,找到一个子集,子集中的元素之和能够整除k, 把子集中的元素加上拿走的那个值为1的元素形成新的集合,这个集合之和能够整除k+1.

如果拿走的元素值为t > 1, 剩下的元素个数为k, 那么肯定有 k > k+ 1 - t。我们从剩下的k 个元素中,随便选出 k+1-t 个元素,在这些元素中,根据归纳法,肯定存在一个子集,使得子集元素的和能整除 k + 1 - t. 把这些子集的元素加上前面拿走的元素,那么所形成的新集合,一定能整除 k + 1

综上,我们就证明了,这样的集合是一定存在的,接下来我们需要考虑的是,如何找到这个集合。

我们看看,把元素逐个加总后对n求余会有什么性质:
sum = (A[0] + A[1] + …. + A[j]) mod n
j 由0一直增加到n-1.

在这个过程中,一定会出现下面这种情况:
存在一个 i, 0 <= i < j, 使得 (A[0]+A[1]+…+A[i]) mod n = (A[0]+A[1] + …A[j]) mod n. (*)

当上面情况出现时,我们有(A[i+1] + A[i+2] +… + A[j]) mod n = 0;

于是(A[i+1], … ,A[j]) 就是满足条件的子集。为何(*)所描述的情况一定会发生呢?我们知道,对某个数求余,所得结果必然小于该数,因此对n求余,那么结果一定落入到1和n-1之间。

sum = (A[0] + A[1] + …. + A[j]) mod n

j 由0一直增加到n-1, 也就是上面的计算最多会进行n 次,得到n个求余的结果,然而求余的结果最多只可能有n-1中不同情况,那么n个结果中,肯定得有出现重复的时候,一旦出现重复的时候,(*)所描述的情况就出现了。

根据上面算法原理,我们可以实现如下代码:

public ArrayList<Integer> moduleSubSet(int[] A) {
        int[] B = new int[A.length];
        for (int i = 0; i < B.length; i++) {
            B[i] = 0;
        }

        int sum = 0;
        ArrayList<Integer> subSet = new ArrayList<Integer>();
        for (int i = 0; i < A.length; i++) {
            sum += A[i];
            subSet.add(i);
            int t = sum % A.length;
            if (t == 0) {
                return subSet;
            }

            int pre_sum = 0;
            if (t != 0) {
                if (B[t] != 0) {
                    for (int j = 0; j < i; j++) {
                        pre_sum += A[j];
                        if (pre_sum % A.length != t) {
                            subSet.remove((Integer)j);
                        } else {
                            subSet.remove((Integer)j);
                            return subSet;
                        }
                    }
                }

               B[t] = 1;
            }
        }

        return null;
    }

在for循环中,我们把数组A的元素逐个相加,然后对长度n求余,所得结果记为t,然后拿这个结果到另一个校验数组B中检验一下,如果B[t] 的值是1,这表明(*)所描述的情况出现了,此时我们再遍历以便(A[0]….A[j]) 把i找出来,找到i的办法就是再次将元素逐个相加,当所得结果对n求余后,与前面算得的t相等,那么此时的元素下标i满足条件,找到i之后,再把下标0到i从集合中去除,那么集合中剩下的下标就是对应自己中的元素在原数组中的下标。

我们再看看主入口函数的实现:

public static void main(String[] args) {
        ArrayAndString  as = new ArrayAndString();
        int[] A = new int[]{1,1,1,3};
        //int[] A = new int[]{711, 704, 427, 995, 334, 62, 763, 98, 733, 721};
        ArrayList<Integer> subSet = as.moduleSubSet(A);
        System.out.println("sub set is: ");
        for (int i = 0; i < subSet.size(); i++) {
            System.out.print(subSet.get(i) + " ");
        }
    }

上面代码运行后结果为:
sub set is:
1 2 3 4
也就是说(A[1] + A[2] + A[3] + A[4]) mod 10 = 0, 大家可以自己检测一下,所得结果确实是正确的。

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:

面试算法:在整形数组中构建元素之和能整除数组长度的子集_求余


标签:subSet,int,sum,元素,子集,数组,集合,整除,mod
From: https://blog.51cto.com/u_16160261/6476212

相关文章

  • 面试算法:波浪型数组的快速排序法
    波浪型数组是具备这样特性的数组,它的元素先是递增,到达某个点后开始低贱,接着达到某个点时又递增,接着右递减,以下数组就是一个波浪型数组:A:57,131,493,294,221,339,418,452,442,190A[0]A[1]A[2]A[3]A[4]A[5]A[6],A[7],A[8],A[9]不难发现,A[0]-A[4]是一个波浪,因为......
  • 数组形式组织的树
    引入在LeetCode中,二叉树一般是以链表结点的形式组织的,定义如下:structTreeNode{intval;TreeNode*left;TreeNode*right;TreeNode(intx):val(x),left(nullptr),right(nullptr){}};其实也可以用数组的形式组织,即使用$parent$数组,$y=parent[x......
  • 1814.统计一个数组中好对子的数目
    问题描述1814.统计一个数组中好对子的数目解题思路首先,变换一下题目的需求,nums[i]-rev(nums[i])==nums[j]-rev(nums[j]),然后利用哈希表记录每个值出现了多少次就可以了。代码classSolution{public:intrev(intnum){vector<int>tmp;inta......
  • 1877.数组中最大数对和的最小值
    问题描述1877.数组中最大数对和的最小值解题思路贪心将数组从小到大排序,最小最大配对,次小次大配对,依次配对,结果就是这些配对和的最大值。代码classSolution{public:intminPairSum(vector<int>&nums){sort(nums.begin(),nums.end());intres=......
  • 2357.使数组中所有元素都等于零
    问题描述2357.使数组中所有元素都等于零(Easy)给你一个非负整数数组nums。在一步操作中,你必须:选出一个正整数x,x需要小于或等于nums中最小的非零元素。nums中的每个正整数都减去x。返回使nums中所有元素都等于0需要的最少操作数。示例1:输入:nums=......
  • 【剑指Offer】1、二维数组中的查找
    【剑指Offer】1、二维数组中的查找题目描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。解题思路:很明显,由于该二维数组上到下......
  • Redis入门 – Jedis存储Java对象 - (Java序列化为byte数组方式)
     Redis入门–Jedis存储Java对象-(Java序列化为byte数组方式) 在Jedis开发中,我们很多时候希望直接把一个对象放到Redis中,然后在需要的时候取出来。Redis的key和value都支持二进制安全的字符串,存储Java对象不是问题,下面我们看一下如何来实现。 1要存储的对象现在写一个很土的J......
  • golang对于[]byte数组转string进行比较的优化
    当需要比较两个[]byte数组是否相等时有好几种方案,下面可以看出前三种方案都是优化过的,效率高的方案。packagemainimport( "bytes" "crypto/rand" mr"math/rand" "testing")funcStringEqual(nint,ffunc(a,b[]byte)bool){ buf:=make([]byte,1024) rand.......
  • 数组
    <script>constarr=['a','b','c','d','e','f','g','h']//后面添加push删pop前面添加unshift删shift//slice截取console.log(arr.slice(1,3))//返回一个新数组,从1......
  • JS-数组和函数
    1.数组数组Array:是一种可以按顺序保存数据的数据类型1.1声明数组let数组名=[数据1,数据2,数据3,...,数据n]或let数组名=newArray(数据1,数据2,数据3,...,数据n)<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahtt......