算法-哈希

一、基础理论

哈希表是由 HashTable 实现的,相关的实现有HashMapHashSet。是根据关键码值(Key value)而直接进行访问的数据结构。

二、常用方法

2.1. HashMap常用方法

2.1.1 创建

// 不指定容量,默认初始容量为16
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = Maps.newHashMap();
        
// 构造一个初始容量为8的HashMap
HashMap<String, String> map = new HashMap<>(8);

2.1.2 遍历

EntrySet遍历

// EntrySet for循环遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

// EntrySet Iterator迭代器遍历
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String, String> entry = iterator.next();
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

keySet遍历

// keySet for循环遍历
for (String key : map.keySet()) {
    System.out.println(key + ":" + map.get(key));
}

// keySet Iterator迭代器遍历
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    String key = iterator.next();
    System.out.println(key + ":" + map.get(key));
}

2.1.3 其他常用方法

put(); //将键/值对添加到 hashMap 中
putAll();  //将所有键/值对添加到 hashMap 中
remove(); //删除 hashMap 中指定键 key 的映射关系
containsKey(); 	//检查 hashMap 中是否存在指定的 key 对应的映射关系。
containsValue(); //检查 hashMap 中是否存在指定的 value 对应的映射关系。
values(); //获取 hashMap 中所有 value 值的集合。
size(); //计算 hashMap 中键/值对的数量
get(); //获取指定 key 对应对 value
getOrDefault(); // 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
isEmpty(); //判断 hashMap 是否为空
replace(); //替换 hashMap 中是指定的 key 对应的 value。
replaceAll(); //将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。

2.2. HashSet常用方法

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。

2.2.1 创建

// 不指定容量,默认初始容量为16
HashSet<String> set1 = new HashSet<>();
HashSet<String> set2 = Sets.newHashSet();

// 构造一个初始容量为8的HashSet
HashSet<String> set = new HashSet<>(8);

2.2.2 遍历

for 循环遍历

//for 循环遍历
for (String str : set) {
    System.out.println(str);
}

Iterator 迭代器遍历

//Iterator 迭代器遍历
Iterator<String> it = set.iterator();
while(it.hasNext()) {
    String str = it.next();
    System.out.println(str);
}

2.2.3 其他常用方法

add(); //添加元素,同一个元素被添加了两次,它在集合中也只会出现一次,因为集合中的每个元素都必须是唯一的。
contains(); // 判断元素是否存在于集合当中
remove(); // 删除集合中的元素,删除成功返回 true,否则为 false
clear(); //删除集合中所有元素
size(); //计算 HashSet 中的元素数量 
for (String i : sites) //可以使用 for-each 来迭代 HashSet 中的元素。
isEmpty(); // 判断集合是否为空
toArray(); //将内容转到数组中

2.3 ArrayList常用方法

ArrayList类是一个动态数组类型,可以随时从链表中添加或删除一个元素。ArrayList实现了List接口并且可以动态改变大小的。

2.3.1 创建

// 默认构造函数创建
ArrayList<String> list1 = new ArrayList<>();
ArrayList<String> list2 = Lists.newArrayList();

// 使用包含初始元素的构造函数创建
ArrayList<String> list = new ArrayList<>(Arrays.asList("s1", "s2", "s3"));

2.3.2 遍历

for 循环遍历

// for 循环遍历
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

增强for循环

//增强for循环
for(String s: list) {
    System.out.println(s);
}

iterator 迭代器遍历

// iterator 迭代器遍历
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}

2.3.3 其他常用方法

add("a"); //将a加入到list,依次按照顺序向ArrayList中添加数据
add(N, "E"); //在第N个数据后面添加一个数据
addAll(); //将一个ArrayList中的所有数据添加到另外一个ArraList中
remove(2); //按照位置删除单个数据
remove("d"); //按照内容删除单个数据
removeAll(); //按照集合同时删除多个数据
clear(); //清空list
get(int index); //查询指定位置的元素
set(int index, E element); //修改元素
isEmpty(); //判断是否为空
size();  //获取该数组的长度(包含元素的个数)
indexOf(); //第一次出现位置
lastIndexOf(); //最后一次出现位置
toArray(); //转化成数组

三、leetcode例题详解

3.1 两数之和

leetcode题目链接:1. 两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

解法一:暴力解法

双重遍历寻找两数之和为目标值的下标

// 1. 暴力解法
public int[] twoSum(int[] nums, int target) {
    int[] result = new int[2];
    for(int i = 0; i < nums.length; i++){
        for(int j = i+1; j < nums.length; j++){
            if(nums[i] + nums[j] == target){
                result[0] = i;
                result[1] = j;
                return result;
            }
        }
    }

    return null;
}

