主要整理了力扣HOT100相关题目解析。后续有新的实现思路会不断完善。具体原理全部都放到了代码注释中,可整体直接copy到本地运行、理解。
1. 哈希
1.1 两数之和问题
class Solution {
public int[] twoSum(int[] nums, int target) {
//固定第一个数字为i进行循环,第二个数字那就是sum-i
int len = nums.length;
//将第二个数字存入map集合中,key:sum-i的那个数字;
//value:如果该key存在于nums数组中,那么就可以将该数字的下标作为该key的value;
Map<Integer,Integer> ht = new HashMap<Integer,Integer>();
for(int i =0;i<len;i++){
int remaining = target-nums[i];
/**
判断remaining是否存在于map中,存在的话直接返回即可;
此时还没有将i及其值放入到map中,所以后续遍历要先不断放入后面的数字;
一直到想要的数字并且map也存在这个数字的时候才会返回值;
此时的i代表了第二个数字的索引,因此位于第二位;
在map中找到想要的那个数字其实是第一位。
*/
if(ht.containsKey(remaining)){
return new int[]{ht.get(remaining),i};
}
//将i在数组中对应的值作为key,索引i作为value,放入到map集合中
ht.put(nums[i],i);
}
return new int[0];
}
}
1.2 字母异位词分组
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
//hashmap存储:key是排序后的唯一字符串,value是可以变换为key的字符串列表
Map<String, List<String>> map = new HashMap<String, List<String>>();
for (String str : strs) {
/**
遍历排序所有的strs,然后将其排序
排序的字符串作为key(可保证唯一性)
而每次获取的array作为value即可*/
char[] array = str.toCharArray();
Arrays.sort(array);
String key = new String(array);
//创建列表,将map中具有相同key的字符串放到一个列表中,
//不存在的话创建新的空列表(这表示一种新的字母异位词分组形式)
List<String> list = map.getOrDefault(key, new ArrayList<String>());
//将这个str添加到对应的那个list中
list.add(str);
//map中此时将排序后唯一的字符串视为key;
//可以还原成key的字符串存储到了list中,将这个list作为hashmap的值
map.put(key, list);
}
//通过.values()方法获取所有值的集合
return new ArrayList<>(map.values());
}
}
1.3 最长连续序列
class Solution {
public int longestConsecutive(int[] nums) {
//转换成hashset存储nums中的所有数据
//hashset过滤掉重复元素,可直接符合数字连续的要求
HashSet<Integer> set = new HashSet<Integer>();
for (int num : nums) {
set.add(num);
}
//先定义最小长度是0(有空数组)
int sublen = 0;
//对于hashset中的每一个num进行判断
for (int num : set) {
/**判断hashset中是否存在num-1这个数字,
true,表明现在这个num不是最小的起始点,直接跳过对该num的操作;
等于fasle表明他可能是最小的,需要下一步判断
注意这里无法判断全局最小,如果要全局最小,
需要在这里嵌套一个for循环遍历整个set,复杂度变高*/
if (set.contains(num - 1)) {
continue;
}
//先定义此时这个for循环中,临时子序列的长度以及临时数字的大小
int forlen = 1;
int fornum = num;
/**
判断hashset中是否包含比此时的临时数字要大1的数字,
有的话表明连上了,继续while到下一个数字;
直到连不上数字时,此时就存储了以num为起始点,
连续子序列的最大值fornum,以及最长的长度值forlen*/
while (set.contains(fornum + 1)) {
fornum += 1;
forlen += 1;
}
//此前获取到的最长长度 和 该循环中的num得到的最长长度值,取max即可
sublen = Math.max(sublen, forlen);
}
return sublen;
}
}
2. 双指针(快慢指针)解决问题
2.1 移动target元素
class Solution {
public void moveZeroes(int[] nums) {
//核心:slowIndex用于存储不为val的索引,fastIndex往后找不为val的位置
int val = 0;
int len = nums.length;
//慢指针,每一个slowIndex的位置都代表了将要存储的不为val的数据的索引
int slowIndex = 0;
//快指针遍历整个nums数组
for (int fastIndex = 0; fastIndex < len; fastIndex++) {
//快指针用于寻找nums中不等于val的数字索引位置
//当找到不为val的位置时,将此时slowIndex处于的位置赋值为找到的值
if (nums[fastIndex] != val) {
nums[slowIndex] = nums[fastIndex];
//找到后,相当于slowIndex这个位置已经存在新的数据了,继续定位下一个位置即可。
slowIndex++;
}
}
/**for循环后,代表了已经重新将所有不为val的数据放到了最前面,
并且此时slowIndex指向了前面数据的下一个位置,将数组后面填充val即可*/
while (slowIndex < len) {
nums[slowIndex] = val;
slowIndex++;
}
}
}
2.2 三数之和
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
int len = nums.length;
Arrays.sort(nums);
for (int first = 0; first < len; first++) {
//当最小的fisrt都大于0的时候,就没必要进行了
if (nums[first] > 0) {
break;
}
//如果first和前面的值重复了,需要跳过
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
/**
维护第二个数字和第三个数字
类似于双指针的形式,来指定范围 */
int second = first + 1, third = len - 1;
while (second < third) {
int sum = nums[first] + nums[second] + nums[third];
if (sum == 0) {
//满足要求时,需要将f,s,t存入到列表中
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
//将该列表存入到最终结果集合中
result.add(list);
second++;
third--;
//second重复时跳过 后面 重复的second继续往后
while (second < third && nums[second] == nums[second - 1]) {
second++;
}
//third重复同样要跳过 前面 重复的third继续往前
while (second < third && nums[third] == nums[third + 1]) {
third--;
}
} else if (sum > 0) {
//sum过大,需要缩小最大值third
third--;
} else {
//sum过小,需要缩小最小值second
second++;
}
}
}
return result;
}
}
2.3 盛水最多的容器
class Solution {
public int maxArea(int[] height) {
//使用左右双指针的方法,逐步缩小左右区间范围,在这个过程中不断更新maxarea
int left = 0;
int right = height.length - 1;
int maxarea = 0;
while (left < right) {
//取最矮的作为容器高度
int minheight = Math.min(height[left], height[right]);
//计算此时区间面积
int currArea = minheight * (right - left);
//比较此时的区间面积,已获取到的最大面积,取二者最大值
maxarea = Math.max(maxarea, currArea);
//需要更新影响面积的索引(最矮的那个位置的索引需要移动)
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxarea;
}
}
2.4 接雨水问题
3. 滑动窗口
3.1 无重复字符的最长子串
class Solution {
public int lengthOfLongestSubstring(String s) {
/**
左右指针表示窗口边界,不同于[0,len]开始缩小
这个窗口大小是从0开始扩张,然后收缩
有不重复的话右指针扩展边界,重复的话左指针缩小窗口边界*/
Map<Character, Integer> window = new HashMap<>();
int left = 0, right = 0, len = s.length();
int result = 0;
//需要使用右指针逐渐向右侧移动遍历整个字符串
while (right < len) {
//记录当前右指针对应的字符的值
char right_bound_char = s.charAt(right);
right++;
/**
将该值记录到map中,key是该字符,value:
如果能get到那么获取在map中该字符真实的出现次数;
否则默认是第一次出现,记录为0+1.*/
window.put(right_bound_char, window.getOrDefault(right_bound_char, 0) + 1);
/**
当这个窗口中最新的字符存在重复时
那么需要缩小窗口直至最新get到的字符的value不大于1
也就是一直缩小到每个字符最多一次时的窗口才能符合要求*/
while (window.get(right_bound_char) > 1) {
//获取左边界的字符
char left_bound_char = s.charAt(left);
left++;
//将左边界字符的value值进行更新,为get-1
window.put(left_bound_char, window.get(left_bound_char) - 1);
}
//result记录的是不断更新的最新长度;
//right - left表示的是当前循环所能达到的最长长度
result = Math.max(result, right - left);
}
return result;
}
}
3.2 找到字符串的字母异位词
class Solution {
public List<Integer> findAnagrams(String s, String p) {
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
//将子串p的所有字符以及各字符的数量存入到need中
for (char c : p.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
int valid = 0;
//列表存储符合要求的子串起始索引
List<Integer> resultList = new ArrayList<>();
while (right < s.length()) {
char right_bound_char = s.charAt(right);
right++;
//当need中存在这个字符的时候,需要将其添加到window集合中
if (need.containsKey(right_bound_char)) {
window.put(right_bound_char, window.getOrDefault(right_bound_char, 0) + 1);
//当该字符在window和need两个map中的数量一样时,
//表明满足了这个字符以及数量限制的要求
if (window.get(right_bound_char).equals(need.get(right_bound_char))) {
valid++;
}
}
//确保窗口长度不超过目标字符串p的长度,在窗口内字符频率达标的时候及时记录结果
while (right - left >= p.length()) {
//当通过上面得到的valid正好也是need长度的时候,表示完全符合条件,
//将其索引存入到list中
if (valid == need.size()) {
resultList.add(left);
}
//通过left缩小左边界
char left_bound_char = s.charAt(left);
left++;
//need中包含这个字符时
if (need.containsKey(left_bound_char)) {
//need需要这个字符,但是窗口左边界又要右移的时候,
//如果之前恰好满足了数量的条件,此时移动了,必然使valid的数量-1
if (window.get(left_bound_char).equals(need.get(left_bound_char))) {
valid--;
}
//更新这个字符在window中的数量,value-1
window.put(left_bound_char, window.get(left_bound_char) - 1);
}
}
}
return resultList;
}
}
3.3 最小覆盖子串问题
class Solution {
public String minWindow(String s, String t) {
//核心:窗口增加和减少的时机判断
//window需要先扩大,直至满足子串的要求
//随后针对大范围的window进行缩小,使其满足最终的要求
Map<Character, Integer> window = new HashMap<>();
Map<Character, Integer> need = new HashMap<>();
for (char c : t.toCharArray()) {
need.put(c, need.getOrDefault(c, 0) + 1);
}
int left = 0, right = 0;
//当valid==need.size()的时候表示全部拿到了所需的字符
int valid = 0;
//start记录开始位置,len表示子串长度,那么(s,s+l)就是最终子串
//注意len设置,需要在循环中对其进行更新,便于最终返回时的判断
int start = 0, len = Integer.MAX_VALUE;
while (right < s.length()) {
//得到每个字符
char right_bound_char = s.charAt(right);
right++;
//判断need中是否存在这个字符,需要的话put这个字符,并使valid+1
if (need.containsKey(right_bound_char)) {
window.put(right_bound_char, window.getOrDefault(right_bound_char, 0) + 1);
if (window.get(right_bound_char).equals(need.get(right_bound_char))) {
valid++;
}
}
//当满足valid==need.size()时候,就需要精细化字符范围,缩小窗口长度
while (valid == need.size()) {
//更新最小覆盖子串的位置:从start开始到start+len的范围就是最小子串
//要保证这次的窗口长度需要小于原来的长度,继续缩小
if (right - left < len) {
start = left;
len = right - left;
}
//从左侧开始循环,判断需要移除的字符
char left_bound_char = s.charAt(left);
left++;
//当need中包含这个字符,需要移除这个字符
if (need.containsKey(left_bound_char)) {
//判断:如果此时window中该字符数量和need需要的该字符数量一致的时候,
//如果移除了该字符必然会导致valid-1;
if (window.get(left_bound_char).equals(need.get(left_bound_char))) {
valid--;
}
window.put(left_bound_char, window.get(left_bound_char) - 1);
}
}
}
//整体循环结束后,返回最小覆盖子串
//len之前设置为maxvalue,如果没有在循环中更新那就表明不存在这样的子串返回""",
//否则返回范围(s,s+l)
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
}
4. 子串
**总结**
如果觉得对你有帮助的话,可以收藏起来方便后续浏览;或者您有什么建议的话,可以在评论区交流,后续我也会不断维护。谢谢!
1247

被折叠的 条评论
为什么被折叠?



