题:
//给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
//
// 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
//
// 你可以按任意顺序返回答案。
//
//
//
// 示例 1:
//
//输入:nums = [2,7,11,15], target = 9
//输出:[0,1]
//解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
//
//
// 示例 2:
//
//输入:nums = [3,2,4], target = 6
//输出:[1,2]
//
//
// 示例 3:
//
//输入:nums = [3,3], target = 6
//输出:[0,1]
//
//
//
//
// 提示:
//
//
// 2 <= nums.length <= 10⁴
// -10⁹ <= nums[i] <= 10⁹
// -10⁹ <= target <= 10⁹
// 只会存在一个有效答案
//
//
//
//
// 进阶:你可以想出一个时间复杂度小于 O(n²) 的算法吗?
//
// Related Topics 数组 哈希表 👍 16687 👎 0
package questions.leetcode.editor.cn
fun main() {
val solution = TwoSum1.Solution()
val result = solution.twoSum(intArrayOf(2, 7, 9, 11), 9)
println(result.asList())
}
class TwoSum1 {
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
fun twoSum(nums: IntArray, target: Int): IntArray {
if (nums.size < 2) {
return intArrayOf(0, 0)
}
val map = HashMap<Int, Int>()
for (i in nums.indices) {
val temp = target - nums[i]
if (map.containsKey(temp)) {
return intArrayOf(map[temp]!!, i)
} else {
map[nums[i]] = i
}
}
return intArrayOf(0, 0)
}
}
//leetcode submit region end(Prohibit modification and deletion)
}
解析:
这种两个数配对的题目都可以用 Map 来解决,主要是用的Map查找效率高的特性,仅仅由于 Array 的查找效率不如 Map 而已。
将遍历过的数据存放到 Map 里面,并在遍历后面的数的时候,在 Map 里查询是否存在可以配对的数据
扩展
题目说找出唯一的答案即可
现在改为有序数组,找出所有的答案(前提是数组里至少有一个答案)
// value: -1 1 1 4 4 5 6 7 10 12 14 21
// index: 0 1 2 3 4 5 6 7 8 9 10 11
// target = 9
// 输出 `List<IntArray>` 为{(0,8), (3,5), (4,5)} (顺序可乱)
// 由于 Map 有去重的特性,如果我们再用上述方法,就会在遍历到下标为 4 的 4 的时候,将 下标原本为 3 的 4 覆盖 ,然后失去 (3,5) 这个对
// 可以在这里停下思考一下
// 所以我们需要一个不去重,但是可以和 HashMap 拥有同级别的查找效率的数据结构
// 而 HashMap 是用哈希值来计算数组索引,然后进行红黑树查找的,
// 所以要么我们自己重写 HashMap 要么想别的办法
package questions.leetcode.editor.cn
fun main() {
val solution = TwoSum1Extension.Solution()
val result = solution.twoSum(intArrayOf(-1, 1, 1, 4, 4, 5, 6, 7, 10, 12, 14, 21), 9)
for (t in result) {
println("(${t[0]},${t[1]})")
}
}
class TwoSum1Extension {
// 计算有序数组的 两数之和结果
class Solution {
/**
* 分别寻找两个数,小于 target/2 ,并且最接近的 target/2 的数 以及 大于 target/2 ,并且最接近的 target/2 的数
* 例如:1,2,3,4,5,5,6,7 target = 9,则这两个数为 4 和 5,
* 例如:-3,-1,0,9,11 target = -1, 则这两个数为 -1 和 0
* 找到这两个数分别向两边寻找
*/
fun twoSum(orderedNums: IntArray, target: Int): List<IntArray> {
if (orderedNums.size < 2) {
return emptyList()
}
val result = ArrayList<IntArray>()
var closeSmaller = 0
var closeBigger = 0
// 如果最小的数小于 target/2 则不存在这样的数对之和等于 target (因为最小的数 ×2 已经大于 target 了)
if ((target / 2) < orderedNums[closeSmaller]) {
return emptyList()
}
for (i in orderedNums.indices) {
if (orderedNums[i] < target / 2 + 1) {
closeSmaller = i
} else {
break
}
}
if (closeSmaller < orderedNums.size - 1) {
closeBigger = closeSmaller + 1
} else {
return emptyList()
}
if (closeSmaller <= orderedNums.size / 2 + 1) {
for (i in closeSmaller downTo 0) {
for (j in closeBigger until orderedNums.size) {
when {
orderedNums[i] + orderedNums[j] == target -> {
result.add(intArrayOf(i, j))
}
orderedNums[i] + orderedNums[j] > target -> {
break
}
}
}
}
}
return result
}
}
}
时间复杂度为