解法二: 哈希解法

遍历数组,通过在hashMap中寻找当前值距离目标和的差值来判断是否符合结果。同时将遍历过的数组元素存入hashMap,供后续的遍历查找。

// 2. 哈希解法
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for(int i = 0; i < nums.length; i++){
        if(map.containsKey(target-nums[i])){
            return new int[]{i, map.get(target-nums[i])};
        }
        map.put(nums[i], i);
    }

    return null;
}

3.2 字母异位词分组

leetcode题目链接:49. 字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

示例 1:

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

示例 2:

输入: strs = [""]
输出: [[""]]

示例 3:

输入: strs = ["a"]
输出: [["a"]]

解法:哈希解法

1. 将数组中的元素按照字符排好序,异位词对应的排好序的字符串是相同的

2. 将排好序的字符串作为hashMap的key,value为多个异位词的list;

3. hashMap中已经是规整好的字母异位词,输出hashMap的values即可;

public List<List<String>> groupAnagrams(String[] strs) {
    if(strs == null || strs.length <= 0){
        return new ArrayList<>();
    }

    //String用来记录集合中单个元素的字母排序后的结果
    HashMap<String, List<String>> map = new HashMap();
    for(String str : strs){
        //字符串转为字符数组并排序
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        String key = String.valueOf(chars);

        //判断map中有没有这组集合
        if(!map.containsKey(key)){
            List<String> list = new ArrayList();
            map.put(key, list);
        }
        map.get(key).add(str);
    }

    return new ArrayList<>(map.values());
}

3.3 最长连续序列

leetcode题目链接:128. 最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。

示例 2:

输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9

解法一:

// 解法一:先对数组进行排序,寻找最长的连续序列
// 缺点:当数组中有重复的元素时,处理方式不太雅观
public int longestConsecutive(int[] nums) {
    if(nums == null){
        return 0;
    }
    if(nums.length <= 1){
        return nums.length;
    }

    Arrays.sort(nums);

    int startIndex = 0;
    int endIndex = 0;
    int res = 1;
    for(int i = 0; i < nums.length - 1; i++){
        //处理特殊情况,数组中有元素是重复的,导致连续序列中断
        if(nums[i] == nums[i+1]){
            startIndex = startIndex +1;
            continue;
        }

        //寻找连续序列
        if(nums[i] + 1 == nums[i+1]){
            endIndex = i + 1;
            continue;
        }

        //数组遍历未完成,连续序列中断,计算当前最大长度
        int currRes = i - startIndex + 1;
        res = Math.max(res, currRes);

        //从中断位置,继续出发寻找连续序列
        startIndex = i+1;
    }

    //数组遍历完成,连续序列中断,计算当前最大长度
    int currRes = nums.length - startIndex;
    res = Math.max(res, currRes);

    return res;
}

解法二:

 // 解法二:先对数组进行排序,寻找最长的连续序列
 // 相对解法一的优化:不再使用数组的下标计算连续序列的长度,而是使用计数的方式
 //                这样可以避免当数组中有重复的元素时,处理方式不雅观的问题
 public int longestConsecutive(int[] nums) {
     if(nums == null){
         return 0;
     }
     if(nums.length <= 1){
         return nums.length;
     }

     Arrays.sort(nums);

     int result = 1;
     int count = 1;
     for(int i = 0; i < nums.length - 1; i++){
         //处理特殊情况,数组中有元素是重复的,导致连续序列中断
         if(nums[i] == nums[i+1]){
             continue;
         }

         //寻找连续序列
         if(nums[i] + 1 == nums[i+1]){
             count++;
             continue;
         }


         result = Math.max(count, result);
         count = 1;
     }
     result = Math.max(count, result);

     return result;
 }

解法三:哈希解法

// 解法三:不需要先对数组进行排序,直接用hashset存储寻找前后相邻的值是否存在,同时也处理了数组中值重复的问题
public int longestConsecutive(int[] nums) {
    if(nums == null){
        return 0;
    }
    if(nums.length <= 1){
        return nums.length;
    }

    // 先把数组中的元素存储进hashset
    Set<Integer> set = new HashSet<>();
    for(int num : nums){
        set.add(num);
    }

    int count = 1;
    int max = 1;
    // 遍历数组,寻找有序数组的最左边界(set中不存在当前值-1,就说明当前值可以是最左边界)
    for(int num : nums){
        if(set.contains(num-1)){
            continue;
        }

        while(set.contains(num + 1)){
            count++;
            num++;
        }

        max = Math.max(count, max);
        count = 1;
    }

    return max;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值