跳转至

Leetcode Hot100 哈希

1. 两数之和 - 力扣(LeetCode)

class Solution {
//使用哈希表
public int[] twoSum(int[] nums, int target) {
    int[] res = new int[2];
    // 如果数组为空则直接退出
    if(nums == null || nums.length == 0){
        return res;
    }
    Map<Integer, Integer> map = new HashMap<>();
    for(int i = 0; i < nums.length; i++){
        int temp = target - nums[i];   // 遍历当前元素,并在map中寻找是否有匹配的key
        if(map.containsKey(temp)){
            res[1] = i;
            res[0] = map.get(temp);// 这里就是满足了匹配下,从map中获取到对应的下标,于是可以被res返回
            break;
        }
        map.put(nums[i], i);    // 如果没找到匹配对,就把访问过的元素和下标加入到map中
        // 因为后面查询到的元素会回头向前匹配,这样就避免了使用两次for循环的暴力解法
    }
    // 之所以要用(数组值,数组下标)这样的结构以及map来实现,是因为最终要返回的不是所求得的值,而是值在数组中所对应的下标
    return res;
}
}

**复杂度分析**
- **时间复杂度O(n)**其中 n 是数组的长度遍历数组一次哈希表的查找和插入操作都是 O(1) 时间复杂度
- **空间复杂度O(n)**其中 n 是数组的长度哈希表最多存储 n 个键值对
class Solution {
//使用哈希表方法2
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> indexMap = new HashMap<>();
        // 本质上也是减少了“从后往前”的二次查询的复杂度
        // 因为暴力解法之所以麻烦的原因是因为,代码不清楚之前所访问过的元素是否满足和当前元素搭配的情况
        // 所以才需要每个元素都重复访问一次全组
        // 而HashMap的优化就免去了冗余的重复访问,直接将访问过的元素存入map,等到后面查询的时候直接查询map即可
        for(int i = 0; i < nums.length; i++){
            int balance = target - nums[i];  // 记录当前的目标值的余数
            if(indexMap.containsKey(balance)){  // 查找当前的map中是否有满足要求的值
                return new int []{i, indexMap.get(balance)}; //  如果有,返回目标值
            } else{
                indexMap.put(nums[i], i); //  如果没有,把访问过的元素和下标加入map中
            }
        }
        return null;
    }
}

**复杂度分析**
- **时间复杂度O(n)**其中 n 是数组的长度遍历数组一次哈希表的查找和插入操作都是 O(1) 时间复杂度
- **空间复杂度O(n)**其中 n 是数组的长度哈希表最多存储 n 个键值对

49. 字母异位词分组 - 力扣(LeetCode)

class Solution {
    // 设计思路,通过hashmap比较不同单词是否以一个顺序的同一单词变换而来
    public List<List<String>> groupAnagrams(String[] strs) {
        HashMap<String,ArrayList<String>> map = new HashMap<>();// 重点1:懂得使用java的hashmap来存储数据
        // 重点2:hashmap的类型应该为以string为键,返回的数组为值
        for(String s: strs){
            char[] chars = s.toCharArray();// 重点3:toCharArray方法
            Arrays.sort(chars);
            String key = Arrays.toString(chars);
            if(!map.containsKey(key)){
                map.put(key,new ArrayList<String>());
            }
            map.get(key).add(s);
        }
        return new ArrayList<>(map.values());// 重点4:将map的values转为数组的方法
    }
}

**复杂度分析**
- **时间复杂度O(nk log k)**其中 n 是字符串数组的长度k 是字符串的最大长度每个字符串需要排序排序的时间复杂度是 O(k log k)总共有 n 个字符串
- **空间复杂度O(nk)**其中 n 是字符串数组的长度k 是字符串的最大长度需要存储所有字符串到哈希表中

提升

