题目1–1:两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
来源:力扣(LeetCode)
思路1
暴力求解:
复杂度分析:
时间复杂度:O(n^2)
空间复杂度:O(1)。
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
int i=0;
int res[]={0,0};
while(i<nums.length){
for(int j=i+1;j<nums.length;j++){
if(nums[j]==target - nums[i])
{
res[0] = i;
res[1] = j;
}
}
i++;
}
return res;
}
}
思路2
思路基本一样,只是使用HashMap以空间换取速度的方式,我们可以将查找 target - nums[i]的查找时间从 O(n) 降低到O(1)
代码
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<>();
int[] res = new int[2];
for (int i = 0; i < nums.length; i++) {
int x = target - nums[i];
if (map.get(x) != null) {
res[0] = map.get(x);
res[1] = i;
return res;
}
map.put(nums[i],i);
}
return res;
}
}
题目2–167:两数之和II -输入有序数组
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
来源:力扣(LeetCode)
思路
此题相比第一题,多了一个输入条件,就是输入有序数组,因此,我们可以初始化两个指针 low 和 high 分别指向列表的头尾。
从两个方向同时进行迭代,要么找到两数之和的解,要么两个指针相遇。
在每个步骤中,我们将根据不同的条件移动指针:
如果当前指针指向元素的和小于目标值,则应该增加总和来满足目标值,即将 low 指针向前移动获得更大的值。
如果当前指针指向元素的和大于目标值,则应该减少总和来满足目标值,即将 high 向 low 靠近来减少总和。
如果当前指针指向元素的和等于目标值,则直接返回结果。
如果两个指针相交,说明当前列表不存在组合成目标值的两个数。
数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。
代码
class Solution {
public int[] twoSum(int[] numbers, int target) {
if (numbers == null) return null;
int i = 0, j = numbers.length - 1;
while (i < j) {
int sum = numbers[i] + numbers[j];
if (sum == target) {
return new int[]{i + 1, j + 1};
} else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
}
题目3–170:两数之和 III - 数据结构设计
设计并实现一个 TwoSum 的类,使该类需要支持 add 和 find 的操作。
add 操作 - 对内部数据结构增加一个数。
find 操作 - 寻找内部数据结构中是否存在一对整数,使得两数之和与给定的数相等。
示例 1:
add(1); add(3); add(5);
find(4) -> true
find(7) -> false
示例 2:
add(3); add(1); add(2);
find(3) -> true
find(6) -> false
来源:力扣(LeetCode)
思路:
基于前两道题目的经验,对于find方法,很自然地想到用双指针,从两个方向迭代。
但是要知道,双指针方法的前提条件之一是输入列表有序。
因此,这里我们可以用一个ArrayList来保存我们的数据,同时用boolean型的数值来标记是否有序。当我们调用find()方法的时候,去检查一下,数据是否有序,若无序,则先排序,再用双指针方法去查找当前列表是否存在组合成目标值的两个数。
时间复杂度:
add(number):O(1)
find(value):O(N⋅log(N)),在最坏的情况下,我们需要对列表进行排序和遍历整个列表,这需要O(N⋅log(N)) 和O(N) 的时间。因此总的时间复杂度为O(N⋅log(N))。
空间复杂度:O(N),其中 N 指的是列表中的元素个数。
代码:
class TwoSum {
private ArrayList<Integer> nums;
private boolean is_sorted;
public TwoSum() {
this.nums = new ArrayList<Integer>();
this.is_sorted = false;
}
public void add(int number) {
this.nums.add(number);
this.is_sorted = false;
}
public boolean find(int value) {
if (!this.is_sorted) {
Collections.sort(this.nums);
this.is_sorted = true;
}
int low = 0, high = this.nums.size() - 1;
while (low < high) {
int twosum = this.nums.get(low) + this.nums.get(high);
if (twosum < value)
low += 1;
else if (twosum > value)
high -= 1;
else
return true;
}
return false;
}
}
思路:
(这个思路借鉴于力扣解题的某答案)
使用2个HashSet:
all: 负责记录所有元素
duplicate: 负责记录重复的元素
实现方法:
add:如果all中已有该元素,则放入duplicate;否则放入all
find:遍历all中的元素,寻找 target = value - num 是否存在。存在则找到。
这里是否存在分为2种情况判断:
target == num,则在重复元素集合duplicate中找;
target != num,则在所有元素集合all中找。
复杂度分析:
n为已加入的元素个数,
时间复杂度:1次add为O(1),1次find为O(n)
空间复杂度:O(n)
代码:
class TwoSum {
private Set<Integer> all;
private Set<Integer> duplicate;
/** Initialize your data structure here. */
public TwoSum() {
all = new HashSet();
duplicate = new HashSet();
}
/** Add the number to an internal data structure.. */
public void add(int number) {
if (all.contains(number))
duplicate.add(number);
else
all.add(number);
}
/** Find if there exists any pair of numbers which sum is equal to the value. */
public boolean find(int value) {
int target;
for (int num: all) {
target = value - num;
if (target == num && duplicate.contains(target))
return true;
if (target != num && all.contains(target))
return true;
}
return false;
}
}
题目4–653:两数之和 IV - 输入 BST
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
案例 1:
输入:
5
/
3 6
/ \
2 4 7
Target = 9
输出: True
案例 2:
输入:
5
/
3 6
/ \
2 4 7
Target = 28
输出: False
来源:力扣(LeetCode)
思路
我比较懒,对一个方法会用到底,这个题目和之前的区别就是输入的是一个BST,那我们只需要把输入转换成前面的有序数组就可以了。众所周知,BST的中序遍历是一个递增的有序数列。因此,相比于题目2,只需多一步中序遍历就可以了。
复杂度分析:
时间复杂度:O(n),其中 n 是树中节点的数量。本方法需要中序遍历整棵树。
空间复杂度:O(n),list 中存储 nn 个元素。
代码
public boolean findTarget(TreeNode root, int k) {
if (root == null)
return false;
List<Integer> list = new ArrayList<>();
inOrder(root, list);//相比题目2多出来的一步
int i = 0, j = list.size() - 1;
while (i < j) {
int sum = list.get(i)+list.get(j);
if (sum == k) {
return true;
} else if (sum < k) {
i++;
} else {
j--;
}
}
return false;
}
public void inOrder(TreeNode root, List<Integer> list) {
if(root == null) {
return;
}
inOrder(root.left, list);
list.add(root.val);
inOrder(root.right, list);
}