【华为od刷题(C++)】HJ21 简单密码(哈希表、unordered_map)

我的代码1:

#include 
using namespace std;

int main() {

    string s;
    while (cin >> s) {//从标准输入流中读取字符串 s,并持续读取直到输入结束
        for (int i = 0; i < s.size(); ++i ) {
            //s.size() 返回字符串的长度
            
            switch (s[i]) {//根据 s[i](字符串 s 中第 i 个字符)进行匹配判断

                case 'A'...'Y' :
                    s[i] += 33;
                    break;
                //如果当前字符是 'A' 到 'Y' 范围内的字符
                //执行 s[i] += 33; 语句,即将字符的 ASCII 值增加 33
                //这会将大写字母转化为小写字母

                case 'Z' :
                    s[i] = 'a' ;
                    break;

                case 'a' ... 'c':
                    s[i] = '2';
                    break;

                case 'd' ... 'f':
                    s[i] = '3';
                    break;

                case 'g' ... 'i':
                    s[i] = '4';
                    break;

                case 'j' ... 'l':
                    s[i] = '5';
                    break;

                case 'm' ... 'o':
                    s[i] = '6';
                    break;

                case 'p' ... 's':
                    s[i] = '7';
                    break;

                case 't' ... 'v':
                    s[i] = '8';
                    break;

                case 'w' ... 'z':
                    s[i] = '9';
                    break;

                default:
                    break;
            }

        }
        cout << s << endl;//输出修改后的字符串 s,并换行
    }
    return 0;//main() 函数结束,程序返回 0,表示程序成功结束
}
  • 这段程序会从标准输入流读取一个字符串,然后对其中的每个字符进行修改:
    • 大写字母('A' 到 'Y')被转换为对应的小写字母
    • 特殊字符 'Z' 被转换为 'a'
    • 小写字母根据字母范围('a' 到 'z')被替换为对应的数字字符(2 到 9)
  • 处理后的字符串会被输出到标准输出流

这段程序的作用是对输入的字符串进行字符映射并打印出结果

我的代码2:

#include //用于输入输出
#include //用于使用哈希表存储字母到数字的映射
#include //用于检查字符是否为字母(isalpha函数)
using namespace std;

int main() {
    unordered_map m;//用来存储字母到数字的映射关系
    m['a'] = m['b'] = m['c'] = '2';
    m['d'] = m['e'] = m['f'] = '3';
    m['g'] = m['h'] = m['i'] = '4';
    m['j'] = m['k'] = m['l'] = '5';
    m['m'] = m['n'] = m['o'] = '6';
    m['p'] = m['q'] = m['r'] = m['s'] = '7';
    m['t'] = m['u'] = m['v'] = '8';
    m['w'] = m['x'] = m['y'] = m['z'] = '9';
    m['Z'] = 'a';
    string str;
    while (cin >> str) {//使用while (cin >> str)来读取输入的每一行字符串
        for (int i = 0; i < str.size(); ++i ) {
            //两个条件的if不可以反
            if (str[i] >= 'A' && str[i] < 'Z') {
                str[i] = str[i] - 'A' + 'a' + 1;
        //'A' 是字符 'A' 的 ASCII 值,通常是 65
        //'a' 是字符 'a' 的 ASCII 值,通常是 97
        //str[i] - 'A' 计算的是字符 str[i] 距离字符 'A' 的偏移量
        //将偏移量加上 'a',这将使得字符从大写字母转换为小写字母
        //+ 1 将偏移值增加 1
        //假设原来的字符是大写字母,执行这一操作后会变成下一个字母的 ASCII 值

                //对于每个字符,首先判断它是否为大写字母
                //若是,则将其转换为小写字母并加1(即将其转换为下一个字母)
            } else if (isalpha(str[i])) {
            //isalpha(str[i]) 是 C/C++ 中一个标准库函数
            //用于检查字符是否是字母(A-Z 或 a-z)
            //这个函数返回一个布尔值
            //若字符是字母,则返回 true(通常是非零值);
            //否则,返回 false(零值)

                str[i] = m[str[i]];
                //如果是小写字母,则直接使用映射表m将其转换为相应的数字
            }
        }
        cout << str << endl;
    }
    return 0;
}

