【Leecode笔记】第十五周(下)(12.18-12.20)哈希表专题

这篇博客记录了LeetCode第十五周的哈希表专题解题笔记,包括只出现一次的数字、两句话中的不常见单词等九道题目。通过哈希表解决各种问题,如计算质数、找不同、验证外星语词典等,展示了哈希表在算法中的应用。
摘要由CSDN通过智能技术生成

【12.18】————————————————————————————————————————————

【第一题】只出现一次的数字

在这里插入图片描述

分析:不需要额外空间的方法,就往位运算上想。
在这里插入图片描述

//异或
//执行用时:1 ms, 在所有 Java 提交中击败了99.91% 的用户
//内存消耗:38.6 MB, 在所有 Java 提交中击败了80.24% 的用户
class Solution{
	public int SingleNumber(int[] nums){
		int single = 0;
		for(int num:nums){
			single^=num;
		}
		return single;
	}
}
//哈希表
//执行用时:14 ms, 在所有 Java 提交中击败了14.51% 的用户
//内存消耗:39 MB, 在所有 Java 提交中击败了44.07% 的用户
class Solution{
	public int singleNumber(int[] nums){
		Map<Integer,Integer> map = new HashMap<>();
		for(Integer i:nums){
			Integer count = map.get(i);
			count = count == null ? 1:++count;
			map.put(i,count);
		}
		for(Integer i:map.keySet()){
			Integer count = map.get(i);
			if(count == 1){return i;}
		}
		return -1;
	}
}
//哈希集
public static int SingleNumber(int[] nums){
	HashSet<int> set = new HashSet<int>();
	for(int i = 0;i < nums.length;i++){
	//假如之前添加过,那么第二次出现的时候将之前放入的移除,此时出现两次的元素全部被移除
		if(!set.Add(nums[i])){
			set.Remove(nums[i]);
		}
	}
	//只剩下唯一一个出现一次的元素。
	return set.First();
}
//先排序,比较前后元素是否相等
class Solution{
	public int singleNumber(int[] nums){
		Arrays.sort(nums);
		for(int i = 0;i < nums.length;i+=2){
			if(nums[i]!=nums[i+1]){
				return nums[i];
			}
		}
		return nums[nums.length-1];
	}
}

【第二题】两句话中的不常见单词

在这里插入图片描述

分析:哈希表。
为何我总能想到复杂度超高的方法?orz

//执行用时:7 ms, 在所有 Java 提交中击败了21.05% 的用户
//内存消耗:38.7 MB, 在所有 Java 提交中击败了20.23% 的用户
class Solution {
    public String[] uncommonFromSentences(String A, String B) {
        String con = A+" "+B;
        String[] arr1 = con.split(" ");
        StringBuffer buf = new StringBuffer();
        Map<String,Integer> map = new HashMap<>();
        for(String i:arr1){
            Integer count = map.get(i);
            count = count == null?1:++count;
            map.put(i,count);
        }
        for(String j:map.keySet()){
            Integer count = map.get(j);
            if(count == 1){
                buf.append(j).append(" ");
            }
        }
        if(buf.toString().length() >0){
            return buf.toString().trim().split(" ");
        }else{
            return new String[buf.length()];
        }    
    }
}
//执行用时:6 ms, 在所有 Java 提交中击败了26.96% 的用户
//内存消耗:38.7 MB, 在所有 Java 提交中击败了20.66% 的用户
class Solution{
	public String[] uncommonFromSentences(String A,String B){
		Map<String,Integer> count = new HashMap();
		String s = A+" "+B;
		for(String word:s.split(" "))
			count.put(word,count.getOrDefault(word,0)+1);
		
		List<Stirng> ans = new LinkedList();
		for(String word:count.keySet()){
			if(count.get(word) == 1)
				ans.add(word);
		}
		return ans.toArray(new String[ans.size()]);
	}
}

【第三题】拼写单词

在这里插入图片描述

分析:
第一种方法:26个桶。
第二种方法,用两个哈希表,分别记录chars的各个字母个数,以及words中各个字符串的各个字母个数,比较个数是否够,够的话就加在总长度上。

