前言
前言:LeetCode Hot 100 题详解
文章目录
1. 两数之和
- 原题链接:两数之和
- 知识点:哈希表
- 思路
- 枚举第二个数;
- 哈希表存储所有数;
- 查找哈希表中是否存在目标数;
- 返回两个数的下标;
- 时间复杂度:O(n)
- 题解
class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> heap = new HashMap<>(); // 哈希表用来存储所有数 for (int i = 0; i < nums.length; i ++) { // 枚举第二个数 int another = target - nums[i]; // 目标数 if (heap.containsKey(another)) { // 哈希表中是否存在 return new int[] {heap.get(another), i}; // 存在,返回对应下标 } heap.put(nums[i], i); // 每次都将数组中的每一元素加入到哈希表中 } return null; } }
2. 字母异位词分组
- 原题链接:字母异位词分组
- 知识点:哈希表
- 题意解读
- 给定一个字符串数组,返回字母相同但排序顺序不同的所有的字符串组合;
- 思路
- 对所有的字符串进行排序;
- 将排序后相同的字符串存储同一个数组中;
- 遍历哈希表,返回哈希表中所有的 value;
- 题解
class Solution { public List<List<String>> groupAnagrams(String[] strs) { List<List<String>> res = new ArrayList<>(); // 答案数组 Map<String, List<String>> heap = new HashMap<>(); // 哈希表 for (int i = 0; i < strs.length; i ++) { char[] c = strs[i].toCharArray(); // 将每个字符串转换为字符数组 Arrays.sort(c); // 对转换后的字符串串进行排序,排序按照 ASCII String s = String.valueOf(c); // 或者 String s = new String(c); if (!heap.containsKey(s)) heap.put(s, new ArrayList<>()); // 新增键和值 heap.get(s).add(strs[i]); // 将相同键的值存到一个数组中 } for (List<String> tmp : heap.values()) // heap.values() 返回哈希表中所有值的集合 res.add(tmp); return res; } }
3. 最长连续序列
- 原题链接:最长连续序列
- 知识点:哈希表
- 思路
- 将所有数存入哈希表中;
- 枚举原数组中的所有数,保证枚举的每个数作为答案长度的起点(枚举完后删除,避免出现重复起点),依次 +1 进行判断是否存在于哈希表中,直到不存在,求出最大长度 ;
- 题解
class Solution { public int longestConsecutive(int[] nums) { // 先将所有数插入到哈希表中,再去枚举所有数 Set<Integer> hashSet = new HashSet<>(); for (int x : nums) hashSet.add(x); int res = 0; // 枚举只枚举每一段的起点,每次枚举完将其删除 for (int x : nums) { if (hashSet.contains(x) && !hashSet.contains(x - 1)) { int y = x; hashSet.remove(x); while (hashSet.contains(y + 1)) { hashSet.remove(++ y); } if (y - x + 1 > res) res = y - x + 1; } } return res; } }
4. 移动零
- 原题链接:移动零
- 知识点:双指针
- 思路
- 遍历整个数组,将非 0 元素存储数组前半段,
- 将为 0 的元素存入非 0 元素的后面,即整个元素的后半段;
- 题解
class Solution { public void moveZeroes(int[] nums) { int k = 0; for (int num : nums) { // 遍历整个数组 // 筛选非0元素 if (num != 0) nums[k ++] = num; } // 将0整体存入非0元素的末尾 while (k < nums.length) nums[k ++] = 0; } }
5. 盛最多水的容器
-
原题链接:盛最多水的容器
-
知识点:双指针
-
题解
class Solution { public int maxArea(int[] height) { int res = 0; for (int i = 0, j = height.length - 1; i < j; ) { res = max(res, min(height[i], height[j]) * (j - i)); if (height[i] > height[j]) j --; else i ++; } return res; } public int min (int a, int b) { if (a <= b) return a; else return b; } public int max (int a, int b) { if (a >= b) return a; else return b; } }
6. 三数之和
- 原题链接:三数之和
- 知识点:双指针
- 思路
- 定义三个指针(
i
、j
、k
)指向三个不同的数; - 固定指针
i
,指针j
和指针k
则可以使用双指针算法;
- 定义三个指针(
- 时间复杂度:O(n2)
- 题解
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res = new ArrayList<>(); // 结果数组 Arrays.sort(nums); // 对原数组进行升序排列 for (int i = 0; i < nums.length; i ++) { // 去重:如果下一个与上一个数相同跳过,因为上一个数已经将所有结果枚举完了 if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1, k = nums.length - 1; j < k; j ++) { // 同理去重 if (j > i + 1 && nums[j] == nums[j - 1]) continue; // 试探下一个数,如果满足条件则一直向指针 j 靠拢 while (j < k - 1 && nums[k - 1] + nums[j] + nums[i] >= 0) k--; // 停下后判断是否三数之和为0,如果是存储结果数组 if (nums[k] + nums[j] + nums[i] == 0) res.add(Arrays.asList(nums[i],nums[j],nums[k])); } } return res; } }
7. 无重复字符的最长子串
- 原题链接: 无重复字符的最长子串
- 知识点:滑动窗口/双指针
- 时间复杂度:O(n)
- 题解
// Java 代码 class Solution { public int lengthOfLongestSubstring(String s) { Map<Character, Integer> heap = new HashMap<>(); int res = 0; // 枚举尾节点 for (int i = 0, j = 0; i < s.length(); i++) { char currentChar = s.charAt(i); heap.put(currentChar, heap.getOrDefault(currentChar, 0) + 1); while (heap.get(currentChar) > 1) { char leftChar = s.charAt(j++); heap.put(leftChar, heap.get(leftChar) - 1); } res = Math.max(res, i - j + 1); } return res; } } // C++ 代码 class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map<char, int> heap; int res = 0; for (int i = 0, j = 0; i < s.size(); i ++) { heap[s[i]] ++; while (heap[s[i]] > 1) heap[s[j ++]] --; res = max(res, i - j + 1); } return res; } };
8. 和为 K 的子数组
- 原题链接: 和为 K 的子数组
- 知识点:双指针/哈希表
- 时间复杂度:O(n)
- 题解
class Solution { public: int subarraySum(vector<int>& nums, int k) { int n = nums.size(); vector<int> s(n + 1); // 前缀和数组 for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + nums[i - 1]; unordered_map<int, int> hash; hash[0] = 1; // s[0] = 0 且 s[0] 出现1次 int res = 0; for (int i = 1; i <= n; i ++) { res += hash[s[i] - k]; hash[s[i]] ++; } return res; } };
9. 滑动窗口最大值
- 原题链接: 滑动窗口最大值
- 知识点:单调队列(双端队列)
- 时间复杂度:O(n)
- 题解
class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> q; vector<int> res; for (int i = 0; i < nums.size(); i++) { if (q.size() && i - k + 1 > q.front()) q.pop_front(); while (q.size() && nums[i] >= nums[q.back()]) q.pop_back(); q.push_back(i); if (i >= k - 1) res.push_back(nums[q.front()]); } return res; } };
10. 颜色分类
- 原题链接: 颜色分类
- 知识点:三指针
- 时间复杂度:O(n)
- 题解
class Solution { public void sortColors(int[] nums) { int left = 0; int right = nums.length - 1; int index = 0; while (index <= right) { int cur = nums[index]; if (cur == 0) { swap(nums, index, left); index ++; left ++; } else if (cur == 1) { index ++; } else if (cur == 2) { swap(nums, index, right); right --; } } } private void swap(int[]nums, int i, int j) { int t = nums[i]; nums[i] = nums[j]; nums[j] = t; } }
11. 删除有序数组中的重复项
- 题解
class Solution {
public int removeDuplicates(int[] nums) {
int k = 0;
for (int i = 0; i < nums.length; i ++) {
if (i == 0 || nums[i] != nums[i - 1])
nums[k ++] = nums[i];
}
return k;
}
}
12. 螺旋矩阵
// 偏移量
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> res;
int n = matrix.size();
if (!n) return res;
int m = matrix[0].size();
// 方向
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
vector<vector<bool>> st(n, vector<bool>(m));
for (int i = 0, x = 0, y = 0, d = 0; i < n * m; i ++) {
res.push_back(matrix[x][y]);
st[x][y] = true;
int a = x + dx[d],b = y + dy[d];
if (a < 0 || a >= n || b < 0 || b >= m || st[a][b]) {
d = (d + 1) % 4;
a = x + dx[d];
b = y + dy[d];
}
x = a;
y = b;
}
return res;
}
};