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]; }