//执行用时:8 ms, 在所有 Java 提交中击败了69.38% 的用户
//内存消耗:39 MB, 在所有 Java 提交中击败了65.17% 的用户
class Solution {
    public int countCharacters(String[] words, String chars) {
        int[] arr = new int[26];
        int sum = 0;
        for(int i = 0;i < chars.length();i++){
            arr[chars.charAt(i)-'a']++;
        }
        
        for(int j = 0;j < words.length;j++){
            int[] arrCopy = new int[26];
            System.arraycopy(arr,0,arrCopy,0,arr.length);
            int k;
            for(k = 0;k < words[j].length();k++){
                if(arrCopy[words[j].charAt(k)-'a']==0){
                    break;
                }else{
                    arrCopy[words[j].charAt(k)-'a']--;
                }
            }
            if(k >=words[j].length()){
                //System.out.println(words[j]);
                sum+=words[j].length();
            }
        }
        return sum;
    }
}
class Solution {
    public int countCharacters(String[] words, String chars) {
        Map<Character, Integer> charsCnt = new HashMap<Character, Integer>();
        int length = chars.length();
        for (int i = 0; i < length; ++i) {
            char c = chars.charAt(i);
            charsCnt.put(c, charsCnt.getOrDefault(c, 0) + 1);
        }
        int ans = 0;
        for (String word : words) {
            Map<Character, Integer> wordCnt = new HashMap<Character, Integer>();
            int wordLength = word.length();
            for (int i = 0; i < wordLength; ++i) {
                char c = word.charAt(i);
                wordCnt.put(c, wordCnt.getOrDefault(c, 0) + 1);
            }
            boolean isAns = true;
            for (int i = 0; i < wordLength; ++i) {
                char c = word.charAt(i);
                if (charsCnt.getOrDefault(c, 0) < wordCnt.getOrDefault(c, 0)) {
                    isAns = false;
                    break;
                }
            }
            if (isAns) {
                ans += word.length();
            }
        }
        return ans;
    }
}

//执行用时:63 ms, 在所有 Java 提交中击败了19.41% 的用户
//内存消耗:39.1 MB, 在所有 Java 提交中击败了55.94% 的用户
   public int countCharacters(String[] words, String chars) {
    	int ans=0;
    	//统计chars中字母出现的次数
    	Map<Character,Integer> map=new HashMap<>();
    	for(char c:chars.toCharArray())
    		map.put(c, map.getOrDefault(c, 0)+1);
    	for(String word:words) {
    		boolean can=true;
        	//统计word中字母出现的次数
        	Map<Character,Integer> letterCount=new HashMap<>();
    		for(char c:word.toCharArray()) {
    			letterCount.put(c, letterCount.getOrDefault(c, 0)+1);
    		}
    		//判断可否拼写该单词
    		for(char c:word.toCharArray()) {
    			if(!map.containsKey(c)||map.get(c)<letterCount.get(c)) {
    				can=false;
    				break;
    			}
    		}
    		if(can)
    			ans+=word.length();
    	}
    	return ans;
    }

【第四题】快乐数

在这里插入图片描述

分析:这个题就变成了判断是否有环的问题。
判断是否有环——1.假如新进入的数字原本已经存在;2.快慢指针
在这里插入图片描述

//哈希集合判重
class Solution{
	private int getNext(int n){
		int totalSum = 0;
		//第一步,将数字各个位分解
		while(n > 0){
			int d = n%10;
			n = n/10;
			totalSum +=d*d;
		}
		return totalSum;
	}
	
