哈希表习题:有效的字母异位词、两个数组的交集、快乐数、两数之和

有效的字母异位词

242题:给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true

示例 2: 输入: s = “rat”, t = “car” 输出: false

说明: 你可以假设字符串只包含小写字母

数组就是一个简单的哈希表

bool Solution1::isAnagram(string s, string t) {
	//将第一个字符串存放到哈希表中,再从哈希表中减去第二个字符串,最后所有元素频次结果为0才表明相同,
	//如果小于0则表明两字符串不相同?或先对字符串排序再比较?
	if (s.length() != t.length())
	{
		return false;
	}
	unordered_map<int, int> map1;
	//vector<int> map1(26,0);定义一个初始化为0的26位容器
	for (int i = 0; i < s.size(); i++)
	{
		map1[s[i] - 'a']++;//将字符转换为数字作为索引,其值为出现频次
	}
	for (int j = 0; j < s.size(); j++)
	{
		map1[t[j] - 'a']--;
		if (map1[t[j] - 'a'] < 0)
		{
			return false;
		}
	}
	return true;
}
//先排序后比较,但c++编译器会对s==t报错
class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) {
            return false;
        }
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        return s == t;
    }
};

题目383:给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

与上一题一样

bool Solution2::canConstruct(string ransomNote, string magazine) {
	//先将magazine存入哈希表,再将ransomNote从哈希表中减去,如果有频次为负则false
	if (magazine.size() < ransomNote.size())
	{
		return false;
	}
	unordered_map<int, int> hmap;
	int i ;
	for (i = 0; i < magazine.size(); i++)
	{
		hmap[magazine[i] - 'a']++;
	}
	for (i = 0; i < ransomNote.size(); i++)
	{
		hmap[ransomNote[i] - 'a']--;
		if (hmap[ransomNote[i] - 'a'] < 0)
		{
			return false;
		}
	}
	return true;
}

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

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
示例

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
	unordered_map<string, vector<string>> hmap;//将排序后的字符串作为键,一组字母异位词作为值
	for (string& str:strs)//难点在于怎么判断字母异位词:排序后相同?统计字符出现次数相同?
	{
		    string key = str;
			//hmap[sort(key.begin(),key.end())]++;错误在于++是计数,现在值是字符串,学习新的存入哈希表方法;
			//把字符串容器存入哈希表(将字母异位词存入一个索引下)
			sort(key.begin(), key.end());
			hmap[key].push_back(str);
	}
	vector<vector<string>> str;
	for (auto it = hmap.begin(); it != hmap.end(); it++)
	{
		str.push_back(it->second);//在容器尾部插入一个元素
	}
	return str;
    }
};

1.学习了哈希表键值对各自存放的类型,并且不能统一的对哈希表++,只有值是数字时才能++,如果存放的是字符串容器,则需要用push_back()方法将值存放在相应的键中。
2.学习了怎么用同一个键表示不同的值,该题中可以排序也可用字符出现次数计数的方法来表示字母异位符
3.排序调用sort()函数,其有两个参数,分别是开始和结束位
4.用it表示哈希表键对应的元素,其中值个数不止一个

例题438:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
		//用两个26位的数值容器载入3位字符,滑动窗口每次删一加一判断两容器是否相等
        if(s.size()<p.size())
        {
            return {};//返回数值空容器
        }
	vector<int> v1(26, 0);
	vector<int> v2(26, 0);
	vector<int> res;
	for (char& ch : p)
	{
		v1[ch - 'a']++;//存放p字符串
	}
	int i = 0;
	while(i<=s.size()-p.size())
	{
		int t = i;
		while(i == 0 && t<p.size())
		{
			v2[s[t] - 'a']++;
			t++;
		}
		if(i!=0)
		{
			v2[s[t - 1] - 'a']--;
			v2[s[t + p.size()-1] - 'a']++;
		}
		if (v2==v1)
		{
			res.push_back(i);
		}
		i++;
	}
	return res;
    }
};

采用滑动窗口原理,每次移动时删去后一个,新增前一个元素,比每次重新存入p个大小更方便

两个数组的交集

