字符哈希 leetcode

 字符哈希

#include <stdio.h>
#include <iostream>
#include <string>

int main()
{
	int char_map[129] = { 0 };		//存放每个字符的个数
	std::string str = "abcaba";		//字符串

	for (int i = 0; i < str.length(); i++)	//str.length() = 6
	{
		char_map[str[i]]++;    // char_map['a']++; 97 char_map[97]++ = 1
 	}


	for (int i = 0; i < 128; i++)
	{
		if(char_map[i] != 0)
			printf("[%c][%d]:%d次\n", i,i,char_map[i]);
	}



	return 0;
}

[a][97]:3次
[b][98]:2次
[c][99]:1次

哈希表排序整数

#include <stdio.h>
#include <iostream>
#include <string>

int main()
{
	int ran_dom[] = { 1,2,66,999,10,1,1,7,4,2,888 };
	int hash[1000] = { 0 };

	for (int i = 0; i < sizeof(ran_dom) / sizeof(int); i++)
	{
		hash[ran_dom[i]]++;
	}

	for (int i = 0; i < 1000; i++)
	{
		for (int j = 0; j < hash[i]; j++)
		{
			printf("%d\n", i);
		}
	}



	return 0;
}

1
1
1
2
2
4
7
10
66
888
999

拉链法

#include <stdio.h>
#include <vector>
#include <iostream>
#include <string>
using namespace std;

// 定义链表节点结构
struct ListNode {
	int val;
	ListNode* next;
	ListNode(int x) :val(x), next(NULL)
	{

	}
};

// 哈希函数,用于计算键值对应的哈希桶索引
int hash_func(int key, int table_len)
{
	return key % table_len;	// 整数哈希,取余
}

// 插入操作,将节点插入到哈希表中
void insert(ListNode* hash_table[], ListNode* node, int table_len)
{
	int hash_key = hash_func(node->val, table_len); // 计算哈希桶索引
	node->next = hash_table[hash_key]; // 将节点插入到链表的头部
	hash_table[hash_key] = node; // 更新哈希桶的头指针
}

// 查找操作,判断给定的值是否在哈希表中
bool search(ListNode* hash_table[], int value, int table_len)
{
	int hash_key = hash_func(value, table_len); // 计算哈希桶索引
	ListNode* head = hash_table[hash_key]; // 获取链表头指针
	while (head)
	{
		if (head->val == value) // 如果找到匹配的值,返回true
		{
			return true;
		}
		head = head->next; // 继续遍历链表中的下一个节点
	}
	return false; // 如果遍历完链表仍然没有找到匹配的值,返回false
}

int main()
{
	const int TABLE_LEN = 11; // 哈希表的长度
	ListNode* hash_table[TABLE_LEN] = { 0 }; // 定义哈希表,初始化为空指针
	std::vector<ListNode*> hash_node_vec; // 定义存储节点指针的向量
	int test[8] = { 1,1,4,9,20,30,150,500 }; // 测试用例数组

	// 将测试用例数组中的值构建成节点,并存储到向量中
	for (int i = 0; i < 8; i++)
	{
		hash_node_vec.push_back(new ListNode(test[i]));
	}

	// 将节点插入到哈希表中
	for (int i = 0; i < hash_node_vec.size(); i++)
	{
		insert(hash_table, hash_node_vec[i], TABLE_LEN);
	}

	printf("Hash Table:\n");

	// 打印哈希表中的链表
	for (int i = 0; i < TABLE_LEN; i++)
	{
		printf("[%d]:", i);
		ListNode* head = hash_table[i]; // 获取当前哈希桶的头指针
		while (head)
		{
			printf("->%d", head->val); // 打印节点的值
			head = head->next; // 继续遍历链表中的下一个节点
		}
		printf("\n");
	}
	printf("\n");
	printf("Test search:\n");

	// 在哈希表中进行查找操作
	for (int i = 0; i < 10; i++)
	{
		if (search(hash_table, i, TABLE_LEN))
		{
			printf("%d is in the hash table.\n", i);
		}
		else
		{
			printf("%d is not in the hash table.\n", i);
		}
	}

 	for(int i=0; i<10; i++)
	{
		delete hash_node_vec[i];
	} 

	return 0;
}