	public boolean isHappy(int n){
		Set<Integer> seen = new HashSet<>();
		while(n!=1 && !seen.contains(n)){
			seen.add(n);
			n = getNext(n);
		}
		return n == 1;
	}
}
//快慢指针断环,快指针每次走两步,慢指针每次走一步,他俩无论从哪个位置开始,一定会在某个位置相遇
//执行用时:1 ms, 在所有 Java 提交中击败了99.81% 的用户
//内存消耗:35.4 MB, 在所有 Java 提交中击败了76.77% 的用户
class Solution{
	public int getNext(int n){
		int totalSum = 0;
		//第一步,将数字各个位分解
		while(n > 0){
			int d = n%10;
			n = n/10;
			totalSum +=d*d;
		}
		return totalSum;
	}
	public boolean isHappy(int n){
		int slowRunner = n;
		int fastRunner = getNext(n);
		while(fastRunner !=1 && slowRunner !=fastRunner){
			slowRunner = getNext(slowRunner);
			fastRunner = getNext(getNext(fastRunner));
		}
		return fastRunner == 1;
	}
}

12.19_________________________________________________________________________

【第五题】计数质数

在这里插入图片描述
分析:我一看,这么简单的题,我啪的一下就提交了,直接超时…
//埃氏筛:假如质数是x,那么把x*x,x(x+1)…标记成合数,也就是说将x的倍数排除。
线性筛:对埃氏筛进行优化,优化的目标是将每个合数只被标记一次,保证线性的时间复杂度。
在这里插入图片描述

//超时版本
class Solution {
    public int countPrimes(int n) {
        int count = 0;
        for(int i = 0;i < n;i++){
            if(isPrime(i)){
                System.out.println(i);
                count++;
            }
        }
        return count;
    }
    public boolean isPrime(int a){
        if(a == 0 || a == 1){return false;}
        for(int i = 2;i <= Math.sqrt(a);i++){
            if(a % i == 0){
                return false;
            }
        }
        return true;
    }
}
//埃氏筛:
//执行用时:27 ms, 在所有 Java 提交中击败了44.73% 的用户
//内存消耗:42.5 MB, 在所有 Java 提交中击败了33.17% 的用户
class Solution{
	public int[] countPrimes(int n){
		int[] isPrime = new int[n];
		Arrays.fill(isPrime,1);
		int ans = 0;
		for(int i = 2;i < n;i++){
			if(isPrime[i] == 1){
				ans+=1;
				if((long)i * i < n){
				//x*x  x(x+1) x(x+2)
					for(int j = i*i;j < n;j+=i){
						isPrime[j] = 0;
					}
				}
			}
		}
		return ans;
	}
}
class Solution{
	public int countPrimes(int n){
		List<Integer> primes = new ArrayList<Integer>();
		int[] isPrime = new int[n];
		Arrays.fill(isPrime,1);
		for(int i = 2;i < n;i++){
			if(isPrime[i] == 1){
				primes.add(i);
			}
			for(int j = 0;j < primes.size() && i * primes.get(j) < n;++j){
				isPrime[i*primes.get(j)] = 0;
				if(i % primes.get(j) == 0){
					break;
				}
			}
		}
		return primes.size();
	}
}

【第六题】两个数组的交集II

在这里插入图片描述

分析:题意就是求交集。
建立哈希表,记录每个key出现的次数。然后扫描另一个数组,假如他的元素在映射表可以找到,那么比较value,相等的话将其加入buffer,否则将一个元素加入。最后将buf转array。

//执行用时:16 ms, 在所有 Java 提交中击败了5.09% 的用户
//内存消耗:38.8 MB, 在所有 Java 提交中击败了55.34% 的用户
class Solution{
    public int[] intersect(int[] nums1,int[] nums2){
        //存放列表1中的元素
        List<Integer> list1 = new ArrayList<>();
        for(int num:nums1){
            list1.add(num);
        }
        //存放交集元素
        List<Integer> list2 = new ArrayList<>();
        for(int num:nums2){
            if(list1.contains(num)){
                list2.add(num);
                //匹配一次之后将数字从list1中删除
                list1.remove(Integer.valueOf(num));
            }
        }
        //将存放交集元素的list2转换成数组
        int[] res = new int[list2.size()];
        int i = 0;
        for(int num:list2){
            res[i++] = num;
        }
        return res;
    }
}
//利用映射实现
执行用时:3 ms, 在所有 Java 提交中击败了79.80% 的用户
内存消耗:38.6 MB, 在所有 Java 提交中击败了76.34% 的用户
class Solution{
    public int[] intersect(int[] nums1,int[] nums2){
        //将列表1中的元素和频次都放入映射中
        Map<Integer,Integer> map = new HashMap<>(nums1.length);
        int i = 0;
        for(int num:nums1){
            Integer count = map.get(num);
            if(count == null){
                map.put(num,1);
            }else{
                map.put(num,++count);
            }
            //map.put(nums1[i++],map.getOrDefault(i,1)+1); 
        }
        //list存放交集
        List<Integer> list = new ArrayList<>();
        for(int num:nums2){
            //获取映射中该数值出现的频次
            Integer count = map.get(num);
            if(count != null && count!=0){
                list.add(num);
                map.put(num,--count);
            } 
        }
        int[] res = new int[list.size()];
        for(int j= 0;j < list.size();j++){
            res[j] = list.get(j);
        }
        return res;
    }
}

