LeetCode1—Two Sum

本类型博客中的各算法的时间复杂度分析均为博主自己推算,本类型博客也是博主自己刷LeetCode的自己的一些总结,因此个中错误可能较多,非常欢迎各位大神在博客下方评论,请不吝赐教


一、问题

  • 输入:整数数组nums和目标值target
  • 输出:在整数数组中找出两个值,使得其和为target,返回这两个数在数组中的索引
  • 假设条件:①数组中的每个数最多使用一次;②假设每个测试用例都只有唯一的一组索引

二、输入输出示例

  • 输入:nums = [2, 7, 11, 15], target = 9
  • 输出:[0, 1] (nums[0]+nums[1]=9)

三、解法


本题的基本思路就是先选取一个数,计算target跟它的差值,然后在剩下的数中查找有没有跟差值相等的数,有的话则返回两个的索引,没有的话则换一个数继续这个步骤。因此该问题解法的时间复杂度为 遍历次数*搜索次数,遍历次数固定为O(n),因此算法时间复杂度差异都在搜索算法上。以下几种解法都体现了不同的搜索方式。

解法一:暴力搜索

解题思路:针对每个数字,搜索数组中其他数字是否和它的和为目标值,如果是的话则返回结果,否则继续遍历下一个数字。每次处理完一个数字后,就可以确定它肯定不会出现在答案中,因此在处理下个数字时,就不再需要遍历它,以此减少遍历次数。
package com.happy.leetcode.p1;

import java.util.Arrays;

/**
 * 29.69%
 * @author bird
 *
 */
public class TwoSumV1 {

    public static void main(String[] args) {
        int[] nums = {2, 7, 11, 15};
        int target = 9;
        int[] result = new TwoSumV1().twoSum(nums, target);
        System.out.println(Arrays.toString(result));
    }
    
    public int[] twoSum(int[] nums, int target) {
        for(int i=0; i<nums.length-1; i++) {
            // 搜索剩下的数字
            for(int j=i+1; j<nums.length; j++) {
                if((nums[i]+nums[j])==target) {
                    return new int[] {i, j};
                }
            }
        }
        return null;
    }
    
}

时间复杂度分析:两层循环,所以时间复杂度为 O(n^2)

解法二:快排+二分法搜索

解题思路:该解法的思路与解法一类似,但是在搜索另一个值时采用二分法搜索,由于二分法搜索前需要进行排序,因此先使用快排处理数组

package com.happy.leetcode.p1;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 48.34%
 * @author bird
 *
 */
public class TwoSumV2 {

    public static void main(String[] args) {
        int[] nums = {2, 5, 5, 11};
        int target = 10;
        int[] result = new TwoSumV2().twoSum(nums, target);
        System.out.println(Arrays.toString(result));
    }
    
    public int[] twoSum(int[] nums, int target) {
        // 存储原数组的索引
        Integer[] indexes = new Integer[nums.length];
        for(int i=0; i<indexes.length; i++) {
            indexes[i] = i;
        }
        // 快排,获取到按原数组的值升序排序后的索引数组
        Arrays.sort(indexes, new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return nums[o1]-nums[o2];
            }
        });
        for(int i=0; i<indexes.length-1; i++) {
            int rest = target - nums[indexes[i]];
            // 二分法搜索
            int index = binarySearch(nums, indexes, i+1, rest);
            if(index==-1) {
                continue;
            }
            return new int[] {indexes[i], indexes[index]};
        }
        return null;
    }
    
    /**
     * 二分法搜索
     * @param nums  原数组
     * @param indexes  排序后的索引数组
     * @param start 起始位置
     * @param target  搜索目标值
     * @return
     */
    private int binarySearch(int[] nums, Integer[] indexes, int start, int target) {
        int end = nums.length-1;
        while(start<=end) {
            int index = (start+end)/2;
            if(nums[indexes[index]]==target) {
                return index;
            }else if(nums[indexes[index]]<target) {
                start = index+1;
            }else {
                end = index-1;
            }
        }
        return -1;
    }
    
}
时间复杂度分析:快排的时间复杂度为 O(nlogn),针对数组中每个数要进行一次二分搜索,时间复杂度为logn,要遍历n个数,所以时间复杂度为O(nlogn),而总的为两者之和,所以时间复杂度为 O(nlogn)

解法三:利用Java中的Map类型来提高搜索效率

解题思路:该解题思路与第一种类似,只是在搜索的那一步将直接从数组中搜索转变为利用Java的Map类型来存储数组,从Map中搜索符合条件的值
package com.happy.leetcode.p1;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 63.85%
 * @author bird
 *
 */
public class TwoSumV5 {

    public static void main(String[] args) {
        int[] nums = {2, 5, 5, 11};
        int target = 10;
        int[] result = new TwoSumV5().twoSum(nums, target);
        System.out.println(Arrays.toString(result));
    }
    
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        int rest;
        for(int i=0; i<nums.length; i++) {
            rest = target-nums[i];
            if(map.containsKey(rest)) {
                return new int[] {map.get(rest), i};
            }
            map.put(nums[i], i);
        }
        return null;
    }
    
}
时间复杂度分析:Map的键搜索的时间复杂度为O(1),而数组遍历的时间复杂度为O(n),因此总的时间复杂度为 O(n)

解法四:使用散列的思想存储已经遍历的数组的数

解题思路:该解法思路同解法三,只是在存储时使用自己定义的散列思想将数值和其存储位置进行一一映射,这样在查找时可以大大增加时间复杂度。
package com.happy.leetcode.p1;

import java.util.Arrays;

/**
 * 99.73%
 * @author bird
 *
 */
public class TwoSumV4 {

    public static void main(String[] args) {
        int[] nums = {2, 5, 5, 11};
        int target = -14;
        int[] result = new TwoSumV4().twoSum(nums, target);
        System.out.println(Arrays.toString(result));
    }
    
    public int[] twoSum(int[] nums, int target) {
        int[] a = new int[16050];
        int temp;
        for(int i = 0; i < nums.length; ++i) {
            temp = target - nums[i] + 5;
            if(temp >= 0) {
                if(a[temp] > 0) {
                    return new int[] {a[temp] - 1, i};
                } else {
                    a[nums[i] + 5] = i + 1;
                }
            }
        }
        return null;
    }
    
}
时间复杂度分析:遍历次数为n,搜索时间复杂度为O(1),因此总的时间复杂度为 O(n)
【注意】该代码并不适用于通用性解法,因为实际的测试用例的数值范围较小,因此该散列算法方法简单,执行效率高,因此执行时间极短,但是并不适用于通用情况,比如当数值为负时无法给出正确答案


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值