兩數之和問題各變種多解法小結

兩數之和問題各變種多解法小結

聲明

文章均爲本人技術筆記,轉載請註明出處:
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/java

LintCode_56:兩數之和等於target

題目大意:給出未排序數組nums和指定目標target,返回數組中兩數之和$= target$的組合元素下標[index1, index2], 要求下標從1開始,並且$index1 < index2$,保證題目中有且只有1個可行解;segmentfault

解法1:暴力$O(n^2)$時間複雜度求解

解題思路:暴力二重循環求解;
複雜度分析:時間複雜度$O(n^2)$,空間複雜度$O(1)$數組

/**
 * 解法1:時間複雜度O(n^2),空間複雜度O(1)
 * 遍歷求兩數之和等於target,返回兩數下標(從1開始)
 * http://www.lintcode.com/zh-cn/problem/two-sum/
 * @author yzwall
 */
class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] results = new int[2];
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    results[0] = i + 1;
                    results[1] = j + 1;
                    return results;
                }
            }
        }
        return results;
    }
}

解法2:HashMap $O(n)$時間複雜度求解

解題思路:耗費$O(n)$空間構造哈希表,遍歷數組每一個元素nums[i],哈希表對應存儲<target - nums[i], i>,存儲nums[i]指望的「另外一半」,一旦哈希表中包含nums[i],表明「另外一半」早已存儲在哈希表中,直接返回便可;
複雜度分析:時間複雜度$O(n)$,空間複雜度$O(n)$less

/**
 * 解法2:HashMap求解,時間複雜度O(n),空間複雜度O(n)
 * 遍歷求兩數之和等於target,返回兩數下標(從1開始)
 * http://www.lintcode.com/zh-cn/problem/two-sum/
 * @author yzwall
 */
class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int[] results = new int[2];
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                results[0] = map.get(nums[i]) + 1;
                results[1] = i + 1;
                break;
            }
            map.put(target - nums[i], i);
        }
        return results;
    }
}

解法3:雙指針$O(nlog(n))$時間複雜度求解

解題思路:首先將數組排序(時間複雜度$O(nlog(n))$),而後經過雙指針ij分別從數組兩頭同時遍歷,保存數組排序前的元素位置可以使用HashMap保存(空間複雜度$O(n)$),也可用輔助類保存(空間複雜度$O(1)$);
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(n)$ or $O(1)$;優化

/**
 * 解法3:HashMap + 雙指針求解,時間複雜度O(nlogn),空間複雜度O(n)
 * 遍歷求兩數之和等於target,返回兩數下標(從1開始)
 * http://www.lintcode.com/zh-cn/problem/two-sum/
 * @author yzwall
 */
class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, ArrayList<Integer>> map = new HashMap<>();
        int[] results = new int[2];
        // HashMap用於記錄排序前數組元素對應下標
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(nums[i])) {
                map.get(nums[i]).add(i);
                continue;
            }
            map.put(nums[i], new ArrayList<Integer>());
            map.get(nums[i]).add(i);
        }
        
        int i = 0, j = nums.length - 1;
        // 排序後雙指針求解
        Arrays.sort(nums);
        while (i < j) {
            if (nums[i] + nums[j] == target) {
                int index1 = map.get(nums[i]).get(0);
                // 重複元素已經訪問過一次,從對應位置列表中剔除
                map.get(nums[i]).remove(0);
                int index2 = map.get(nums[j]).get(0);
                // 保證results[0] < result[1]
                results[0] = Math.min(index1, index2) + 1;
                results[1] = Math.max(index1, index2) + 1;
                return results;
            }
            if (nums[i] + nums[j] > target) {
                j--;
            } else {
                i++;
            }
        }
        return results;
    }
}

LintCode_587:兩數之和等於target的不重複組合數目

題目大意:給出未排序數組nums和指定目標target,返回數組中兩數之和$= target$的全部不重複組合數.net

解法:雙指針法$O(n)$時間複雜度求解

解題思路:數組排序後使用雙指針分別從起點和終點遍歷,若是存在$a + b = target$,則若是找到$a$全部組合方案,則$b$無需再找;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度經過HashSet去重,耗費額外空間$O(n)$(可優化到$O(1)$)指針

/**
 * 雙指針找兩數和等於target的不重複組合數目,時間複雜度O(n),空間複雜度O(n)
 * 求兩數之和等於target的全部不重複組合數目
 * http://www.lintcode.com/zh-cn/problem/two-sum-unique-pairs/
 * @author yzwall
 */
class Solution {
    public int twoSum6(int[] nums, int target) {
        int pairs = 0;
        if (nums == null || nums.length < 2) {
            return pairs;
        }
        Arrays.sort(nums);
        int i = 0;
        int j = nums.length - 1;
        HashSet<Integer> set = new HashSet<>();
        while (i < j) {
            // 若是a + b = target, a找到後,b無需再找
            while (i < j && set.contains(nums[i])) {
                i++;
            }
            while (i < j && set.contains(nums[j])) {
                j--;
            }
            if (i < j) {
                if (nums[i] + nums[j] == target) {
                    set.add(nums[i]);
                    set.add(nums[j]);
                    pairs++;
                } else if (nums[i] + nums[j] > target) {
                    j--;
                } else {
                    i++;
                }
            }
        }
        return pairs;
    }
}

LintCode_608:兩數之和等於target(數組已排序)