Hash Table:
[0]:
[1]:->1->1
[2]:
[3]:
[4]:->4
[5]:->500
[6]:
[7]:->150
[8]:->30
[9]:->20->9
[10]:

Test search:
0 is not in the hash table.
1 is in the hash table.
2 is not in the hash table.
3 is not in the hash table.
4 is in the hash table.
5 is not in the hash table.
6 is not in the hash table.
7 is not in the hash table.
8 is not in the hash table.
9 is in the hash table.

class Solution {
public:
    bool patternMatching(string pattern, string value) {
        // 统计模式中 'a' 和 'b' 的个数
        int count_a = 0, count_b = 0;
        for (char ch: pattern) {
            if (ch == 'a') {
                ++count_a;
            } else {
                ++count_b;
            }
        }
        // 如果 'a' 的个数小于 'b' 的个数,交换 'a' 和 'b' 的个数,并将模式中的字符 'a' 和 'b' 交换
        if (count_a < count_b) {
            swap(count_a, count_b);
            for (char& ch: pattern) {
                ch = (ch == 'a' ? 'b' : 'a');
            }
        }
        // 如果 value 为空,则判断 'b' 的个数是否为0
        if (value.empty()) {
            return count_b == 0;
        }
        // 如果模式为空,则返回false
        if (pattern.empty()) {
            return false;
        }
        // 遍历所有可能的 'a' 的长度 len_a
        for (int len_a = 0; count_a * len_a <= value.size(); ++len_a) {
            // 剩余的字符个数
            int rest = value.size() - count_a * len_a;
            // 如果 'b' 的个数为0且剩余的字符个数也为0,或者 'b' 的个数不为0且剩余的字符个数可以整除 'b' 的个数
            if ((count_b == 0 && rest == 0) || (count_b != 0 && rest % count_b == 0)) {
                // 计算 'b' 的长度 len_b
                int len_b = (count_b == 0 ? 0 : rest / count_b);
                int pos = 0;
                bool correct = true;
                string value_a, value_b;
                // 遍历模式中的字符
                for (char ch: pattern) {
                    if (ch == 'a') {
                        // 获取长度为 len_a 的子串,与 value_a 比较
                        string sub = value.substr(pos, len_a);
                        if (!value_a.size()) {
                            value_a = move(sub);
                        } else if (value_a != sub) {
                            correct = false;
                            break;
                        }
                        pos += len_a;
                    } else {
                        // 获取长度为 len_b 的子串,与 value_b 比较
                        string sub = value.substr(pos, len_b);
                        if (!value_b.size()) {
                            value_b = move(sub);
                        } else if (value_b != sub) {
                            correct = false;
                            break;
                        }
                        pos += len_b;
                    }
                }
                // 如果模式匹配正确且 value_a 不等于 value_b,则返回true
                if (correct && value_a != value_b) {
                    return true;
                }
            }
        }
        return false;
    }
};

给定的代码是一个解决字符串模式匹配问题的函数patternMatching。它接受两个字符串作为输入:pattern表示模式,value表示待匹配的字符串。函数的目标是判断是否存在一种匹配方式,使得模式中的字符 'a' 和 'b' 可以对应于 value 中的某些子串,且 'a' 对应的子串与 'b' 对应的子串不相等。

该函数的主要思路是遍历所有可能的 'a' 的长度 len_a,计算剩余字符的个数,并根据 'b' 的个数计算 'b' 的长度 len_b。然后,将模式中的 'a' 对应的子串与 'b' 对应的子串分别提取出来,并进行比较。如果存在一种匹配方式满足条件,则返回true;否则返回false。

力扣

1748. 唯一元素的和

给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。

请你返回 nums 中唯一元素的  。

