一,問題描述java
給定一個整型數組(數組中的元素可重複),以及一個指定的值。打印出數組中兩數之和爲指定值的 全部整數對git
思路1:能夠用hash表來存儲數組中的元素,這樣咱們取得一個數後,去判斷sum - val 在不在數組中,若是在數組中,則找到了一對二元組,它們的和爲sum,該算法的缺點就是須要用到一個hash表,增長了空間複雜度。github
思路2:一樣是基於查找,咱們能夠先將數組排序,而後依次取一個數後,在數組中用二分查找,查找sum -val是否存在,若是存在,則找到了一對二元組,它們的和爲sum,該方法與上面的方法相比,雖然不用實現一個hash表,也沒不須要過多的空間,可是時間多了不少。排序須要O(nLogn),二分查找須要(Logn),查找n次,因此時間複雜度爲O(nLogn)。算法
思路3:該方法基於第2種思路,可是進行了優化,在時間複雜度和空間複雜度是一種折中,可是算法的簡單直觀、易於理解。首先將數組排序,而後用兩個指向數組的指針,一個從前日後掃描,一個從後往前掃描,記爲first和last,若是 fist + last < sum 則將fist向前移動,若是fist + last > sum,則last向後移動。數組
二,算法分析數據結構
一共有兩種方法來求解。方法一藉助排序,方法二採用HashSet函數
方法一:學習
先將整型數組排序,排序以後定義兩個指針left和right。left指向已排序數組中的第一個元素,right指向已排序數組中的最後一個元素優化
將 arr[left]+arr[right]與 給定的元素比較,若前者大,right--;若前者小,left++;若相等,則找到了一對整數之和爲指定值的元素。ui
此方法採用了排序,排序的時間複雜度爲O(NlogN),排序以後掃描整個數組求和比較的時間複雜度爲O(N)。故總的時間複雜度爲O(NlogN)。空間複雜度爲O(1)
方法二:
依次遍歷整型數組,對整型數組中的每個元素,求解它的suplement(expectedSum-arr[i]).suplement就是指定的值減去該數組元素。
若是該元素的 suplement不在HashSet中,則將該元素添加到HashSet。
若是該元素的suplement在HashSet中,說明已經找到了一對整數之和爲指定值的元素。
該方法使用了HashSet,故空間複雜度爲O(N),因爲只須要掃描一遍整型數組,故時間複雜度爲O(N)
三,完整代碼實現:
import java.util.Arrays; import java.util.HashSet; public class ExpectSumOfTwoNumber { public static void expectSum_bySort(int[] arr, int expectSum) { if(arr == null || arr.length == 0) return; Arrays.sort(arr); int left = 0, right = arr.length - 1; while(left < right) { if(arr[left] + arr[right] > expectSum) right--; else if(arr[left] + arr[right] < expectSum) left++; else//equal { System.out.println(arr[left] + " + " + arr[right] + " = " + expectSum); left++; right--; } } } public static void expectSum_bySet(int[] arr, int expectSum) { if(arr == null || arr.length == 0) return; HashSet<Integer> intSets = new HashSet<Integer>(arr.length); int suplement; for (int i : arr) { suplement = expectSum - i; if(!intSets.contains(suplement)){ intSets.add(i); }else{ System.out.println(i + " + " + suplement + " = " + expectSum); } } } //hapjin test public static void main(String[] args) { int[] arr = {2,7,4,9,3}; int expectSum = 11; expectSum_bySet(arr, expectSum); System.out.println("************"); expectSum_bySort(arr, expectSum); System.out.println("----------------"); int[] arr2 = {3,7,9,1,2,8,5,6,10,5}; int expectSum2 = 10; expectSum_bySet(arr2, expectSum2); System.out.println("**********"); expectSum_bySort(arr2, expectSum2); } }
問題描述:給定一個整型的數組,找出其中的兩個數使其和未指定的值,返回兩個數的數組下標(假定是無序數組,數組元素各不相同,要求時間複雜度爲O(n),n爲數組長度,可使用輔助空間)
分析:時間複雜度是O(n),便是掃描一遍數組,不可嵌套掃描。必須所有數組掃描和查找所有,掃描+查找=O(n),可使用數據結構哈希表,哈希表的查找的時間複雜度是O(1)。
(1). 由於輸出是數組下標,那麼就讓數組下標爲哈希表中的值,數組的值爲哈希表中的鍵,掃描一遍數組,put進HashMap,代碼以下(時間複雜度O(n),n爲數組大小):
HashMap<Integer, Integer> map=new HashMap<Integer, Integer>(); for(int i=0;i<nums.length;i++) { map.put(nums[i], i); }
(2).第二遍掃描就是要查找出值,這裏要作兩個判斷,一個就是兩個結果數的值不能相同,這是題目要求的,還有一個就是判斷HashMap是否有值,代碼以下(最壞狀況的時間複雜度是O(n),n爲數組大小):
for(int i=0;i<nums.length;i++) { // 獲得第二個數的值 int two=target-nums[i]; // 若是存在第二個數的數組下標&&結果的兩個數不是同一個數的值 if(map.containsValue(two)&&target!=2*two) { result[0]=i; result[1]=map.get(two); // 返回找到的兩個數的數組下標 return result; } }
/** * 使用輔助空間(使用哈希表,時間複雜度是O(n),空間複雜度:O(n),n是數組大小) * @param nums * @param target * @return 沒有找到的話數組中數值就是{-1,-1},不然找到,其實我想返回null的,可是以爲返回null不禮貌,由於null有毒 */ public static int[] findTwo3(int[] nums, int target) { // 結果數組 int[] result={-1,-1}; // 目標是數組下標,因此鍵值對爲<數值,數值對應數組下標>,這裏要說一下,哈希表的查找的時間複雜度是O(1) HashMap<Integer, Integer> map=new HashMap<Integer, Integer>(); // 1.掃描一遍數組,加入哈希表,時間複雜度是O(n) for(int i=0;i<nums.length;i++) { map.put(nums[i], i); } // 2.第二次掃描,目標值-當前值,差值做爲key,看看map裏有木有,沒有就下一個循環,直到數組掃描完畢或找到value,因此最壞狀況的時間複雜度是O(n) for(int i=0;i<nums.length;i++) { // 獲得第二個數的值 int two=target-nums[i]; // 若是存在第二個數的數組下標&&結果的兩個數不是同一個數的值 if(map.containsValue(two)&&target!=2*two) { result[0]=i; result[1]=map.get(two); // 返回找到的兩個數的數組下標 return result; } } // 沒有找到 return result; }
思考:假如數組中有重複的話,那麼上面的算法將失效,由於數據結構HashMap<Interget,Interget>已經不合適,這樣我以爲可使用HashMap<Interget,List<Interget>>來解決。
問題描述:給定一個整型數組,是否能找出其中的兩個數使其和爲某個指定的值?(假定是無序數組)
/** * 暴力破解 * (窮舉,時間複雜度:O(n^2),正常是不會用這個滴,假如只是爲了快速解題,對時間沒有限制,用這個最簡單) * * @param nums * @param target */ public static void findTwo1(int[] nums, int target) { int one, two; for (int i = 0; i < nums.length; i++) { one = nums[i]; two = target - one; for (int j = 0; j < nums.length; j++) { if (i != j) { if (two == nums[j]) { System.out.println("one:" + one + " two:" + two); return; } } } } System.out.println("找不到這兩個數"); }
/** * 兩個指針二分查找 * (排序時間複雜度爲O(nlog(n)),while最多O(N),因此最終程序的時間複雜度爲:O(nlo(n))) * * @param nums * @param target */ public static void findTwo2(int[] nums, int target) { // 1.排列(用的是Dual-Pivot Quicksort(快速排序),時間複雜度爲O(nlog(n))) Arrays.sort(nums); // 2.類二分查找 int left = 0; int right = nums.length - 1; while (left < right) { if (nums[left] + nums[right] > target) {// 太大 right減小 right--; } else if (nums[left] + nums[right] < target) {// 過小left增長 left++; } else {// 找到結果,結束查找 System.out.println("one:" + nums[left] + " two:" + nums[right]); return; } } System.out.println("找不到這兩個數"); }
問題描述:
設計一個類,包含以下兩個成員函數:
Save(int input) 插入一個整數到一個整數集合裏。
Test(int target) 檢查是否存在兩個數和爲輸入值。若是存在着兩個數,則返回true,不然返回false
容許整數集合中存在相同值的元素分析:
與[算法學習]給定一個整型數組,找出兩個整數爲指定整數的和(2)不一樣,這裏須要算出的是存不存在這兩個數,能夠在上一篇的基礎上修改一下數據結構,HashMap
(1). 寫Save(int input)。這個就簡單了,只需判斷是否存在input爲key,有就value+1,沒有就value=1。代碼以下:
public void Save(int input) { int count = 0; if (map.containsKey(input)) { count = map.get(input); } map.put(input, count + 1); }
(2). 檢查是否存在兩個數和爲輸入值。上面的分析已經講得差很少,這裏就直接貼代碼。代碼以下:
public boolean Test(int target) { Iterator<Integer> iterator = map.keySet().iterator(); while (iterator.hasNext()) { int one = iterator.next(); int two = target - one; System.out.println("one:"+one+" two:"+two); if (map.containsKey(two)) { // two<<1等價於two*2 if (!(target ==two<<1 && map.get(two) == 1)) { return true; } } } return false; }
import java.util.HashMap; import java.util.Iterator; public class TwoNumOfSum3 { // key:數值,value:數值對應的個數 HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); /** * 插入一個整數到一個整數集合裏 * @param input */ public void Save(int input) { int count = 0; if (map.containsKey(input)) { count = map.get(input); } map.put(input, count + 1); } /** * 檢查是否存在兩個數和爲輸入值 * @param target * @return 若是存在着兩個數,則返回true,不然返回false */ public boolean Test(int target) { Iterator<Integer> iterator = map.keySet().iterator(); while (iterator.hasNext()) { int one = iterator.next(); int two = target - one; System.out.println("one:"+one+" two:"+two); if (map.containsKey(two)) { if (!(target ==two<<1 && map.get(two) == 1)) { return true; } } } return false; } /** * @param args */ public static void main(String[] args) { TwoNumOfSum3 t=new TwoNumOfSum3(); t.Save(5); t.Save(10); t.Save(4); t.Save(7); System.out.println(t.Test(12)); } }
時間複雜度O(n)的解法
咱們能夠用一個哈希表或數組或bitmap(後二者要求數組中的整數非負)來保存sum-x的值, 這樣咱們就只須要遍歷數組兩次便可找到和爲指定值的整數對。這種方法須要O(n) 的輔助空間。若是直接用數組或是bitmap來作,輔助空間的大小與數組中的最大整數相關, 經常致使大量空間浪費。好比原數組中有5個數:1億,2億,3億,4億,5億。sum爲5億, 那麼咱們將bitmap中的sum-x位置1,即第4億位,第3億位,第2億位,第1億位,第0位置1. 而其它位置都浪費了。
若是使用哈希表,雖然不會有大量空間浪費,但要考慮衝突問題。
時間複雜度爲O(nlogn)的解法
咱們來考慮一種空間複雜度爲O(1),並且實現也很簡單的算法。首先,將數組排序。 好比排序後獲得的數組a是:-2 -1 0 3 5 6 7 9 13 14。而後使用low和high 兩個下標指向數組的首尾元素。若是a[low]+a[high] > sum,那麼說明a[high] 和數組中的任何其它一個數的和都必定大於sum(由於它和最小的a[low]相加都大於sum)。 所以,a[high]不會與數組中任何一個數相加獲得sum,因而咱們能夠直接不要它, 即讓high向前移動一位。一樣的,若是a[low]+a[high] < sum,那麼說明a[low] 和數組中的任何其它一個數的和都必定小於sum(由於它和最大的a[high]相加都小於sum)。 所以,咱們也能夠直接不要它,讓low向前移動一位。若是a[low]+a[high]等於sum, 則輸出。當low小於high時,不斷地重複上面的操做便可。