題目大意:題目是LintCode_56的簡化版,解法1和解法2可直接使用;與解法1,2相比,解法3雙指針法充分利用數組已排序條件,時間複雜度下降到$O(n)$,空間複雜度下降到$O(1)$;code

LintCode_443:兩數之和大於target

題目大意:求出數組nums中兩數之和$> target$的組合數目;blog

解法1:暴力$O(n^2)$時間複雜度求解

二重循環暴力求解;排序

解法2:雙指針法$O(nlog(n))$時間複雜度求解

解題思路:首先將數組nums升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] > target, $$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] > target$$,所以執行pairs += (j - i),此時$nums[j]$全部方案搜索完畢,執行j--

複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$

/**
 * 解法2:雙指針法求解,時間複雜度O(logn),空間複雜度O(1) 
 * 求兩數之和大於target的組合數目
 * http://www.lintcode.com/en/problem/two-sum-greater-than-target/
 * @author yzwall
 */
class Solution {
     public int twoSum2(int[] nums, int target) {
            int pairs = 0;
            if (nums == null || nums.length < 2) {
                return pairs;
            }
            Arrays.sort(nums);
            int i = 0;
            int j = nums.length - 1;
            while (i < j) {
                if (nums[i] + nums[j] > target) {
                    pairs += j - i;
                    // nums[j]全部方案求解完畢,j--
                    j--;
                } else {
                    i++;
                }
            }
            return pairs;
     }
}

LintCode_609:兩數之和不超過target

題目大意:求出數組nums中兩數之和$leqslant target$的組合數目;

LintCode_610:兩數之差等於target

解法1:暴力$O(n^2)$時間複雜度求解

二重循環暴力求解;

解法2:雙指針法$O(nlog(n))$時間複雜度求解

解題思路:首先將數組nums升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] \leq target, $$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] \leq target$$,所以執行pairs += (j - i),此時$nums[i]$全部方案搜索完畢,執行i++
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$

/**
 * 雙指針法求解,時間複雜度O(nlogn),空間複雜度O(1)
 * 求兩數之和小於等於target的全部組合數目
 * http://www.lintcode.com/en/problem/two-sum-less-than-or-equal-to-target/
 * @author yzwall
 */
class Solution {
     public int twoSum5(int[] nums, int target) {
        int pairs = 0;
        if (nums == null || nums.length < 2) {
            return pairs;
        }
        Arrays.sort(nums);
        int i = 0;
        int j = nums.length - 1;
        while (i < j) {
            // nums[i]的全部組合 = j - i
            if (nums[i] + nums[j] <= target) {
                pairs += j - i;
                i++;
            } else {
                j--;
            }
        }
        return pairs;
     }
}

LintCode_533:兩數之和最接近target

題目大意:求出數組nums中兩數之和與target的最近距離(非負);

解法1:暴力$O(n^2)$時間複雜度求解

解題思路:二重循環不斷迭代最小距離;
複雜度分析:時間複雜度$O(n^2)$,空間複雜度$O(1)$;

/**
 * 解法1:暴力時間複雜度O(n^2)
 * 求兩數之和最接近target,求最近距離
 * http://www.lintcode.com/en/problem/two-sum-closest-to-target/
 * @author yzwall
 */
class Solution {
    public int twoSumClosest(int[] nums, int target) {
        if (nums == null || nums.length < 2) {
            return target;
        }
        
        int min = Integer.MAX_VALUE;
        int temp;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                temp = target - (nums[i] + nums[j]);
                min = Math.min(min, Math.abs(temp));
            }
        }
        return min;
    }
}

解法2:雙指針法$O(nlog(n))$時間複雜度求解

解題思路:首先將數組排序,雙指針分別從起點和終點遍歷,臨時距離
$$
temp = left | target - (nums[i] + nums[j]) right |
$$
不停與最短距離$min$比較迭代,$temp = 0$時直接返回;
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$;

/**
 * 解法2:雙指針法求解,時間複雜度O(nlogn)
 * 求兩數之和最接近target,求最近距離
 * http://www.lintcode.com/en/problem/two-sum-closest-to-target/
 * @author yzwall
 */
class Solution {
    public int twoSumClosest(int[] nums, int target) {
        if (nums == null || nums.length < 2) {
            return target;
        }
        
        Arrays.sort(nums);
        int i = 0;
        int j = nums.length - 1;
        int min = Integer.MAX_VALUE;
        int temp;
        while (i < j) {
            temp = Math.abs(target - (nums[i] + nums[j]));
            if (temp == 0) {
                return 0;
            }
            min = Math.min(min, temp);
            if (nums[i] + nums[j] > target) {
                // 距離過大, j--
                j--;
            } else {
                // 距離太小, i++
                i++;
            }
        }
        return min;
   }
}

LintCode_610:兩數之差等於target

解法1:暴力$O(n^2)$時間複雜度求解

二重循環暴力求解;

解法2:雙指針法$O(nlog n)$時間複雜度求解

解題思路:首先將數組nums升序排序,雙指針$i$從起點開始,指針$j$從終點開始,一旦有:$$nums[i] + nums[j] \leq target$$則因爲數組嚴格不遞減,$$\forall num\in[nums[i], nums[j]], num + nums[j] \leq target$$,所以執行pairs += (j - i),此時$nums[i]$全部方案搜索完畢,執行i++
複雜度分析:時間複雜度$O(nlog(n))$,空間複雜度$O(1)$

相關文章
相關標籤/搜索