代码随想录算法训练营第6天 | 哈希表1

哈希表: 哈希表是根据关键码的值直接进行访问的数据结构(数组就是一张哈希表,可以通过数组索引下标直接访问数组中的元素)。
面向的问题: 一般哈希表都是用来快速判断一个元素是否出现在集合里。
哈希表查询的时间复杂度为: O(1)

哈希函数: 索引和关键码的值之间的映射关系。

哈希冲突(碰撞):几个关键码的值同时映射到同一个索引的位置。
解决方法:

  1. 拉链法,发生冲突的位置存储在链表中。2. 线性探测法,冲突的位置后面在找其他空位放置其他元素。

三种哈希结构: 数组(vector/array);集合(set) ;映射(map)

集合底层实现是否有序数值是否可以重复是否能够更改数值查询效率
set红黑树有序O(logN)
multiset红黑树有序O(logN)
unordered_set哈希表无序O(1)

注:

  1. 底层实现为红黑树的数据结构,改变key的值会导致整个树的错乱,所以只能删除和增加。
  2. 使用集合来解决哈希问题时,优先使用unordered_set,因为它的查询和增删效率是最优的;如果需要集合是有序的,使用set;如果不仅要求有序还要求有重复数据,就用multiset。
映射底层实现是否有序数值是否可以重复是否能够更改数值查询效率
map红黑树有序O(logN)
multimap红黑树有序O(logN)
unordered_map哈希表无序O(1)

注: map中对key值有限制,对value的值没有限制(因为key值存储方式是使用红黑树实现的)。

总结: 当我们遇到了要快速判断一个元素是否出现过,要考虑哈希法。哈希法是牺牲了空间换时间。

242.有效的字母异位词

题目: 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
链接: https://leetcode.cn/problems/valid-anagram
思路:

  1. 题中只包含26个小写字母,可以将字母a-z映射到索引0-25上。
  2. 只需要使用数组即可。
  3. 哈希函数:index = s[i] - ‘a’
  4. 在s里面统计key值来一遍set[index]++,在t里面统计key值来一遍set[index]–,然后看看是不是set里面的key全为0了。
class Solution {
public:
	bool isAnagram(string s, string t) {
		int countSet[26] = {0};
		for(int i = 0; i < s.size(); ++i){
			int temp = s[i] - 'a';
			countSet[temp]++;
		}
		for(int i = 0; i < t.size(); ++i){
			int temp = t[i] - 'a';
			countSet[temp]--;
		}
		for(auto x:countSet){
			if(x != 0){
				return false;
			}
		}
		return true;
	}
};

时间复杂度:O(N)遍历三遍3 × N
空间复杂度:O(1)定义的是一个常数大小的数组

349.两个数组的交集

题目: 给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
链接: https://leetcode.cn/problems/intersection-of-two-arrays/
思路:

  1. 先把数组nums1的值存入set,然后遍历nums2,看看set里面是否包含nums2的元素,若包含就放进ans里面。
  2. 一些暗示:输出结果每个元素唯一(不重复/需要去重),不考虑输出顺序(无序),使用unordered_set数据结构。
    注: 如果哈希值比较少、特别分散、跨度非常大,使用数组会造成空间浪费。
class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		unordered_set<int> ansTemp;
		unordered_set<int> record;
		for(auto num : nums1){
			record.insert(num);
		}
		for(auto num : nums2){
			if(record.count(num)){
			ansTemp.insert(num);
			}
		}
		vector<int> ans;
		for(auto x:ansTemp){
			ans.push_back(x);
		}
		return ans;
	}
};

上述代码没问题,但是不够简洁(注意使用迭代器进行数组、集合的初始化),优化后的代码为:

class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		unordered_set<int> ansTemp;
		unordered_set<int> record(nums1.begin(), nums1.end());
		for(auto num : nums2){
			if(record.count(num)){
				ansTemp.insert(num);
			}
		}
		vector<int> ans(ansTemp.begin(), ansTemp.end());
		return ans;
	}
};

注: 使用set占用的空间比数组大,速度比数组慢,set把数值映射到key上都要做hash计算的。
**本题后面 力扣改了 题目描述 和 后台测试数据,增添了 数值范围:

  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000
    所以就可以 使用数组来做哈希表了, 因为数组都是 1000以内的。
class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		unordered_set<int> ansTemp;
		int record[1001] = {0};
		for(auto num:nums1){
			record[num] = 1;
		}
		for(auto num : nums2){
			if(record[num] == 1){
			ansTemp.insert(num);
			}
		}
		vector<int> ans(ansTemp.begin(), ansTemp.end());
		return ans;
	}
};

总结: 将一个unordered_set替换成数组,占用内存有所减少;去重操作使用unordered_set很方便;注意使用迭代器初始化数组。

202.快乐数

题目: 编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
思路:

  1. 无限循环但始终变不到 1,使用哈希法判断sum是否重复出现,可以使用unordered_set数据结构。
  2. 写一个函数进行每一次操作的计算。
class Solution {
	int getSum(int n){
		int sum = 0;
		while(n){
			int temp = n % 10;
			sum += temp * temp;
			n /= 10;
		}
		return sum;
	}
public:
	bool isHappy(int n) {
		unordered_set<int> sumBuffer;
		int sum = getSum(n);
		while(sum != 1){
			if(!sumBuffer.count(sum)){
				sumBuffer.insert(sum);
				sum = getSum(sum);
			}
			else{
				return false;
			}
		}
		return true;
	}
};

1.两数之和

题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
链接: https://leetcode.cn/problems/two-sum
思路:

  1. 使用哈希法-映射的数据结构,一个key = target- nums[i]对应一个value = (nums中的数)的下标,无序,可以使用unordered_map
  2. 一些暗示:每种输入只会对应一个答案;数组中同一个元素在答案里不能重复出现。
  3. 在遍历数组的时候,只需要向map去查询是否有和目前遍历元素,如果有,就找到的匹配对,如果没有,就把目前遍历的元素比配的数值放进map中,因为map存放的就是我们访问过的元素比配的数值。
    注: map<key, value>判断key值元素是否存在。
  4. find()返回一个迭代器:找到,返回迭代器指向要查找的元素;未找到,返回迭代器指向end。
  5. count()返回0/10未找到,1找到。
class Solution {
public:
	vector<int> twoSum(vector<int>& nums, int target) {
		vector<int> ans;
		unordered_map<int, int> map;
		for(int i = 0; i < nums.size(); ++i){
			//查看一下之前是否有元素 = target - nums[i]
			if(map.count(nums[i])){
				ans.push_back(i);
				ans.push_back(map[nums[i]]);
			}
			//没有 就把自己的target - nums[i]存进去
			else{
				map[target - nums[i]] = i;
			}
		}
		return ans;
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值