【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)

哈希表基础:

快速判断一个元素是否出现集合里的时候,就要考虑哈希法,哈希法是牺牲了空间换取了时间

哈希表:将数据映射到哈希表中存储,当数据量大于哈希表的容量时可能会出现两个数据映射到表中同一个索引位,如此称为哈希碰撞哈希冲突),解决方法为拉链法线性探测法

拉链法:在冲突位置拉出一条链表,存储冲突的多个数据;

线性探测法:保证哈希表容量大于数据总量,给冲突的数据分配空位。

常见的哈希结构:数组、set和map

【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)_第1张图片

 红黑树是一种平衡二叉搜索树,所以key值是有序的且key不可以修改,只能删除和增加。

【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)_第2张图片

 第一题

力扣

【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)_第3张图片

 GPT解答,比较清晰

class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.length() != t.length()) {
            return false; // 长度不相等,不可能是异位词
        }

        std::map count;
        // 遍历字符串 s,统计每个字符的出现次数
        for (char c : s) {
            count[c]++;
        }

        // 遍历字符串 t,减去每个字符的出现次数
        for (char c : t) {
            count[c]--;
        }

        // 如果两个字符串是异位词,则 count 中所有字符的计数应为 0
        for (const auto& pair : count) {
            if (pair.second != 0) {
                return false;
            }
        }
        return true;
    }
};

自己第二次做的:(注意是对table的索引进行操作,string可以用length()直接取长度)

class Solution {
public:
    bool isAnagram(string s, string t) {
        if ( s.length() != t.length()) {
            return false;
        }
        vector table(26, 0);
        for (int i=0; i

第二题:

力扣

【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)_第4张图片

 第三道AC的题,需要注意的是,auto i:nums1这个写法是C++11引入的一种“范围基于循环”,表示遍历容器中的元素,auto关键字用于自动推导迭代器的类型,对于容器nums1,循环将遍历其中每个元素并赋值给i,因此直接写table[i]而不是table[nums1[i]]。

class Solution {
public:
    vector intersection(vector& nums1, vector& nums2) {
        std::map table;
        for (auto i : nums1) {
                table[i] = 1;
        }
        for (auto i : nums2) {
            if (table[i] == 1) {
                table[i] = -1;
            }
        }
        vector num;
        for (auto& pair : table) {
            if (pair.second == -1) {
                num.push_back(pair.first);
            }
        }
        return num;
    }
};

其实可以将第二个循环和三个合并:

class Solution {
public:
    vector intersection(vector& nums1, vector& nums2) {
        std::map table;
        vector num;
        for (auto i : nums1) {
                table[i] = 1;
        }
        for (auto i : nums2) {
            if (table[i] == 1) {
                num.push_back(i);
                table[i] = 0; //去重
            }
        }
        return num;
    }
};

学习记录:

使用数组来做哈希的题目,是因为题目都限制了数值的大小。没有限制数值的大小不适合用数组做。而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。此时就要使用另一种结构体了,set。但是直接使用set不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的,因此需要综合考虑。

  • std::set
  • std::multiset
  • std::unordered_set

std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。

因此如果使用set,写法跟map差不多,但它存储不重复的元素,并且按照元素的排序规则自动进行排序。因此,当使用 std::set 来存储结果时,重复元素会自动被剔除,无需手动去重。

ChatGPT给出的解,更理解了set的用法,其中count 是 std::set 和 std::unordered_set 等关联容器的一个成员函数,用于统计容器中指定元素的个数。使用 count 函数,你可以判断某个元素是否存在于 std::set 或 std::unordered_set 中。如果返回值为 1,则表示元素存在;如果返回值为 0,则表示元素不存在。

class Solution {
public:
    vector intersection(vector& nums1, vector& nums2) {
        std::set set1(nums1.begin(), nums1.end());
        std::set set2(nums2.begin(), nums2.end());
        
        vector num;
        for (int i : set1) {
            if (set2.count(i) > 0) {
                num.push_back(i);
            }
        }
        return num;
    }
};

代码随想录和我自己写的差不多,但是他用了insert函数,解决了手动赋值去重的操作:

class Solution {
public:
    vector intersection(vector& nums1, vector& nums2) {
        unordered_set 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(result_set.begin(), result_set.end());
    }
};

insert 函数的定义如下:

std::pair insert (const value_type& val);

其中,val 是要插入的元素值。insert 函数会将 val 插入到集合中,并返回一个 std::pair 对象,其中包含一个迭代器(iterator)和一个布尔值(bool)。迭代器指向插入的元素(或者指向已经存在的相同元素),而布尔值表示插入是否成功。

如果集合中已经存在与 val 相同的元素,则插入操作不会生效,此时返回的迭代器指向已经存在的相同元素,而布尔值为 false。如果集合中不存在与 val 相同的元素,则插入操作会生效,此时返回的迭代器指向插入的新元素,而布尔值为 true。

第三题:

力扣

【代码随想录】Day 6 哈希法(字母异位词、数组交集、快乐数)_第5张图片

自己写的暴力解法: 

C++11可以用to_string将int转化为string。

class Solution {
public:
    bool isHappy(int n) {
        std::string num = std::to_string(n);
        std::map num_map;
        int sum = 0;
        int i = 0;
        while (i<10000) {
            i++;
            sum = 0;
            for (char c : num) {
                num_map[c] = (c - '0') * (c - '0');            
                sum = sum + num_map[c];
            }
            if (sum == 1) {
                return true;
            }
            num = std::to_string(sum);
        }
        return false; 
    }
};

学习记录:

 记住!遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法!

用哈希法判断sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。(而我自己是用的循环10000次。。。)

判断sum是否重复出现就可以使用unordered_set。用的函数是set.find(sum)是否等于set.end()

代码随想录中取单数是用的常规思路,n%10后n/10再循环。(而我用的是将n转成string,然后遍历string,将每个char再通过 -'0' 的方式转回来。

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) {
        unordered_set set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

你可能感兴趣的:(哈希算法,算法)