例题349:给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序
示例

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
	vector<int> res;
	if (nums1.size() >= nums2.size())
	{
		for (int i = 0; i < nums1.size(); i++)
		{
            auto it=find(res.begin(),res.end(),nums1[i]);//用迭代器it表示较长容器的元素
			for (int j = 0; j < nums2.size(); j++)
			{
				if (nums1[i] == nums2[j] && it==res.end())//find函数返回的地址,end不参与搜索,如果没找到返回.end(),借此判读是否已经存过该数
				{
					res.push_back(nums1[i]);
				}
			}
		}
	}
	if (nums1.size() < nums2.size())
	{
		for (int i = 0; i < nums2.size(); i++)
		{
             auto it=find(res.begin(),res.end(),nums2[i]);
			for (int j = 0; j < nums1.size(); j++)
			{
				if (nums2[i] == nums1[j] && it==res.end())
				{
					res.push_back(nums2[i]);
				}
			}
		}
	}

	return res;
    }
};

该方法在leetcode上过了,但在VS编辑器上迭代器it不相容,借鉴后两种方法的find写法

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(), nums1.end());//直接将nums1存入nums_set
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);//insert方法将num存入set中,会自动去重
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
	};
class Solution {
public:
	vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
		unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
		int hash[1005] = {0}; // 默认数值为0
		for (int num : nums1) { // nums1中出现的字母在hash数组中做记录
			hash[num] = 1;
		}
		for (int num : nums2) { // nums2中出现话,result记录
			if (hash[num] == 1) {
				result_set.insert(num);
			}
		}
		return vector<int>(result_set.begin(), result_set.end());
	}
};

例题350:给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
 	vector<int> num1(1005, 0);
	vector<int> num2(1005, 0);
	vector<int> res;
	for (int a : nums1)
	{
		num1[a]++;//存数值出现次数
	}
	for (int b : nums2)
	{
		num2[b]++;
	}
	for (int i = 0; i < 1005; i++)
	{
        int count=0;
		while(num1[i] != 0 && num2[i] != 0 && count<min(num1[i],num2[i]))
		{
			res.push_back(i);//存入的应该是下标,而不是容器里的次数
			count++;
		}
	}
	return res;
    }
};

键盘输入数值[0,1000]字符方法

vector<int> nums2;
int t;
cout << "please input nums1:" << endl;
while (cin>>t)
{
	if (t < 0)//键盘输入数值容器,以负数结束
	{
		break;
	}
	nums1.push_back(t);
}

快乐数

例题202:编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

无限循环表明如果不是快乐数,和sum会重复。因此,判断一个数是否在某个集合中,应考虑哈希法
可以用unordered_set存放sum,用insert()函数添加;而vector用push_back向后添加元素
另一个难点:怎么求sum各个位上数字的和

  • 为什么把求和函数写入isHappy函数中会超时,而单独提出来就不会超时?
class Solution {
public:
int getSum(int n)
{
	int sum = 0;
	while (n)
	{
		sum += (n % 10) * (n % 10);//求个位数的平方
		n /= 10;
	}
	return sum;
}
    bool isHappy(int n) {
	//无限循环的意思就是sum会重复,把每次的sum存入哈希表中,如果重复就返回false,如果sum为1就返回true
	int sum=0;
	//因为sum的值没有对应下标的关系,如果按数值做下标则空间占用大,可以用set集合
	unordered_set<int> s;
	//不确定n的具体位数,怎么分解n,每次sum的位数不同怎么计算下一个sum?
		while (1)
		{
			//求sum:有点技术
			if (sum == 1)
			{
				return true;
			}
			sum = getSum(n);
			if (s.find(sum) != s.end())//在s中找到了重复的sum
			{
				return false;
			}
			s.insert(sum);
			n = sum;//更新n的值
	     }
    }
};

两数之和

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

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

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

示例

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
      	vector<int> res;
	int i, j,t;
	for (i = 0; i < nums.size(); i++)
	{
		t = target - nums[i];
		for (j = i+1; j < nums.size(); j++)
		{
			if (nums[j] == t)
			{
				res.push_back(i);
				res.push_back(j);
                return res;
			}
		}
	}
	return {};
    }
};

时间复杂度o(n^2) ,进阶(提出时间复杂度<O(n*n)的算法)

代码随想录解法:将数组存入无序哈希表中,哈希表中用来存已经访问过的对象。key为值,value为下标。遍历访问数组,在哈希表中查找是否有对应的剩余部分,如果有则返回两者的下标,否则将该数加入哈希表,时间复杂度为O(n)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            // 遍历当前元素,并在map中寻找是否有匹配的key
            auto iter = map.find(target - nums[i]); 
            if(iter != map.end()) {
                return {iter->second, i};
            }
            // 如果没找到匹配对,就把访问过的元素和下标加入到map中
            map.insert(pair<int, int>(nums[i], i)); 
        }
        return {};
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值