哈希表汇总(未完待续)

哈希表

数组

1. 两数之和

从数组中找到和为target的两个数,并将它们的下标返回
https://leetcode.cn/problems/two-sum/

思路一:快排+双指针
需要先复制一份数组,因为排完序下标就乱了,跟数据并不对应。排序后,用两指针指向头ij,如果头尾元素的和大于target,则j--,小于则i++,等于则跳出循环,从复制的数组中找到原来的数据所在的位置。

链接:代码

思路二:哈希表
将元素的值和下标存到哈希表中,每次遍历下一个元素时,就使用target减去当前元素,然后判断哈希表中是否存在,存在则找到,反之继续。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        //2.哈希表
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        int l = nums.length;
        for(int i = 0; i < l; i++){
            if(map.containsKey(target-nums[i])){
               return new int[]{map.get(target-nums[i]),i};
            } else {
               map.put(nums[i],i);
            }
        }
        return null;
    }
}
217. 存在重复元素

判断一个数组中是否存在重复元素
https://leetcode.cn/problems/contains-duplicate/

思路:HashSet不可重复集合
利用add方法,添加失败返回false,添加成功返回true。当然,也可以用contains进行判断。

class Solution {
    public boolean containsDuplicate(int[] nums) {
        HashSet<Integer> set = new HashSet<Integer>();
        for(int num : nums){
            if(!set.add(num)) return true;
        }
        return false;
    }
}
*594. 最长和谐子序列

https://leetcode.cn/problems/longest-harmonious-subsequence/

class Solution {
    public int findLHS(int[] nums) {
        //HashMap
        HashMap<Integer,Integer> map = new HashMap<>();
        //HashMap存储
        for(int n : nums){
        	map.put(n,map.getOrDefault(n,0)+1);
        }
        //求最大值
        int max=0;//保存最大值
        for(Map.Entry<Integer,Integer> e : map.entrySet()){
            int key = e.getKey();
            int value = e.getValue();
            Integer ir = map.get(key+1);
            if(ir!=null){
            	value+=ir;
            	if(max < value){
            		max = value;
            	}
            }
        }
        return max;
    }
}

法二:滑动窗口

28. 最长连续序列

https://leetcode.cn/problems/longest-consecutive-sequence/

思路一:快排

class Solution {
    public int longestConsecutive(int[] nums) {
        //1.快排
        Arrays.sort(nums);
        int l = nums.length;
        int dp = 1; 
        int max = 0;
        for (int i = 1 ; i < l ; i++) {
            int tmp = nums[i] - nums[i-1];
            if(tmp == 1) {
                dp++;
            } else if(tmp > 1) {
                dp = 1;
            }
            if(dp>max) max = dp;
        }
        return max;
    }
}