【第七题】第k个缺失的正整数

在这里插入图片描述

分析:开辟一个空间为2500的桶

//执行用时:1 ms, 在所有 Java 提交中击败了61.42% 的用户
//内存消耗:38.3 MB, 在所有 Java 提交中击败了61.18% 的用户
class Solution {
    public int findKthPositive(int[] arr, int k) {
        int count = 0;
        int[] map = new int[2500];
        for(int i = 0;i < arr.length;i++){
            map[arr[i]] = arr[i];
        }
        int j = 1;
        while(j < map.length){
            if(count <k){
                if(map[j]!=0){
                    j++;
                }else{
                    j++;
                    ++count;
                    //System.out.println(j);  
                }
            }else{
                return j-1;
            }
        }
        return -1;
    }
}

【第八题】验证外星语词典

在这里插入图片描述

分析:意思就是说,words里的单词的排序要根据order来排。
1.难点就在于如何利用order对words中的字母进行比较先后。
:利用索引下标。

class Solution{
	public boolean isAlienSorted(String[] words,String order){
		//建立大小为26的数组,根据字母来存放其在order的下标
		int[] index = new int[26];
		for(int i = 0;i < order.length();++i){
			index[order.charAt(i) - 'a'] = i;
		}
		search:for(int i = 0;i < words.length-1;++i){
			String word1 = words[i];
			String word2 = words[i+1];
			for(int k = 0;k < Math.min(word1.length(),word2.length());++k){
				//找到第一个不相同的字符
				if(word1.charAt(k) != word2.charAt(k)){
					if(index[word1.charAt(k)-'a'] > index[word2.charAt(k)-'a'])
						return false;
					continue search;
				}
			}
			if(word1.length() > word2.length())
				return false;
		}
		return true;
	}
}
public boolean isAlienSorted(String[] words, String order) {
        int[] index = new int[26];
        for (int i = 0; i < order.length(); ++i)
            index[order.charAt(i) - 'a'] = i;

        search: for (int i = 0; i < words.length - 1; ++i) {
            String word1 = words[i];
            String word2 = words[i+1];

            boolean continueCompare = true;

            // Find the first difference word1[k] != word2[k].
            for (int k = 0; k < Math.min(word1.length(), word2.length()) && continueCompare;
                k++) {
                if (word1.charAt(k) != word2.charAt(k)) {
                    // If they compare badly, it's not sorted.
                    if (index[word1.charAt(k) - 'a'] > index[word2.charAt(k) - 'a'])
                        return false;
                    else //已经比较出高下了
                        continueCompare = false;
                }
            }

            // If we didn't find a first difference, the
            // words are like ("app", "apple").
            if (continueCompare && word1.length() > word2.length())
                return false;
        }

        return true;
    }

【第九题】找不同

在这里插入图片描述

分析:
方法一:设置两个哈希表,分别记录两个字符串中字母的个数,根据相同的key比较value。
方法二:设置列表,访问两个字符串(将两个字符串拼在一起),不存在的字符加入列表,已存在的再次碰到则删除,最后剩下的那个就是新添加的。
方法三:异或。

