「LeetCode Hot 100 题」详解

前言

前言: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. 三数之和

  • 原题链接:三数之和
  • 知识点:双指针
  • 思路
    • 定义三个指针(ijk)指向三个不同的数;
    • 固定指针 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个写代码的修车工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值