找出數組中兩數之和爲指定值的全部整數對

一,問題描述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時,不斷地重複上面的操做便可。

相關文章
相關標籤/搜索