这段代码的思路可以总结为以下几个步骤:

  1. 映射表初始化:首先,使用 unordered_map 创建了一个字母到数字的映射表;该表将小写字母('a' 到 'z')映射到数字('2' 到 '9'),并且特别将大写字母 'Z' 映射为小写字母 'a'

  2. 读取输入:通过 cin 循环读取输入的每一行字符串,直到输入结束

  3. 字符处理

    对每个字符进行逐个检查和转换:
    • 小写字母处理:如果是小写字母('a' 到 'z'),则使用映射表将其转换为对应的数字(如 'a' 转换为 '2','b' 转换为 '2',依此类推)
    • 大写字母处理:如果是大写字母('A' 到 'Z'),将其转换为小写字母,并加1(例如,'A' 转换为 'b','B' 转换为 'c')
  4. 输出结果:处理完一行字符串后,将结果输出到屏幕

思路总结:

  • 初始化映射表,存储字母到数字的映射关系
  • 使用 cin 持续读取每一行字符串
  • 遍历字符串中的每个字符,依据字符的类型(大写字母或小写字母)进行相应的转换
  • 输出处理后的字符串

这样,通过逐个字符的转换,将输入的字符串按照特定规则进行修改并输出

哈希表(Hash Table)是一种常用的数据结构,用于存储键值对(key-value pair);它通过哈希函数将键映射到哈希表中的位置,使得数据的查找、插入和删除操作非常高效;哈希表的主要特点是可以在常数时间内(平均情况下)完成查找、插入和删除操作

哈希表的基本原理:

  1. 哈希函数

    • 哈希表的核心是哈希函数(hash function);哈希函数将每个键(key)映射到哈希表中的一个位置(索引);这个位置在哈希表的底层数组中
    • 哈希函数的目标是尽量让不同的键映射到不同的位置,从而避免哈希冲突
  2. 哈希冲突

    • 哈希冲突(hash collision)发生在两个不同的键映射到哈希表中的同一个位置时
    • 常见的处理冲突的方法有:
      • 链式法:每个哈希表的槽(bucket)可以存储一个链表(或其他数据结构),当发生冲突时,将冲突的元素添加到链表中
      • 开放地址法:当发生冲突时,查找下一个空槽位置,直到找到合适的位置
  3. 负载因子

    • 负载因子(load factor)是哈希表的大小(存储的元素个数)与哈希表的容量(槽的数量)之间的比值
    • 当负载因子过大时,哈希表的性能会降低,此时通常需要进行 再哈希(rehashing),即扩展哈希表的容量,并重新计算元素的位置
  4. 查找、插入和删除操作

    • 查找:通过哈希函数计算出键对应的位置,然后在该位置查找元素
    • 插入:同样通过哈希函数找到适当的位置,然后将键值对插入到哈希表中;如果发生冲突,可以采用链式法或开放地址法解决
    • 删除:找到对应的键,删除该键值对;若采用链式法,则删除链表中的节点;若采用开放地址法,则标记该位置为空

哈希表的时间复杂度:

  • 查找:在平均情况下,哈希表的查找时间复杂度为 O(1),但最坏情况下会退化为 O(n)(例如发生大量哈希冲突时)
  • 插入:在平均情况下,插入的时间复杂度是 O(1),最坏情况下为 O(n)
  • 删除:删除操作的时间复杂度在平均情况下是 O(1),最坏情况下为 O(n)

哈希表的优缺点:

优点:

  • 高效查找:查找、插入和删除操作通常都能在常数时间内完成,尤其是在负载因子较低时
  • 灵活性:可以用作实现其他数据结构(如字典、集合等)的基础

缺点:

  • 内存占用:哈希表可能需要较大的内存空间,尤其在负载因子较低时
  • 哈希冲突:哈希冲突会影响性能,尤其在冲突严重时,哈希表的效率会大大降低
  • 无序性:哈希表中的元素是无序的;如果需要元素的顺序,可能需要额外的存储结构或排序操作

哈希表的应用:

  1. 字典和映射:在很多编程语言中,哈希表是实现字典或映射(如 unordered_map 在 C++ 中,dict 在 Python 中)的一种方式
  2. 缓存系统:哈希表非常适用于实现高速缓存(cache),如 LRU 缓存
  3. 数据库索引:数据库中的哈希索引使用哈希表来快速定位数据行
  4. 数据去重:哈希表可以用来判断一个元素是否在数据集中已经出现,常用于去重操作
  5. 集合操作:哈希表是实现集合(set)的常用数据结构

C++ 中的哈希表:unordered_map 和 unordered_set

在 C++ 中,unordered_mapunordered_set 是通过哈希表实现的:

  • unordered_map:存储键值对,允许根据键来访问值
  • unordered_set:存储唯一的元素,只有键,没有值,常用于去重和集合操作

示例代码(使用 unordered_map):

#include 
#include 
using namespace std;

int main() {
    // 创建一个 unordered_map,存储学生的学号和姓名
    unordered_map studentMap;
    
    // 插入数据
    studentMap[1001] = "Alice";
    studentMap[1002] = "Bob";
    studentMap[1003] = "Charlie";
    
    // 查找数据
    cout << "Student with ID 1001: " << studentMap[1001] << endl;
    
    // 查找不存在的元素
    if (studentMap.find(1004) == studentMap.end()) {
        cout << "Student with ID 1004 not found." << endl;
    }
    
    // 遍历 unordered_map
    for (const auto& pair : studentMap) {
        cout << "ID: " << pair.first << ", Name: " << pair.second << endl;
    }
    
    return 0;
}

unordered_map 是 C++ 标准库中的一个容器类,属于 头文件;它是基于哈希表实现的,提供了高效的键值对存储与查找功能;与 map 不同,unordered_map 内部元素是无序的,元素的排列顺序是由哈希函数决定的,因此它不保证按插入顺序或任何其他特定顺序访问元素

unordered_map 的特点:

  1. 基于哈希表实现:使用哈希表(hash table)作为底层数据结构,每个元素是一个键值对 (key-value)
  2. 元素无序unordered_map 中的元素没有固定的顺序,它的遍历顺序与插入顺序无关,完全取决于哈希表的实现
  3. 时间复杂度
    • 插入和查找的平均时间复杂度为 O(1),但在最坏的情况下(哈希冲突严重时),时间复杂度可能退化为 O(n)
    • 删除元素的平均时间复杂度也是 O(1)
  4. 不允许重复的键:每个键必须是唯一的,如果尝试插入一个已经存在的键,那么新的值会覆盖旧的值

代码示例:

#include 
#include 
using namespace std;

int main() {
    // 创建一个 unordered_map,键和值都是 char 类型
    unordered_map freqMap;
    
    // 插入键值对
    freqMap['a'] = 5;
    freqMap['b'] = 3;
    freqMap['c'] = 7;
    
    // 查找键对应的值
    cout << "Frequency of 'a': " << freqMap['a'] << endl;
    
    // 检查某个键是否存在
    if (freqMap.find('d') == freqMap.end()) {
        cout << "'d' is not in the map." << endl;
    }
    
    // 遍历 unordered_map
    for (const auto& pair : freqMap) {
        cout << pair.first << ": " << pair.second << endl;
    }
    
    return 0;
}

特点和注意事项:

  1. 哈希冲突unordered_map 使用哈希表来存储元素,但如果两个键具有相同的哈希值(哈希冲突),会发生冲突;这时,C++ 标准库会通过链表等方式来解决冲突
  2. 键的哈希函数unordered_map 使用哈希函数来计算键的哈希值;在默认情况下,C++ 使用 std::hash 来计算常见类型(如整型、字符型等)的哈希值

你可能感兴趣的:(c++,华为od,算法)