今天的内容是哈希表
文章目录
- [442. 数组中重复的数据](https://leetcode.cn/problems/find-all-duplicates-in-an-array/)
- [2068. 检查两个字符串是否几乎相等](https://leetcode.cn/problems/check-whether-two-strings-are-almost-equivalent/)
- [2283. 判断一个数的数字计数是否等于数位的值](https://leetcode.cn/problems/check-if-number-has-equal-digit-count-and-digit-value/)
- [884. 两句话中的不常见单词](https://leetcode.cn/problems/uncommon-words-from-two-sentences/)
- [1512. 好数对的数目](https://leetcode.cn/problems/number-of-good-pairs/)
- [2006. 差的绝对值为 K 的数对数目](https://leetcode.cn/problems/count-number-of-pairs-with-absolute-difference-k/)
- [1347. 制造字母异位词的最小步骤数](https://leetcode.cn/problems/minimum-number-of-steps-to-make-two-strings-anagram/)
442. 数组中重复的数据
一道题四种解法,来自对于题解的理解。
暴力哈希表:直接看题目给出数据范围最大是10^5,直接开辟一个100001的数组当作哈希表来存储数字出现的次数。遍历即可。
int* findDuplicates(int* nums, int numsSize, int* returnSize){
int hash[100001] = {0};
*returnSize = 0;
int* ans = (int*)malloc(sizeof(int)*numsSize);
for(int i =0;i<numsSize;i++)
{
++hash[nums[i]];
}
for(int i =0;i<100000;i++)
{
if(hash[i]==2)
{
ans[(*returnSize)++] = i;
}
}
return ans;
}
思路2:原地修改数组,将原数组当作哈希表,然后遍历数组,每次将nums[abs(nums[i])-1]位置的值置为负数,如果,这个位置的值已经是负数了,说明之前出现过一次nums[i]了,那么这个数就出现了两次,直接将其放入结果数组。
int* findDuplicates(int* nums, int numsSize, int* returnSize){
int* ans = (int*)malloc(sizeof(int)*numsSize);
*returnSize = 0;
for(int i =0;i<numsSize;i++)
{
if(nums[abs(nums[i])-1]>0)
{
nums[abs(nums[i])-1] = -nums[abs(nums[i])-1];
}
else
{
ans[(*returnSize)++] = abs(nums[i]);
}
}
return ans;
}
思路3:交换数组元素到对的位置去,因为数组元素个数与元素大小是对应的,如果有元素出现两次,那么必有元素没有出现,直接将每个数组元素移动到对应的位置,最后遍历数组,如果nums[i]!=i+1,也即是该元素没有和位置对应,该元素就出现了两次。
int* findDuplicates(int* nums, int numsSize, int* returnSize){
int*ans = (int*)malloc(sizeof(int)*numsSize);
*returnSize = 0;
for(int i =0;i<numsSize;i++)
{
while(nums[i]!=nums[nums[i]-1])//数字不在应该在的位置就交换
{
int tmp = nums[i];
nums[i] = nums[tmp-1];
nums[tmp-1] = tmp;
}//每次交换都会将nums[i]放到对的位置,直到对的位置和要交换的数字对应了,说明该数字是第二次出现了,就跳出交换循环。
}
for(int i =0;i<numsSize;i++)
{
if(nums[i]!=i+1)
{
ans[(*returnSize)++] = nums[i];
}
}
return ans;
}
思路4:原地哈希,遍历一边数组,将每个位置的而元素交换到正确的位置上,然后判断如果正确的位置上已经有了一个一样的元素,说明该元素是已经出现过的,就放入数组,同时将元素变为负数,防止该元素被交换到其他位置,被重复计入。
int* findDuplicates(int* nums, int numsSize, int* returnSize){
int*ans = (int*)malloc(sizeof(int)*numsSize);
*returnSize = 0;
for(int i =0;i<numsSize;i++)
{
int t = nums[i];
if(t < 0 || t == i+1)
continue;
if(nums[i] == nums[nums[i]-1])
{
ans[(*returnSize)++] = nums[i];
nums[i] *=-1;
}
else//不相等则交换到正确的位置。同时此次遍历无效要让i--
{
int c = nums[t - 1];
nums[t - 1] = t;
nums[i--] = c;
}
}
return ans;
}
2068. 检查两个字符串是否几乎相等
思路:哈希表记录两个数组中每个字符出现的次数,最后遍历两个哈希表求出差,大于3 false遍历结束,true。
bool checkAlmostEquivalent(char * word1, char * word2){
int hash1[26] = { 0 };
int hash2[26] = {0};
for(int i =0;word1[i];i++)
{
int a = word1[i]-'a';
++hash1[a];
}
for(int i =0;word2[i];i++)
{
int a = word2[i]-'a';
++hash2[a];
}
for(int i =0;i<26;i++)
{
if(abs(hash1[i]-hash2[i])>3)
{
return false;
}
}
return true;
}
2283. 判断一个数的数字计数是否等于数位的值
思路: 10个元素的哈希表记录每个元素出现的次数,然后哦遍历,哈希表,最大下标是数组长度,然后将数组内容对应成数字就是该下标数字出现的次数count,然后与哈希表记录的次数比较,不同就返回false。
bool digitCount(char * num){
int hash[10] = {0};
int len = strlen(num);
for(int i =0;num[i];i++)
{
int a = num[i] - '0';
++hash[a];
}
for(int i =0;i<len;i++)
{
int count = num[i] -'0';
if(count!=hash[i])
return false;
}
return true;
}
884. 两句话中的不常见单词
官方题解,日后重刷
思路:利用字符串作为哈希表的索引,先将字符串分割然后加入哈希表中,最后遍历哈希表,如果有单词出现了一次,就把它加入结果数组。
关于c语言使用哈希表的链接学习。
typedef struct {
char * word;
int val;
UT_hash_handle hh;
} HashEntry;
bool insert(char * str, HashEntry ** obj) {
HashEntry * pEntry = NULL;
char *token = NULL;
token = strtok(str, " ");
while (token != NULL ) {
pEntry = NULL;
HASH_FIND_STR(*obj, token, pEntry);
if (NULL == pEntry) {
HashEntry * pEntry = (HashEntry *)malloc(sizeof(HashEntry));
pEntry->word = (char *)malloc(sizeof(char) * (strlen(token) + 1));
strcpy(pEntry->word, token);
pEntry->val = 1;
HASH_ADD_STR(*obj, word, pEntry);
} else {
pEntry->val++;
}
token = strtok(NULL, " ");
}
return true;
}
char ** uncommonFromSentences(char * s1, char * s2, int* returnSize){
HashEntry * freq = NULL;
HashEntry * pEntry = NULL;
insert(s1, &freq);
insert(s2, &freq);
unsigned int sentenceSize = HASH_COUNT(freq);
char ** ans = (char **)malloc(sizeof(char *) * sentenceSize);
int pos = 0;
HashEntry *curr = NULL, *next = NULL;
HASH_ITER(hh, freq, curr, next) {
if (curr->val == 1) {
ans[pos] = (char *)malloc(sizeof(char) * (strlen(curr->word) + 1));
strcpy(ans[pos], curr->word);
pos++;
}
}
HASH_ITER(hh, freq, curr, next) {
free(curr->word);
HASH_DEL(freq, curr);
}
*returnSize = pos;
return ans;
}
补充
1512. 好数对的数目
思路:先根据题目实例分析,数据范围是1–100可以用哈希表记下数字出现次数。遍历数组,如果去哈希表中查找,之前出现过没有,出现过就将出现次数加到计数器上,然后自增哈希表对应位置的次数。
int numIdenticalPairs(int* nums, int numsSize){
int hash[101] = {0};
int ans = 0;
for(int i =0;i<numsSize;i++)
{
if(hash[nums[i]])
ans+=hash[nums[i]];
++hash[nums[i]];
}
return ans;
}
2006. 差的绝对值为 K 的数对数目
思路1:暴力出奇迹,没有什么是两个for循环不能解决的,如果有,那就再来一个。
int countKDifference(int* nums, int numsSize, int k){
int cnt = 0;
for(int i = 0;i<numsSize;i++)
{
for(int j = i+1;j<numsSize;j++)
{
if(abs(nums[i]-nums[j])==k)
{
cnt++;
}
}
}
return cnt;
}
思路2:哈希表,可以看出nums[i] = k+nums[j] 或者 -k+nums[j];所以这就给我们一个哈希思路,遍历数组去哈希表里面查找哈希表中是否存在nums[i]也即是k±nums[j],出现一个就是一个满足的数组,加到计数器上就可。
要注意判断nums[i]±k是不是在数组范围内,防止越界。
int countKDifference(int* nums, int numsSize, int k){
int cnt = 0;
int hash[101] = {0};
for(int i =0;i<numsSize;i++)
{
if(nums[i]+k>0 && nums[i]+k<=100)
cnt+=hash[nums[i]+k];
if(nums[i]-k>0 && nums[i]-k<=100)
cnt+=hash[nums[i]-k];
++hash[nums[i]];
}
return cnt;
}
1347. 制造字母异位词的最小步骤数
思路:定义一个哈希表,记录字符串s中出现的字符个数,然后枚举t中的字符个数,用s的每个字符个数减掉t中出现的字符的个数,如果结果为负,则说明该字符只在t中出现过,不用管,如果为0,则说明这个字符两个字符串都有,所以不用换,只有哈希表中大于0的字符,说明s中有t中没有,所以就需要把这些字符替换进t中,代替那些在hashs中<0的字符。
int minSteps(char * s, char * t){
int hashs[26] = {0};
int cnt = 0;
for(int i =0;s[i];i++)
{
++hashs[s[i]-'a'];
}
for(int i =0;t[i];i++)
{
--hashs[t[i]-'a'];
}
for(int i =0;i<26;i++)
{
if(hashs[i]>0)
cnt+=hashs[i];
}
return cnt;
}
放个没解决的链接。
https://leetcode.cn/problems/group-anagrams-lcci/