LeetCode100刷题笔记

哈希

  1. 两数之和
    两种解法,一种双层for,算法复杂度O(n^2)
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[0];
}

一种是HashMap存储遍历过的值的方式,这样整体只需要遍历一次就可以找到答案了,算法复杂度O(n),用空间换时间

public int[] twoSum(int[] nums, int target) {
	Map<Integer, Integer> map = new HashMap<>();
	for (int i = 0; i < nums.length; i++) {
		if (map.containsKey(target - nums[i])) {
			return new int[]{map.get(target - nums[i]), i};
		}
		map.put(nums[i], i);
	}
	return new int[0];
}
  1. 字母异位词分组
    这个是题解里面的一个解法,使用到了流操作的一些方法来解决这个问题,主要的思想是将每个单词的字母都打散,然后按照字母表的顺序重新编排,字母异位词就会被排到一起去了,其中,collect返回的结果是一个map,map的key部分是重新洗牌后的单词,而value部分则是原来的单词,所以最后需要调用values方法获得原始的单词。
public List<List<String>> groupAnagrams(String[] strs) {
	return new ArrayList(Arrays.stream(strs)
		 .collect(Collectors.groupingBy(item -> {
			 char[] chars = item.toCharArray();
			 Arrays.sort(chars);
			 return new String(chars);
		 }))
		 .values());
}
  1. 最长连续序列
    使用set先将所有的数据都存进去一下,然后对set进行遍历,判断该数字是否已经被计数,如果已经被计数过了,就不用再次计数,跳过。内层的while作用就是用来进行计数的,而外层的就是将记过数的数据跳过去的。
class Solution {
    public int longestConsecutive(int[] nums) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
        int ret = 0;
        for (int num : set) {
            if (!set.contains(num - 1)) {
                int curNum = num;
                int count = 1;
                while (set.contains(curNum + 1)) {
                    count++;
                    curNum++;
                }
                ret = Math.max(ret, count);
            }
        }
        return ret;
    }
}

双指针

  1. 移动零
    虽然,但是,这道题使用一个index指针就够了,复杂度O(n)
class Solution {
    public void moveZeroes(int[] nums) {
        int index = 0;
        for (int num : nums) {
            if (num != 0) {
                nums[index++] = num;
            }
        }
        for (int i = index; i < nums.length; i++) {
            nums[i] = 0;
        }
    }
}
  1. 盛最多水的容器
    双指针方式解决,双指针指向左右两边的柱子下标,下标相减就是容器的“宽度”,两根柱子中小的哪一根就会成为容器容纳量的瓶颈。容器的容量由“高度”和“宽度”两部分组成,两部分的最佳比例就是我们要找的目标,并且容器的“高度”和“宽度”之间还有一定的对应关系,所以,我们解决这个问题的时候思路上就可以想办法将一个量作为标准,让这个量缓慢变化,而同时尽量使另一个量达到最优的结果,遍历所有这两个量的组合,算出里面最好的一种组合,然后作为结果返回即可。
class Solution {
    public int maxArea(int[] height) {
        // 下标相减就是容器的“宽度”,“高度”就是数组元素的大小
        int left = 0;
        int right = height.length - 1;
        int ret = 0;
        while (left < right) {
            ret = Math.max((right - left) * Math.min(height[left], height[right]), ret);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return ret;
    }
}
  1. 三数之和
    这道题的重点在于去重,下面这种解法按照我的理解就是三重的去重操作,第一重是对于第一个选择的数字去重,然后是对于第二个,第三个
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ret = new ArrayList<>();
        if (nums.length < 3) {
            return ret;
        }
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) {
                break;
            }
            if (i != 0 && nums[i - 1] == nums[i]) {
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] == 0) {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    ret.add(list);
                    while (left < right && nums[left] == nums[++left]);
                    while (left < right && nums[right] == nums[--right]); 
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    while (left < right && nums[left] == nums[++left]);
                } else {
                    while (left < right && nums[right] == nums[--right]); 
                }
            }
        }
        return ret;
    }
}
  1. 接雨水
    单调栈,满足大于栈顶的就有可能接水,否则,放进去。
class Solution {
    public int trap(int[] height) {
        if (height == null) {
            return 0;
        }
        Stack<Integer> stack = new Stack<>();
        int ret = 0;
        for (int i = 0; i < height.length; i++) {
            while (!stack.empty() && height[stack.peek()] < height[i]) {
                int curIndex = stack.pop();
                while (!stack.empty() && height[curIndex] == height[stack.peek()]) {
                    stack.pop();
                }
                if (!stack.empty()) {
                    int stackTop = stack.peek();
                    ret += (i - stackTop - 1) * (Math.min(height[stackTop] - height[curIndex], height[i] - height[curIndex]));
                }
            }
            stack.push(i);
        }
        return ret;
    }
}

滑动窗口

  1. 无重复字符的最长子串
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int l = 0; 
        int r = 0; 
        int ret = 0;
        Set<Character> set = new HashSet<>();
        while (r < s.length()) {
            if (!set.contains(s.charAt(r))) {
                set.add(s.charAt(r));
                ret = Math.max(ret, r - l + 1);
                r++;
            } else {
                set.remove(s.charAt(l));
                l++;
            }
        }
        return ret;
    }
}
  1. 找到字符串中所有字母异位词
    这道题的解法重点在下面的for循环中,上面的for只是将“模板单词”包含的字母数量进行记录,而重点来了,下面的那个for遍历所有的s字符串中的字母,利用“模板单词”的长度与要在s字符串中找到的异位词子串间的关系,将整道题的解法变得十分奇妙,判断是否匹配到异位词只需要判断滑动窗口的长度是否满足条件,这样做的前提是前面将s字符串滑动串口中的单词数量统计到一个数组中去后,对数组相应位置的数量判断是否满足要求,不满足则进行调整。
class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> ret = new ArrayList<>();
        int n = s.length();
        int m = p.length();
        if (n < m) {
            return ret;
        }
        int[] sCut = new int[26];
        int[] pCut = new int[26];
        for (int i = 0; i < m; i++) {
            pCut[p.charAt(i) - 'a']++;
        }
        int left = 0;
        for (int right = 0; right < n; right++) {
            int curRight = s.charAt(right) - 'a';
            sCut[curRight]++;
            while (sCut[curRight] > pCut[curRight]) {
                sCut[s.charAt(left) - 'a']--;
                left++;
            }
            if (right - left + 1 == m) {
                ret.add(left);
            }
        } 
        return ret;
    }
}

子串

posted @ 2023-04-27 09:19  薯叶  阅读(73)  评论(0)    收藏  举报