1. 字母异位词分组(HashMap)
题目:
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
题解:
- 初始化一个空的 HashMap map。
- 遍历字符串数组 strs。
- 对第一个字符串 "eat"执行:将 "eat" 转换为字符数组 ['e', 'a', 't'] 对字符数组进行排序,得到 ['a', 'e', 't']
- 使用排序后的字符数组创建 key "aet" 从 map 中获取 key 为 "aet" 的值,由于不存在,因此创建一个新的空列表 list = [] 将 "eat" 添加到 list 中,现在 list = ["eat"] 将 key 为 "aet",value 为 ["eat"] 的键值对存入 map。
- 对第二个字符串 "tea" 执行类似操作: 字符数组为 ['t', 'e', 'a'],排序后为 ['a', 'e', 't'],key 为 "aet" 从 map 中获取 key 为 "aet" 的值,存在,为 ["eat"] 将 "tea" 添加到列表中,现在列表为 ["eat", "tea"] 将更新后的列表存入 map,key 为 "aet" 对其余字符串 "tan", "ate", "nat", "bat" 执行类似操作,最终 map 为: key 为 "aet",value 为 ["eat", "tea", "ate"] key 为 "ant",value 为 ["tan", "nat"] key 为 "abt",value 为 ["bat"]
- 从 map 中获取所有 value,构造结果列表,即 [ ["eat", "tea", "ate"], ["tan", "nat"], ["bat"] ]
- 可以看到,通过将每个字符串排序作为 key,并存储字母异位词的字符串列表作为 value,算法成功将字母异位词分组了。
- 这样的分组过程更加高效,避免了对每个字符串都进行两两比较的低效操作。
代码实现:
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<String, List<String>>();
for (String str : strs) {
char[] array = str.toCharArray();
Arrays.sort(array);
String key = new String(array);
List<String> list = map.getOrDefault(key, new ArrayList<String>());
list.add(str);
map.put(key, list);
}
return new ArrayList<List<String>>(map.values());
}
}
总结:利用HashMap中元素的唯一性,来进行两个字符串是否存在相同元素的判断。
2. 最长连续序列(HashSet)
题目:
给定一个未排序的整数数组nums,找出数字连续的最长序列的长度。
不要求序列元素在原数组中连续,并且算法时间复杂度为O(n)。
题解:
- 首先利用HashSet集合对数组进行去重,减少重复比较
- for循环遍历集合中的每个值num,当集合中不存在比该值的小的值,即set.contains(num-1)为false时,记录该元素为currentNum,记录当前连续序列长度currentStreak为1。
- 接下来,使用while循环,每次查找比currentNum大1的值是否存在,即判断set.contains(currentNum+1)是true or false,如果是true,则将当前连续序列长度currentStreak加1,并且将当前元素currentNum+1,循环下去直到不满足条件。
- while循环结束后,得到以num开头的最长连续序列长度currentStreak,将其与当前最长连续序列长度longestStreak进行比较,取较大的值重新赋值给longestStreak。
- for循环遍历结束,返回最终的最长连续序列长度longestStreak。
代码实现:
class Solution {
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;
}
}
总结:
- 利用HashSet的contains方法,判断某元素是否在集合内,简化比较过程。
- 利用判断(num-1)是否在集合内的方法,提前把不可能是连续序列的开头的数据排除了,避免了存在比当前序列更长的序列的情况。
3. 移动零
题目:
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。必须在不复制数组的情况下原地对数组进行操作。
代码实现:
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length, left = 0, right = 0;
while (right < n) {
if (nums[right] != 0) {
swap(nums, left, right);
left++;
}
right++;
}
}
public void swap(int[] nums, int left, int right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
总结:
- 利用两个变量作为指针,左指针的作用是找到0,右指针的作用是指向0的后一元素。
- 通过左右指针的互换,实现在保持非零元素顺序不变的情况下,将0移到数组末尾。
4. 盛最多水的容器(双指针)
题目:
- 给定一个长度为n的整数数组height。
- 有n条垂线,第i条线的两个端点是(i,0)和(i,height[i])。
- 找出其中的两条线,使得它们与x轴共同构成的容器可以容纳最多的水。
- 返回容器可以储存的最大水量。
题解:
代码实现:(存在超时错误)
class Solution {
public int maxArea(int[] height) {
int waterfilling=0;
for (int i=0;i<height.length-1;i++){
int left=i;
int right=i+1;
int currentwater=0;
while(right<height.length){
currentwater=(right-left)*Math.min(height[left],height[right]);
if(currentwater>waterfilling){
waterfilling=currentwater;
}
right++;
}
}
return waterfilling;
}
}
代码实现:(通过)
class Solution {
public int maxArea(int[] height) {
int left=0;
int right=height.length-1;
int waterfilling=0;
int currentwater=0;
while(true){
currentwater=(right-left)*Math.min(height[left],height[right]);
if(currentwater>waterfilling){
waterfilling=currentwater;
}
if(height[left]<height[right]){
left++ ;
}else{
right--;
}
if(left==right){
break;
}
}
return waterfilling;
}
}
总结: