字符哈希
#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
}
};
这段代码的思路如下:
- 定义变量
j
和ans
,并初始化ans
为 0,用于记录最终结果。- 定义大小为 101 的
hash
数组,用于记录每个数字出现的次数。这里假设数字的范围在 0 到 100 之间。- 使用
memset
函数将hash
数组初始化为 0,确保所有计数器的初始值为 0。- 遍历
nums
数组,使用变量j
作为循环变量。- 在循环体内,首先将
hash[nums[j]]
的值累加到ans
中。因为对于当前数字nums[j]
,它能够与之前出现过的相同数字形成的符合条件的数对数量为hash[nums[j]]
。- 然后,将
nums[j]
对应位置的计数器加 1,表示该数字出现了一次。- 循环结束后,返回最终的结果
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
}
};
这段代码的思路如下:
- 定义大小为 101 的
hash
数组,用于记录每个数字出现的次数。这里假设数字的范围在 1 到 100 之间。 - 初始化结果
ans
为 0。 - 使用
memset
函数将hash
数组初始化为 0,确保所有计数器的初始值为 0。 - 遍历
nums
数组,使用变量j
作为循环变量。 - 在循环体内,分别计算两个目标值
x
,一个是nums[j] + k
,另一个是nums[j] - k
。这样可以找到与当前数字nums[j]
相差k
的两个目标值。 - 对于每个目标值
x
,首先判断它是否在有效范围内(1 到 100),如果是,则将hash[x]
的值累加到ans
中。 - 最后,将当前数字
nums[j]
对应位置的计数器加 1。 - 循环结束后,返回最终的结果
ans
,即满足条件的数对的数量。
这段代码的目的是计算给定数组中差值为 k
的数对的数量。通过遍历数组并使用哈希表记录每个数字出现的次数,可以快速计算出满足条件的数对的数量。