首页 > 技术文章 > 算法---------两数之和

caoxinyu 2019-03-15 20:22 原文

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

我的解答:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int length = nums.length;
        //i j  首位两个数字
        for (int i = 0 ,j = length -1;i < j ;i++,j--){
            if (nums[i] + nums[j] == target) {
                return new int[]{i,j};
            }
                // k 是i 后面的一位
            for (int k = i +1; k < j; k++){
                //  如果满足条件  返回   否则k 移动
                if (nums[i] + nums[k] == target){
                    return new int[]{i,k};
                }else if (nums[j] + nums[k] == target){
                    return new int[]{k,j};
                }
            }
            // 全都不满足条件  ij 移动

        }

        return null;
    }
}

网上最快的解答:

class Solution {
   int size = 2048;
	int[] map = new int[size];
	int length = 2047;
	int index;
    
    public int[] twoSum(int[] nums, int target) {
    	for (int i = 0; i < nums.length; i++) {
			index = nums[i]&length; //

			if (map[index] != 0) {
				return new int[] { map[index] - 1, i };
			} else {
				map[(target - index)&length ] = i + 1;
			}
		}
		throw new IllegalArgumentException("No two sum solution");
    }
}

这个算法我看了大概三天(因为我还要工作,没有大块的时间去研究),花了图,转换成二进制,才终于明白作者的大概意思。
当然,同时也看出了这个算法的漏洞。
比如:

twoSum(new int[]{2047,0, 800001, 800000, 7,2,2048}, 4095);

返回的结果是[0,1],第0个元素和第一个元素,相加显然不等于4095.

作者大概的执行思路是:

1100 0011 0101 0000 0000  800000  num[i]	
		    111 1111 1111   2047  length
		    101 0000 0000   1280   index
		   
		   
		   
1 1000 0110 1010 0000 0000    target 1600000	
			 101 0000 0000  index
1 1000 0110 0101 0000 0000   target -index
			 111 1111 1111   2047  length
			 
			 
			 101 0000 0000			 

补充:2047 二进制是11111111111 (11个1)

首先取出一个数,然后让这个数num[i]&2047 = a

之后取判断map[a]是不是0(存不存在),如果不是0(存在的话)就直接返回这个数的索引,和存在的map 里面的索引。

如果不存在,就让target - num[i] ,然后&2047(11个1)。

漏洞分析:

在作者的思想里,11位就可以确定一个数字,同时也认为,target 减去这个数之后的11 也可以确定是一个数。其实是错误的。23位到11这区间的数据,作者没有关心。这算法从这里看就是有漏洞的。我们就从23位到11位之间做文章,就可以得到这个算法的漏洞。

比如在做着的算法里,0 和2048(100000000000) 是一个数字 因为,最后11位都是0. 根据这个漏洞去反推作者的算法。
目标数 1111 1111 1111 (4095)
我找的数组的第一个数是 111 1111 1111 (2047)
1111 1111 1111 (4095) - 111 1111 1111 (2047) = 100000000000
然后和11111111111 做& 运算之后的数字是0
这时候如果第二个数字是0,就直接返回了0 和 2048,根本不是target 4095

比如

twoSum(new int[]{2047,0, 800001, 800000, 7,2,2048}, 4095);

这个结果应该是0,6, 但是按照网上最快的,返回是0,1。
是因为:
首先,第一个数2047是
111111111111(11个1) & 2047 等于 2047(11个1)

然后targe(4095 ) - 2047 相当于 111111111111(12个) - 11111111111(11个1) = 100000000000 (应该是2048)

然后把这个数100000000000 (2048) & 2047 = 0

然后把map[0] = 1

到第二个数的时候,0 & 2047 = 0 ,发现这个数存在,就取出来返回。结果两个数之后是2047 ,根本不是我想要的4095

推荐阅读