class Solution {
public:
    int sumOfUnique(vector<int>& nums) {
        vector<int> hash(101,0);
        int sum = 0;
        for(int i=0; i<nums.size(); i++)
        {
            hash[nums[i]]++;
        }

        for(int i=0; i<nums.size(); i++)
        {
            if(hash[nums[i]] == 1)
            {
                sum += nums[i];
            }
        }
        return sum;
    }


};
class Solution {
public:
    int sumOfUnique(vector<int> &nums) {
        unordered_map<int, int> cnt; // 创建无序哈希表 cnt,用于记录每个数字出现的次数
        for (int num : nums) {
            ++cnt[num]; // 遍历 nums,将每个数字作为键,将其出现次数加 1 存储在 cnt 中
        }
        int ans = 0; // 初始化答案为 0
        for (auto &[num, c] : cnt) { // 遍历 cnt 中的每个键值对
            if (c == 1) { // 如果数字的出现次数为 1,说明是唯一元素
                ans += num; // 将该唯一元素的值累加到答案中
            }
        }
        return ans; // 返回答案
    }
};

 
for (int num : nums)

for (auto &[num, c] : cnt)

这两句是使用 C++11 引入的范围遍历(Range-based For Loop)语法。

1. `for (int num : nums)`:
   - 这是一种简化的循环语法,用于遍历容器 `nums` 中的元素。
   - `int num` 是一个声明,用于定义一个循环变量 `num`,它会依次取 `nums` 容器中的每个元素的值。
   - 在循环体内部,可以使用变量 `num` 来访问当前遍历到的元素的值。
   - 这种语法可以替代传统的使用索引的循环方式,使代码更简洁易读。

2. `for (auto &[num, c] : cnt)`:
   - 这也是 C++11 引入的范围遍历语法,用于遍历容器 `cnt` 中的键值对。
   - `auto &[num, c]` 是一个声明,使用了自动类型推导(auto)来推导变量的类型。
   - `&[num, c]` 是一个引用绑定(reference binding),将键值对的键绑定到 `num`,将键值对的值绑定到 `c`。
   - 在循环体内部,可以使用变量 `num` 来访问当前遍历到的键,使用变量 `c` 来访问当前遍历到的值。
   - 这种语法可以方便地同时访问键和值,使代码更简洁易读。

这些语法的引入是为了使 C++ 代码更加简洁、易读和高效。它们在 C++11 中引入,并成为了 C++ 的常见用法。使用范围遍历可以减少手动迭代和索引管理的工作,提高代码的可读性和简洁性。

 `unordered_map` 和 `map`

`unordered_map` 和 `map` 是 C++ 标准库中两种不同的关联容器,它们用于实现键值对的存储和检索。它们的主要区别在于底层实现和特性上。

1. 底层实现:
   - `unordered_map` 使用哈希表(散列表)作为底层数据结构,以实现快速的插入、查找和删除操作。由于使用哈希表,其元素的存储顺序是不确定的。
   - `map` 使用红黑树作为底层数据结构,以实现有序的键值对存储和高效的插入、查找和删除操作。红黑树保持元素按照键的升序排序。

2. 时间复杂度:
   - `unordered_map` 的插入、删除和查找操作的平均时间复杂度为 O(1),最坏情况下的时间复杂度为 O(n),其中 n 是元素的数量。
   - `map` 的插入、删除和查找操作的平均时间复杂度为 O(log n),其中 n 是元素的数量。

3. 元素顺序:
   - `unordered_map` 不保持元素的顺序,元素在哈希表中根据键的哈希值分布。
   - `map` 保持元素按照键的升序排序,因此可以迭代得到有序的键值对。

4. 内存占用:
   - `unordered_map` 基于哈希表,相对于 `map` 需要更多的额外内存来存储哈希表的索引结构。
   - `map` 基于红黑树,相对于 `unordered_map` 可能更节省内存。