//执行用时:10 ms, 在所有 Java 提交中击败了15.66% 的用户
//内存消耗:37 MB, 在所有 Java 提交中击败了40.56% 的用户
class Solution {
    public char findTheDifference(String s, String t) {
        Map<Character,Integer> map1 = new HashMap<>();
        Map<Character,Integer> map2 = new HashMap<>();

        if(s.length() == 0){return t.charAt(0);}

        for(char c:s.toCharArray()){
            map1.put(c,map1.getOrDefault(c,0)+1);
        }
        for(char c:t.toCharArray()){
            map2.put(c,map2.getOrDefault(c,0)+1);
        }
        for(Character c:map2.keySet()){
            if(map1.getOrDefault(c,0) == 0){
                return c;
            }else{
                if(map1.get(c) != map2.get(c)){
                    return c;
                }
            }
        }
        return ' ';
    }
}
//执行用时:6 ms, 在所有 Java 提交中击败了16.30% 的用户
//内存消耗:38.4 MB, 在所有 Java 提交中击败了5.03% 的用户
class Solution{
    public char findTheDifference(String s, String t) {
        HashSet<Character> set = new HashSet<Character>();
        String con = s+t;
        for(char c:con.toCharArray()){
            if(!set.add(c)){
                set.remove(c);
            }
        }
        for(Character c:set){
            return c;
        }
        //return set.toString();
        return 0;
    }
}
//执行用时:3 ms, 在所有 Java 提交中击败了44.67% 的用户
//内存消耗:36.7 MB, 在所有 Java 提交中击败了82.89% 的用户
class Solution{
	public char findTheDifference(String s,String t){
		int ret = 0;
		for(int i = 0;i < s.length();++i){
			ret ^= s.charAt(i);
		}
		for(int i = 0;i < t.length();++i){
			ret ^=t.charAt(i);
		}
		return (char)ret;
	}
}
//————————————————————————————————————————————————————————————
//执行用时:3 ms, 在所有 Java 提交中击败了44.67% 的用户
//内存消耗:36.9 MB, 在所有 Java 提交中击败了58.63% 的用户
class Solution{
    public char findTheDifference(String s, String t) {
        int single = 0;
        String con = s+t;
        for(Character c:con.toCharArray()){
            single ^= c;
        }
        return (char)single;
    }
}

【第十题】错误的集合

在这里插入图片描述

分析:
方法1:直接26个桶,将数字映射进之后,找到value = 2,和value=0的key。
方法2:额外数组。数组索引代表数字,数组值代表数字出现次数。
方法3:排序后,判断相邻两个数字是否只差1就可找到缺失数字。
方法4:一个重复数字/字符——>异或!!!!
方法5:哈希。

//执行用时:2 ms, 在所有 Java 提交中击败了92.45% 的用户
//内存消耗:39.9 MB, 在所有 Java 提交中击败了76.32% 的用户
class Solution{
    public int[] findErrorNums(int[] nums){
        int[] map = new int[nums.length+1];
        int[] ret = new int[2];
        for(int i = 0;i < nums.length;i++){
            if(map[nums[i]] == 0){
                map[nums[i]] = nums[i];
            }else{
                ret[0] = nums[i];
            }
        }
        for(int i = 1;i <map.length;i++){
            if(map[i] == 0){
                ret[1] = i;
            }
        }
        return ret;
    }
}
public class Solution {
    public int[] findErrorNums(int[] nums) {
        int[] arr = new int[nums.length + 1];
        int dup = -1, missing = 1;
        for (int i = 0; i < nums.length; i++) {
            arr[nums[i]] += 1;
        }
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] == 0)
                missing = i;
            else if (arr[i] == 2)
                dup = i;
        }
        return new int[]{dup, missing};
    }
}
public class Solution {
    public int[] findErrorNums(int[] nums) {
        Arrays.sort(nums);
        int dup = -1, missing = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i - 1])
                dup = nums[i];
            else if (nums[i] > nums[i - 1] + 1)
                missing = nums[i - 1] + 1;
        }
        return new int[] {dup, nums[nums.length - 1] != nums.length ? nums.length : missing};
    }
}
public class Solution {
    public int[] findErrorNums(int[] nums) {
        int xor = 0, xor0 = 0, xor1 = 0;
        for (int n: nums)
            xor ^= n;
        for (int i = 1; i <= nums.length; i++)
            xor ^= i;
        int rightmostbit = xor & ~(xor - 1);
        for (int n: nums) {
            if ((n & rightmostbit) != 0)
                xor1 ^= n;
            else
                xor0 ^= n;
        }
        for (int i = 1; i <= nums.length; i++) {
            if ((i & rightmostbit) != 0)
                xor1 ^= i;
            else
                xor0 ^= i;
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == xor0)
                return new int[]{xor0, xor1};
        }
        return new int[]{xor1, xor0};
    }
}