// 遍历数组: 拿出每一个字符串
// 寻找"代表元"(制造Key): 将字符串拆成字符, 按字母顺序排序, 然后再拼回字符串. 比如 "tea" 和 "eat" 排序后都会变成 "aet".
// 哈希分组(放入字典): 把这个排序后的字符串 "aet" 当作 Key,把原始字符串存入对应的 List (Value) 中
// 提取结果: 最后把哈希表里所有的 List 提取出来, 组合成一个大的 List 返回.

import java.util.*;
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        return new AbstractList<List<String>>() {
            List<List<String>> list;

            private void init() {
                if (list != null) return;

                Map<String, List<String>> m = new HashMap<>();

                for (String s : strs) {
                    String sorted = sort(s);

                    List<String> group = m.get(sorted);
                    if (group != null) {
                        group.add(s);
                    } else {
                        List<String> newGroup = new ArrayList<>();
                        newGroup.add(s);
                        m.put(sorted, newGroup);
                    }
                }

                list = new ArrayList<>(m.values());
            }

            private String sort(String s) {
                char[] S = s.toCharArray();
                Arrays.sort(S);
                return new String(S);
            }

            @Override
            public int size() {
                init();
                return list.size();
            }

            @Override
            public List<String> get(int i) {
                init();
                return list.get(i);
            }
        };
    }
}

**复杂度分析**
- **时间复杂度O(nk log k)**其中 n 是字符串数组的长度k 是字符串的最大长度每个字符串需要排序排序的时间复杂度是 O(k log k)总共有 n 个字符串
- **空间复杂度O(nk)**其中 n 是字符串数组的长度k 是字符串的最大长度需要存储所有字符串到哈希表中
- ****该方法使用了懒加载的方式只有在首次访问时才会计算结果节省了不必要的计算时间

128. 最长连续序列 - 力扣(LeetCode)

class Solution {
    public int longestConsecutive(int[] nums) {
        // 先对数据按从小到大排序,再顺序比较数组内的每个数之间是否连续
        // 如果连续,就记录下当前的连续序列的长度
        // 如果不连续,就更新最大连续序列的长度
        // 最后返回最大连续序列的长度

        if (nums == null || nums.length == 0) {
            return 0;
        }

        Arrays.sort(nums);

        int maxLength = 1;
        int currentLength = 1;

        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i - 1]) {
                continue;
            }
            if (nums[i] == nums[i - 1] + 1) {
                currentLength++;
            } else {
                maxLength = Math.max(maxLength, currentLength);
                currentLength = 1;
            }
        }

        maxLength = Math.max(maxLength, currentLength);

        return maxLength;
    }
}

**复杂度分析**
- **时间复杂度O(n log n)**其中 n 是数组的长度排序的时间复杂度是 O(n log n)遍历数组的时间复杂度是 O(n)因此总时间复杂度是 O(n log n)
- **空间复杂度O(1)**只使用了常数级别的额外空间

哈希表解题方法**

class Solution {
    public int longestConsecutive(int[] nums) {
        // 创建哈希集合,用于存储数组中的所有元素,实现O(1)时间的查找
        Set<Integer> num_set = new HashSet<Integer>();
        for (int num : nums) {
            num_set.add(num);
        }

        // 记录最长连续序列的长度
        int longestStreak = 0;

        // 遍历哈希集合中的每个元素
        for (int num : num_set) {
            // 关键优化:只有当num-1不存在时,才以num为起点开始计算连续序列
            // 这样可以避免重复计算,确保每个连续序列只被计算一次
            if (!num_set.contains(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;

                // 从当前起点开始,向后查找连续的数字
                while (num_set.contains(currentNum + 1)) {
                    // 假设当前数字的下一个数字存在,就继续查找连续序列的下一个数字
                    currentNum += 1; // 当前数字增加1,继续查找连续序列
                    currentStreak += 1; // 当前连续序列的长度增加1
                }

                // 更新最长连续序列的长度
                longestStreak = Math.max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }
}

**复杂度分析**
- **时间复杂度O(n)**其中 n 是数组的长度每个元素最多被访问两次一次是添加到哈希集合中一次是在查找连续序列时
- **空间复杂度O(n)**其中 n 是数组的长度哈希集合需要存储所有元素

评论