根据使用场景的不同,我们可以选择合适的关联容器。如果需要快速的插入、删除和查找操作,并且不关心元素的顺序,则可以使用 `unordered_map`。如果需要有序的键值对集合,或者需要按照键的升序遍历元素,则可以使用 `map`。

需要注意的是,`unordered_map` 和 `map` 提供的接口和用法大致相同,包括插入、删除、查找等操作,因此可以根据实际需求选择适合的容器来实现相关功能。

2206. 将数组划分成相等数对

给你一个整数数组 nums ,它包含 2 * n 个整数。

你需要将 nums 划分成 n 个数对,满足:

  • 每个元素 只属于一个 数对。
  • 同一数对中的元素 相等 。

如果可以将 nums 划分成 n 个数对,请你返回 true ,否则返回 false 。

class Solution {
public:
    bool divideArray(vector<int>& nums) {
        unordered_map<int,int> cnt;
        for(int num:nums)
        {
            cnt[num]++;
        }


/*
        for(const auto & kvp :cnt)
        {
            if(kvp.second & 1)
*/
        for(auto &[num,c] :cnt)
        {
            if(c&1)
            {
                return false;
            }
        }
        return true;
    }
};

1832. 判断句子是否为全字母句

全字母句 指包含英语字母表中每个字母至少一次的句子。

给你一个仅由小写英文字母组成的字符串 sentence ,请你判断 sentence 是否为 全字母句 。

如果是,返回 true ;否则,返回 false 。

class Solution {
public:
    bool checkIfPangram(string sentence) {
       
       vector<int> exist(26);

       for(auto i:sentence)
       {
           exist[i-'a'] = true;
       }
       for(auto i:exist)
       {
           if(!i)
           {
               return false;
           }
       }
       return true;
    }
};
class Solution {
public:
    bool checkIfPangram(string sentence) {
        unordered_map<int,int> cnt;
        for(char num : sentence)
        {
            cnt[num]++;
        }
        for(int i='a'; i<='z';i++)
        {
            if(cnt[i]==0)
            {
                return false;
            }
        }
        return true;
    }
};

在您提供的代码中,cnt 是一个 unordered_map<int, int> 类型的容器,用于记录每个字符出现的次数。

在这段代码中,cnt 的大小是根据实际字符范围来确定的。它是一个无序哈希表,以字符的 ASCII 值作为键,出现次数作为值。由于题目限定了字符范围是小写字母 'a' 到 'z',因此 cnt 的大小是 26。

在遍历 sentence 字符串时,将每个字符作为键,将对应位置的哈希表值加 1。由于 cnt 的大小足够容纳 'a' 到 'z' 的字符,不会发生越界现象。

在第二个循环中,遍历从 'a' 到 'z' 的字符,检查每个字符的出现次数。如果某个字符的出现次数为 0,说明该字符在 sentence 中没有出现,返回 false

因此,根据题目要求,这段代码可以正确判断给定的句子是否是一个 pangram(包含所有字母)。

代码

测试用例

测试用例

执行结果

1512. 好数对的数目

给你一个整数数组 nums 。

如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,就可以认为这是一组 好数对 。

返回好数对的数目。

示例 1:

输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

示例 2:

输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对

示例 3:

输入:nums = [1,2,3]
输出:0

提示:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100

class Solution {
public:
    int numIdenticalPairs(vector<int>& nums) {
        int j, ans = 0; // 定义变量 j 和 ans,初始化 ans 为 0
        int hash[101]; // 定义大小为 101 的 hash 数组,用于记录每个数字出现的次数
        memset(hash, 0, sizeof(hash)); // 使用 memset 将 hash 数组初始化为 0
        for (j = 0; j < nums.size(); ++j) { // 遍历 nums 数组
            ans += hash[nums[j]]; // 累加 hash[nums[j]] 的值到 ans
            ++hash[nums[j]]; // 将 nums[j] 对应位置的值加 1
        }
        return ans; // 返回最终的结果 ans
    }
};