public class Solution {
    public int[] findErrorNums(int[] nums) {
        Map < Integer, Integer > map = new HashMap();
        int dup = -1, missing = 1;
        for (int n: nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }
        for (int i = 1; i <= nums.length; i++) {
            if (map.containsKey(i)) {
                if (map.get(i) == 2)
                    dup = i;
            } else
                missing = i;
        }
        return new int[]{dup, missing};
    }
}

【第十一题】两个列表的最小索引总和

在这里插入图片描述

分析:找出两个字符串数组的共同字符串,并且索引和最小。双指针。
难点:如何根据索引找到值,因为答案可能不止一个,说明一个索引可能对应多个值。用数组不行,用哈希不行

如何保存所有下标和相同的字符串对。

方法一:哈希表。key为索引和,value为字符串列表;(好慢好慢好慢)
方法二:遍历下标和。升序遍历,同时只需要遍历一个list,另一个list的下标直接可以由sum-i获得。一旦有匹配,将其加入字符串数组。
方法三:哈希表,只为一个表建立哈希条目。结果字符串数组只保留下标和为min的字符串。(最快!)

//执行用时:112 ms, 在所有 Java 提交中击败了16.87% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了92.83% 的用户
public class Solution{
	public String[] findRestaurant(String[] list1,String[] list2){
		HashMap<Integer,List<String>> map = new HashMap<>();
		for(int i= 0;i < list1.length;i++){
			for(int j = 0;j < list2.length;j++){
				if(list1[i].equals(list2[j])){
					if(!map.comtainsKey(i+j))
						map.put(i+j,new ArrayList<String>());
					map.get(i+j).add(list1[i]);
				}
			}
		}
		int min_index_sum = Integer.MAX_VALUE;
		for(int key:map.keySet())
			min_index_sum = Math.min(min_index_sum,key);
		String[] res = new String[map.get(min_index_sum).size()];
		return map.get(min_index_sum).toArray(res);
	}
}
//执行用时:57 ms, 在所有 Java 提交中击败了22.02% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了95.22% 的用户
public class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        List < String > res = new ArrayList < > ();
        for (int sum = 0; sum < list1.length + list2.length - 1; sum++) {
            for (int i = 0; i <= sum; i++) {
                if (i < list1.length && sum - i < list2.length && list1[i].equals(list2[sum - i]))
                    res.add(list1[i]);
            }
            if (res.size() > 0)
                break;
        }
        return res.toArray(new String[res.size()]);
    }
}
//执行用时:8 ms, 在所有 Java 提交中击败了92.18% 的用户
//内存消耗:39.1 MB, 在所有 Java 提交中击败了78.48% 的用户
public class Solution {
    public String[] findRestaurant(String[] list1, String[] list2) {
        HashMap < String, Integer > map = new HashMap < String, Integer > ();
        for (int i = 0; i < list1.length; i++)
            map.put(list1[i], i);
        List < String > res = new ArrayList < > ();
        int min_sum = Integer.MAX_VALUE, sum;
        for (int j = 0; j < list2.length && j <= min_sum; j++) {
            if (map.containsKey(list2[j])) {
                sum = j + map.get(list2[j]);
                if (sum < min_sum) {
                    res.clear();
                    res.add(list2[j]);
                    min_sum = sum;
                } else if (sum == min_sum)
                    res.add(list2[j]);
            }
        }
        return res.toArray(new String[res.size()]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值