1、字母异位词分组(L49)
//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 // // 示例: // // 输入: ["eat", "tea", "tan", "ate", "nat", "bat"] //输出: //[ // ["ate","eat","tea"], // ["nat","tan"], // ["bat"] //] // // 说明: // // // 所有输入均为小写字母。 // 不考虑答案输出的顺序。 // // Related Topics 哈希表 字符串
这里有两点需要注意,第一如何确定字母异位词他们统一的标识,方便存储,这个不管是通过计数或者排序都可以解决,生成每类字母异位词唯一的标识。第二是在排序方法中,将数组转换为字符串的时候,使用的是new String()而不是.toString。
1、计数法
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
int[] counts = new int[26];
for (int i = 0; i < str.length(); i++) {
counts[str.charAt(i) - 'a']++;
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 26; i++) {
if(counts[i] != 0){
//把这个作为哈希表的键进行存储
sb.append((char)('a' + i));
sb.append(counts[i]);
}
}
List<String> list = map.getOrDefault(sb.toString(), new ArrayList<String>());
list.add(str);
map.put(sb.toString(), list);
}
return new ArrayList<List<String>>(map.values());
}
2、排序法
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
char[] chars = str.toCharArray();
Arrays.sort(chars);
String s = new String(chars);
List<String> list = map.getOrDefault(s, new ArrayList<String>());
list.add(str);
map.put(s, list);
}
return new ArrayList<List<String>>(map.values());
}
2、跳跃游戏(L55)
//给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。 // // 数组中的每个元素代表你在该位置可以跳跃的最大长度。 // // 判断你是否能够到达最后一个下标。 // // // // 示例 1: // // //输入:nums = [2,3,1,1,4] //输出:true //解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。 // // // 示例 2: // // //输入:nums = [3,2,1,0,4] //输出:false //解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。 // // // // // 提示: // // // 1 <= nums.length <= 3 * 104 // 0 <= nums[i] <= 105 // // Related Topics 贪心算法 数组
有两种解法,一种是从前往后,一种是从后往前,第一种比较好理解,就是不断往后移动并计算我们从第一个点出发可以达到的最远的地方是什么位置,一直到最后,如果最后一个端点在这个最远位置的范围之内,那就是可以到达的。
public boolean canJump(int[] nums) {
//不断更新最远到达位置
int rightmost = 0;
int n = nums.length;
for (int i = 0; i < n - 1; i++) {
if(i <= rightmost){
rightmost = Math.max(rightmost, i + nums[i]);
if(rightmost >= n-1){
return true;
}
}
}
return false;
}
剩下的一种是从后往前,找到可以到达最后一个端点的点我们就不用管后面了,就把这个点到端点之间进行截断,也相当于把这个点当做是端点,一直往前,我们会找到第一个点之后的第一个能够到达端点的点s,如果第一个点能够到达s,那就肯定可以达到最后一个点。
public boolean canJump(int[] nums) {
int n = 1;
for (int i = nums.length - 2; i >= 0; i--) {
//如果前面的点能够到达最后一个点
if(nums[i] >= n){
n = 1;
}else{
n++;
}
//说明第一个点到不了它后一个能够到达终点的点
if(i == 0 && n > 1){
return false;
}
}
return true;
}