3、题目:能否快速找出一个数组(简单起见,数组中元素值各不一样)中的两个数字,让这两个数字之和等于一个给定的值。
例如,给定数组arr(如下图),给定值key为12,则arr[0](5)、arr[4](7)满足要求。
解法一、穷举法
穷举说白了就是不断试,题目为从数组中找两个满足条件的数字,即把数组看成两份,遍历第一份中的所有去第二份里面找是否存在满足条件的数字。
思路:穷举数组中任意的两两组合,并计算取出的两个数之和是否等于给定值即可,通过穷举计算时间复杂度为O(N * N)。对于求和计算具有的对称性,当遍历数组取出数据时,只需向后取值即可(如下图)。
算法:
1、遍历数组,从第一个至最后一个,依次取出数组中的元素A;
2、遍历该元素所在位置后的元素B,计算A + B是否等于给定值,直至相等;
代码:
public int[] getPairsFromArray(int[] arr, int key) { int[] partner = new int[2]; boolean isPartnerIn = false; for (int i = 0; i < arr.length && !isPartnerIn; i++) { for(int j = i + 1; j < arr.length && !isPartnerIn; j++) { if(arr[i] + arr[j] == key) { partner[0] = arr[i]; partner[1] = arr[j]; isPartnerIn = true; } } } return isPartnerIn ? partner : new int[0]; }
穷举法——改进版(感谢 @ck_winner 提供的思路)
思路:以给定值的二分之一(part)为分割数,将数组分割成两部分,其中,左部分小于part值,右边部分大于part值。由于本题中数组中的元素值不同,则如存在两个数之和为给定数,则这两个数必分属左边和右边部分,再通过上面的穷举方式进行查找即可。具体过程可见下图:
算法:
1、通过给定数值的二分之一作为分割数,将数组分割成左右两部分;
2、遍历左部分数值A,右部分数值B,计算A + B是否等于给定值,直至相等;
private int partition(int[] arr, int key) { int tmp = 0; int i = 0, j = arr.length - 1; while(true) { while(i < arr.length && arr[i] <= key) i++; while(j >= 0 && arr[j] > key) j--; if(i >= j) break; //swap arr[i] and arr[j] tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } return j; } public int[] getPairsFromArray4(int[] arr, int key) { int[] partner = new int[2]; boolean isPartnerIn = false; int part = partition(arr, key / 2); for(int i = 0; i <= part && !isPartnerIn; i++) { for(int j = part + 1; j < arr.length && !isPartnerIn; j++) { if(arr[i] + arr[j] == key) { partner[0] = arr[i]; partner[1] = arr[j]; isPartnerIn = true; } } } return isPartnerIn ? partner : new int[0]; }
解法二、无序变有序
将无序数组有序化是处理无序数组常用方法,有序数组有较多较好的处理方法,如双端遍历,二分查找等,通过这些方法可以使处理过程变得简化。
思路:先将无序数组有序化(通过快排方式,计算时间复杂度为O(N * log N)),排序后可采用双向数组遍历方式(如下图)。若arr[i] + arr[j]等于给定值Key,直接返回;若arr[i] + arr[j]小于给定值key,则i向后位移一位;若arr[i] + arr[j]大于给定值key,则j向前位移一位。计算时间复杂度为O(N * log N + N);
计算:
1、通过快排算法将无序数组有序化;
2、采用双端遍历方式,设置i和j分别为数组两端index;若arr[i] + arr[j] = Key,返回结果;若arr[i] + arr[j] < key,i++;若arr[i] + arr[j] > key,j--;
代码:
public int[] getPairsFromArray2(int[] arr, int key) { Arrays.sort(arr); int[] partner = new int[2]; boolean isPartnerIn = false; for (int i = 0, j = arr.length - 1; i < j && !isPartnerIn; ) { if ((arr[i] + arr[j]) == key) { partner[0] = arr[i]; partner[1] = arr[j]; isPartnerIn = true; } else if((arr[i] + arr[j]) < key) { i++; } else { j--; } } return isPartnerIn ? partner : new int[0]; }
解法三、Hash表
之前讨论过bitmap,关系映射表等,形式虽然多样,实则都为空间换时间的方法,通过构造空间位置(key)与值(value)的映射关系,使得通过空间位置(key)取值(value)时间复杂度为O(1)。
思路:将数组中的元素放入bitmap中,然后再遍历数组中的所有元素,判定bitmap中给定元素值-数组元素值位置是否存在。由于bitmap取值时间复杂度为O(1),整个计算时间复杂度为O(N),构造bitmap需要空间为O(M),其中M为数组最大数;
计算:
1、构造bitmap表,将数组中所有元素放入bitmap表中;
2、再次遍历数组中所有元素,并判断bitmap表中key-arr[i]是否存在;
代码:
public int[] getPairsFromArray(int[] arr, int key) { BitSet bs = new BitSet(); for(int i = 0 ; i < arr.length; i++) { bs.set(arr[i]); } int[] partner = new int[2]; boolean isPartnerIn = false; for (int i = 0; i < arr.length && !isPartnerIn; i++) { if (bs.get(key - arr[i]) && (key - arr[i]) != arr[i]) { partner[0] = arr[i]; partner[1] = key - arr[i]; isPartnerIn = true; } } return isPartnerIn ? partner : new int[0]; }