解释:这里的dp有点动态规划的味道,所以才如此命名,只不过一般想得出来就不需要去分析是不是动态规划了。dp[i]`表示以i结尾的连续的最长序列,注意相同元素,上面的处理是直接跳过,因为不需要处理
如果是求dp[i]的话,结果应该为dp[i-1]
综上,可得动态方程
nums[i]-nums[i-1]==0 dp[i]=dp[i-1]
nums[i]-nums[i-1]==1 dp[i]=dp[i-1]+1
nums[i]-nums[i-1] > 1 dp[i]=1

169. 多数元素

求数组中出现次数超过一半的数字

https://leetcode.cn/problems/majority-element/

思路一:快排

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length/2];
    }
}

思路二:哈希

class Solution {
    public int majorityElement(int[] nums) {
        int l = nums.length / 2;
        HashMap<Integer,Integer> map = new HashMap<>();
        for(int num: nums) {
            int count = map.getOrDefault(num, 0) + 1;
            map.put(num, count);
            if(count > l) {
                return num;
            }
        }        
        return 0; 
    }
}

思路三:投票算法,实现O(N) + O(1)

力扣评论:从第一个数开始count=1,遇到相同的就加1,遇到不同的就减1,减到0就重新换个数开始计数,总能找到最多的那个
参考文章

class Solution {
    public int majorityElement(int[] nums) {
        int res = nums[0];
        int count = 1;
        int l = nums.length;
        for(int i=1; i < l; i++) {
            if(res == nums[i]) {
                count++;
            } else {
                count--;
            }
            if(count == 0) {
                res = nums[i];
                count = 1;
            }
        }
        return res;
    }
}
137. 只出现一次的数字 II

https://leetcode.cn/problems/single-number-ii/

快排

class Solution {
    public int singleNumber(int[] nums) {
        //快排
        Arrays.sort(nums);
        int l = nums.length - 1;
        int ans = nums[l];
        int i = 0;
        int j = i + 1;
        while(true) {
            while(j <= l && nums[i]==nums[j]) j++;
            if(j == i + 1) {
                ans = nums[i];
                break;
            }
            i=j;
        }
        return ans;
    }
}

哈希表

class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer,Integer> map = new HashMap<>();
        for(int num: nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        for(Map.Entry<Integer,Integer> entry : map.entrySet()) {
            if(entry.getValue() == 1) {
                return entry.getKey();
            }
        }
        return 0;
    }
}
41. 缺失的第一个正数

https://leetcode.cn/problems/first-missing-positive/

class Solution {
    public int firstMissingPositive(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        int max = 0;
        for(int num : nums) {
            if(num > 0){
                set.add(num);
                if(num > max) {
                    max = num;
                }
            }
        }
        for(int i = 1; i < max; i++) {
            if(!set.contains(i)) {
                return i;
            }
        }
        return max + 1;
    }
}

优化:高级做法未实现


442. 数组中重复的数据

力扣传送门

思路一:快排
数组中的元素要么出现一次,要么出现两次,所以可先使用Arrays.sort,在对数组进行遍历,即比较左右两元素是否相等,相等则说明出现了两次,添加到结果集。

链接:代码略

思路二:哈希表
以元素作为下标,统计元素的个数,个数为2则添加到结果集。这里不需要HashMap,因为数组元素的大小访问在1-n,只需要一个长度为n+1的数组。

class Solution {
    public List<Integer> findDuplicates(int[] nums) {
        List<Integer> res = new ArrayList<>();
        int[] hash = new int[nums.length+1];
        for(int num: nums) {
            hash[num]++;
            if(hash[num] == 2) {
                res.add(num);
            }
        }
        return res;
    }
}
1394. 找出数组中的幸运数

力扣传送门

思路:哈希表
第一次遍历,先统计各个元素出现的次数。
第二次遍历,比较元素和对应的计数是否相同,对于多个幸运数需要选取最大的,可以采用求最大值方法。

class Solution {
    public int findLucky(int[] arr) {
        int len=arr.length;
        int[] hash = new int[501];
        //哈希计数
        for(int i=0;i<len;i++){
            hash[arr[i]]++;
        }
        //查找幸运数
        int max = -1;
        for(int i=0; i<len; i++) {
            if(hash[arr[i]] == arr[i] && max < arr[i]) {
                max = arr[i];
            }
        }
        return max;
    }
}
1122. 数组的相对排序

力扣传送门

思路:哈希表
对数组arr1的元素进行哈希统计,然后遍历arr2构造结果数组,由于arr2的元素均在arr1中,所以通过哈希表即可获得arr2中的元素在arr1中出现的次数,循环保存到结果数组即可。

class Solution {
    public int[] relativeSortArray(int[] arr1, int[] arr2) {
        
        int[] hash = new int[1001];
        //哈希计数
        for (int i = 0; i < arr1.length; i++) hash[arr1[i]]++;

        //map根据arr2的元素赋值给arr1(此时arr1已经没用了,所以可以作为结果数组使用)
        int idx = 0;
        for (int i = 0; i < arr2.length; i++) {
            while (hash[arr2[i]] > 0) {//循环保存
                hash[arr2[i]]--;
                arr1[idx++] = arr2[i];
            }            
        }
        //遍历map中剩余的元素
        for (int i = 0; i < hash.length; i++){
            while (hash[i] > 0) {
                hash[i]--;
                arr1[idx++] = i;
            }            
        }    
        return arr1;
    }
}
1338. 数组大小减半

力扣传送门

思路:哈希表+快排
数组减半的同时,消耗的元素要最少,说明得按元素出现的次数从大到小删除(先删次数多的)。次数统计由哈希表解决,从大到小则需要排序解决。

class Solution {
    public int minSetSize(int[] arr) {
        int len=arr.length;
        int[] v=new int[10001];//题目大小有限定,可以用数组
        int max=0;
        for(int i=0;i<len;i++){
            v[arr[i]]++;
            if(max<arr[i]) max=arr[i];
        }

        Arrays.sort(v,0,max+1);

        int count=0;
        len/=2;
        for(int i=max;i>0;i--){
            if(v[i]>=len) {
                count++;
                break;
            }else{
                len-=v[i];
                count++;
            }
        }
        return count;
    }
}
1282. 用户分组

力扣传送门

思路:双重for循环+访问标记
为每个元素构造一个组,然后与数组里面的元素进行比较,在组的大小内,添加相同的元素,并标记访问过,防止重复访问。

class Solution {
    public List<List<Integer>> groupThePeople(int[] groupSizes) {
        
        List<List<Integer>> res=new ArrayList<List<Integer>>();  //结果
        int len=groupSizes.length;
        for(int i=0;i<len;i++){
        
            List<Integer> group = new ArrayList<Integer>(); //组
            group.add(i); 	//组的第一个成员
            if(groupSizes[i]==0) continue;  //跳过加过组的
            int size=groupSizes[i]; 	//组的大小
            
            for(int j=i+1;j<len;j++){               
                if(groupSizes[i]==groupSizes[j]){
                    if(size<2) break;
                    groupSizes[j]=0;  //标记加入组的
                    group.add(j);
                    size--;
                }
            }
            res.add(group);
        }
        return res;
    }
}

思路二:哈希表
以元素作为key,组作为value,由于相同元素可能有多个组,例如333333有两个组,所以需要判断当value数组的长度 == key时,保存结果,并移除该key。

class Solution {
    public List<List<Integer>> groupThePeople(int[] groupSizes) {
        List<List<Integer>> res = new ArrayList<>(); 
        Map<Integer, List<Integer>> map = new HashMap<>();  //哈希表
        int len = groupSizes.length;

        for(int i=0; i<len; i++) {
            
            List<Integer> group = map.get(groupSizes[i]);             
            if(group == null) {
                //集合大小明确,new时指定size,可避免扩容(提高效率)
                map.put(groupSizes[i], new ArrayList<>(groupSizes[i]));
                group = map.get(groupSizes[i]);
            }
            group.add(i);             
            if(group.size() == groupSizes[i]) {
                res.add(group);
                map.remove(groupSizes[i]);
            }
        }
        return res;
    }
}
*560. 和为 K 的子数组

https://leetcode.cn/problems/subarray-sum-equals-k

暴力
遍历每个串
0-0、0-1……0-n
1-1、1-2……1-n
……

class Solution {
    public int subarraySum(int[] nums, int k) {
        int l = nums.length;
        int count = 0;
        for(int i = 0; i < l; i++) {
            int sum = 0;
            for(int j = i; j < l; j++) {
                sum += nums[j];
                // if(sum > k) break;不可以跳出去,因为后面可能更大
                if(sum == k) count++;
            }
        }
        return count;
    }
 
}

数组预处理+哈希
转换一下,只需要计算0-0、0-1……0-n各个子数组之和
因为每个子数组都可以由上面的数组推出来
例如1-2等于0-n减去0-0。从而只需判断用两个子数组之和的差是否等于k即可,进一步转换就是用子数组减去k
但是这样不能表示0-0、0-1……0-n这些子数组,所以一开始需要map.put(0,1)
也就是表示自己减去自己是存在的

class Solution {
    public int subarraySum(int[] nums, int k) {
        int l = nums.length;        
        int[] dp = new int[l];
        dp[0] = nums[0];        
        for(int i = 1; i < l; i++) {
            dp[i] = dp[i-1] + nums[i];            
        }
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        int count = 0;
        for(int i = 0; i < l; i++) {
            int tmp = dp[i] - k;
            if(map.containsKey(tmp)) {
                count += map.get(tmp);
            }            
            map.put(dp[i], map.getOrDefault(dp[i],0) + 1);
        }
        return count;
    }
 
}

字符串

更多

链接

面试题 01.02. 判定是否互为字符重排

力扣传送门

思路:哈希表
对字符串s1采用加法的方式统计字符的出现次数,对s2则采用减法的方式统计。例如a在s1中出现3次,在s2中也出现3次,那么最终哈希表中a的计数为0,如果中间存在计数小于0的字符,则说明结果为false
由于该题的测试用例仅仅只包含26字母,所以也可用数组作为哈希表

class Solution {
    public boolean CheckPermutation(String s1, String s2) {
        if (s1.length() != s2.length()) {
            return false;
        }
        Map<Character, Integer> map = new HashMap<>();
        //加法统计
        for (int i = 0; i < s1.length(); i++) {
            map.put(s1.charAt(i), map.getOrDefault(s1.charAt(i), 0) + 1);
        }
        //减法统计
        for (int i = 0; i < s2.length(); i++) {
            map.put(s2.charAt(i), map.getOrDefault(s2.charAt(i), 0) - 1);
            if (map.getOrDefault(s2.charAt(i), -1) < 0) {
                return false;
            }
        }
        return true;
    }
}

提示:getOrDefault跟get一样,只是当map中不存在时,它能返回自定义默认值,而不是null

438. 找到字符串中所有字母异位词

上一道的题的扩展,代码相对难写,但思路不难

class Solution {
    public List<Integer> findAnagrams(String s, String p) {        

        int pl = p.length() - 1;
        //需要遍历的起始点
        int sl = s.length() - pl; 
        //特殊情况。。。sl比pl长
        if(sl <= 0)  return new ArrayList<Integer>();

        int[] hashp = new int[26];
        for(char ch : p.toCharArray()) {
            hashp[ch - 'a']++;
        }

        List<Integer> res = new ArrayList<Integer>(sl);
        int[] hashs = new int[26];        

        //初始化第一个字串
        for(int i = 0; i < pl; i++) {
            hashs[s.charAt(i) - 'a']++;
        }        
        for(int i = 0; i < sl; i++) {            
            //新增加一个元素,i + pl代表每个串的最后一个元素
            hashs[s.charAt(i + pl) - 'a']++;
            //判断是否是异位字符串
            if(judge(hashs, hashp)) {
                res.add(i);                
            }
            //减掉移除的元素
            hashs[s.charAt(i) - 'a']--;  
        }
        return res;
    }
    private boolean judge(int[] hashs, int[] hashp) {
        int i = 26;
        while(i-- > 0) {
            if(hashs[i] != hashp[i]) {
                return false;
            }
        }
        return true;
    }
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值