两数之和

Posted by zhouqian on Wednesday, July 6, 2022

力扣 1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。


方案一:利用无需映射 map,保留遍历途中经过的每隔数,每经过一个数判断一次 map[target - num] 是否存在。

func twoSum(nums []int, target int) []int {
    mp := make(map[int]int, len(nums))
    for idx, num := range nums {
        if v, ok := mp[target - num]; ok {
            return []int{v, idx}
        }
        mp[num] = idx
    }
    return nil
}
  • 时间复杂度:O(n),因为需要遍历数组中所有所有数字,所以时间复杂度为 O(n)。
  • 空间复杂度:O(n),因为使用了长度不超过 n 的无序映射,所以空间复杂度为 O(n)。

方案二:双指针,先记录元素下标和值,然后进行快排,计算指针指向元素的和。

type node struct {
    idx, val int // 数组下标和值
}

func twoSum(nums []int, target int) []int {
    // 构建 node 数组
    nodes := make([]*node, len(nums))
    // 遍历 nums,记录元素下标和值
    for i, v := range nums {
        nodes[i] = &node{
            idx: i,
            val: v,
        }
    }
    // 自定义排序,使得节点元素按照 val 递增(时间复杂度约 nlogn)
    sort.Slice(nodes, func(i, j int) bool { return nodes[i].val < nodes[j].val })

    //  定义双指针,分别指向 nodes 数组头和尾
    l, r := 0, len(nodes) - 1
    for l < r {
        // 计算两个指针指向的节点的 val 的和
        switch sum := nodes[l].val + nodes[r].val; {
            case sum > target:
                r--
            case sum < target:
                l++
            default:
                return []int{nodes[l].idx, nodes[r].idx}
        }
    }
    return nil
}
  • 时间复杂度:O(nlogn),程序中存在多次长度不大于 n 的遍历以及一次自定义排序(越耗时 nlogn),所以时间复杂度为 O(nlogn)。
  • 空间复杂度:O(nlogn),因为使用了长度为 n 的数组 nodes,但快排造成的空间消耗为 nlogn,所以空间复杂度为 O(nlogn)。