这段代码的思路如下:

  1. 定义变量 j 和 ans,并初始化 ans 为 0,用于记录最终结果。
  2. 定义大小为 101 的 hash 数组,用于记录每个数字出现的次数。这里假设数字的范围在 0 到 100 之间。
  3. 使用 memset 函数将 hash 数组初始化为 0,确保所有计数器的初始值为 0。
  4. 遍历 nums 数组,使用变量 j 作为循环变量。
  5. 在循环体内,首先将 hash[nums[j]] 的值累加到 ans 中。因为对于当前数字 nums[j],它能够与之前出现过的相同数字形成的符合条件的数对数量为 hash[nums[j]]
  6. 然后,将 nums[j] 对应位置的计数器加 1,表示该数字出现了一次。
  7. 循环结束后,返回最终的结果 ans,即符合条件的数对的数量。

这段代码的目的是计算给定数组中满足条件的数对数量,其中条件是两个数相等且索引不同。通过遍历数组并使用哈希表记录每个数字出现的次数,可以快速计算出符合条件的数对的数量。

class Solution {
public:
    int numIdenticalPairs(vector<int>& nums) {
        int ans=0;
        int counter[101] = {0};
        for(int n:nums)
        {
            ans += counter[n];
            counter[n]++;
        }

        return ans;

    }
};

2006. 差的绝对值为 K 的数对数目

给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。

|x| 的值定义为:

  • 如果 x >= 0 ,那么值为 x 。
  • 如果 x < 0 ,那么值为 -x 。

示例 1:

输入:nums = [1,2,2,1], k = 1
输出:4
解释:差的绝对值为 1 的数对为:

示例 2:

输入:nums = [1,3], k = 3
输出:0
解释:没有任何数对差的绝对值为 3 。

示例 3:

输入:nums = [3,2,1,5,4], k = 2
输出:3
解释:差的绝对值为 2 的数对为:

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100
  • 1 <= k <= 99
class Solution {
public:
    int countKDifference(vector<int>& nums, int k) {
        int hash[101]; // 定义大小为 101 的 hash 数组,用于记录每个数字出现的次数
        int ans = 0; // 初始化结果 ans 为 0
        memset(hash, 0, sizeof(hash)); // 使用 memset 初始化 hash 数组为 0

        for (int j = 0; j < nums.size(); ++j) { // 遍历 nums 数组
            int x = nums[j] + k; // 计算与当前数字 nums[j] 相差 k 的值
            if (x >= 1 && x <= 100) { // 判断 x 是否在有效范围内
                ans += hash[x]; // 累加 hash[x] 的值到 ans
            }
            x = nums[j] - k; // 计算与当前数字 nums[j] 相差 k 的值
            if (x >= 1 && x <= 100) { // 判断 x 是否在有效范围内
                ans += hash[x]; // 累加 hash[x] 的值到 ans
            }
            ++hash[nums[j]]; // 将 nums[j] 对应位置的值加 1
        }
        return ans; // 返回最终的结果 ans
    }
};

这段代码的思路如下:

  1. 定义大小为 101 的 hash 数组,用于记录每个数字出现的次数。这里假设数字的范围在 1 到 100 之间。
  2. 初始化结果 ans 为 0。
  3. 使用 memset 函数将 hash 数组初始化为 0,确保所有计数器的初始值为 0。
  4. 遍历 nums 数组,使用变量 j 作为循环变量。
  5. 在循环体内,分别计算两个目标值 x,一个是 nums[j] + k,另一个是 nums[j] - k。这样可以找到与当前数字 nums[j] 相差 k 的两个目标值。
  6. 对于每个目标值 x,首先判断它是否在有效范围内(1 到 100),如果是,则将 hash[x] 的值累加到 ans 中。
  7. 最后,将当前数字 nums[j] 对应位置的计数器加 1。
  8. 循环结束后,返回最终的结果 ans,即满足条件的数对的数量。

这段代码的目的是计算给定数组中差值为 k 的数对的数量。通过遍历数组并使用哈希表记录每个数字出现的次数,可以快速计算出满足条件的数对的数量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值