目录
1.两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
思路分析
在我们还没学习哈希表之前,我们可以使用暴力递归的方法做这题,下面先提供暴力递归的思路:
遍历得到第一个数,再在剩下的数字遍历,分别得到每个数字。找到相加等于目标值的就直接返回,这种方式虽然说思路简单,但是时间复杂度不如使用哈希表,下面提供暴力递归的代码:
class Solution {
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 new int[]{-1,-1};
}
}
如果我们追求更好的时间复杂度的话,可以用空间换时间,选择创建哈希表去完成,相较于暴力递归,使用哈希表只用遍历一次数组,大大节省了时间,下面讲一下使用哈希表的思路:
- 创建一个新的哈希表
- 确定以数作为 key ,索引作为 value,y = target - x
- 遍历到一个数时,到哈希表内寻找有没有哪个数和这个数相加等于target,如果没有,则把这个数以及这个数的索引存入哈希表,如果有,直接返回这个数及其索引
代码演示
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int x = nums[i];
int y = target - x;
if(map.containsKey(y)){
return new int[]{x,map.get(y)};
}else {
map.put(x,i);
}
}
return new int[]{-1,-1};
}
2.字母异位次分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
思路分析
这道题,我们可以想到两种思路,下面先介绍利用单词本身的顺序结构作为key解题
同样的,我们先创建一个空的哈希表,作为字母异位词,即为单词字母的使用个数相同但字母的位置不同,以力扣给的示例为例,我们把单词从小到大排的顺序作为 key 把单词本身作为 value
这样,我们就可以根据 key 迅速锁定我们遍历得到的新的 value应该存放在哪里,如果没有在哈希表内找到 key,则创建一块新的集合,存放相同key的value
第二种思路,将26个整数数组作为key(注意,本题规定字母都为小写)
因为不能使用整数数组作为 key,所以我们要先进行封装,然后每个单词统计每个字母的个数,就能够作为 key
代码演示
使用顺序排列的单词作为 key:
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,List<String>> map = new HashMap<>();//创建空哈希表
for (String str : strs) {
char[] chars = str.toCharArray(); // 拿到一个单词的每个字母
Arrays.sort(chars); //排列字母顺序
String key = new String(chars); //把排列完的作为 key 存储起来
List<String> list = map.computeIfAbsent(key,k -> new ArrayList<>()); //查询哈希表中是否存在key,不存在就创建新的集合
}
return new ArrayList<>(map.values());//返回结果
}
使用 26个字母的数组作为 key(本题不建议):
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<ArrayKey,List<String>> map = new HashMap<>();
for (String str : strs) {
ArrayKey key = new ArrayKey(str);
List<String> list = map.computeIfAbsent(key, k -> new ArrayList<>());
list.add(str);
}
return new ArrayList<>(map.values());
}
static class ArrayKey{
int[] key = new int[26];
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ArrayKey arrayKey = (ArrayKey) o;
return Arrays.equals(key, arrayKey.key);
}
@Override
public int hashCode() {
return Arrays.hashCode(key);
}
public ArrayKey(String str){
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i); //'a'- 97,'b' - 98
key[ch-97]++;
}
}
}
3.最长连续数列
给定一个未排序的整数数组 nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n)
的算法解决此问题。
思路分析
这道题目力扣官方只给出使用哈希表一种解法,即
虑枚举数组中的每个数 x,考虑以其为起点,不断尝试匹配 x+1,x+2,⋯ 是否存在,假设最长匹配到了 x+y,那么以 x 为起点的最长连续序列即为 x,x+1,x+2,⋯ ,x+y,其长度为 y+1,我们不断枚举并更新答案。这种方式在力扣跑的代码其实并不理想,因为外部虽然只循环了一次但是,内部循环的次数也是很多的,不如直接排序的运行速度快,下面介绍直接排序的方法
我们设置返回值初始长度为 1,先对数组从小到大进行排序(Arrays.sort()),然后遍历数组,当遇到当前这个数是前一个数 + 1时,把返回值 +1,当遇到不满足条件时,将返回值重新设置为 1,最后不同的返回值之间进行比较,返回最大的即可
代码演示
class Solution {
//直接排序
/*public int longestConsecutive(int[] nums) {
if(nums.length < 1){
return 0;
}
Arrays.sort(nums);
int length = 1;
int res = 1;
for (int i = 1; i < nums.length; i++) {
if(nums[i] == nums[i - 1]){
continue;
} else if (nums[i] == nums[i - 1] + 1) {
length++;
res = Math.max(res,length);
}else {
length = 1;
}
}
return res;
}*/
//使用哈希表
public int longestConsecutive(int[] nums) {
Set<Integer> num_set = new HashSet<Integer>();
for (int num : nums) {
num_set.add(num);
}
int longestStreak = 0;
for (int num : num_set) {
if (!num_set.contains(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (num_set.contains(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = Math.max(longestStreak, currentStreak);
}
}
return longestStreak;
}
}