c++刷算法【哈希表】

温习数据结构与算法,准备一些比赛,为了学业和就业,以及提升自己的编程能力,将系统的刷刷算法,入手c++跟着carl,leetcode刷题笔记将持续更新…

哈希表是根据关键码(索引下标)的值而直接进行访问的数据结构。

一般哈希表都是用来快速判断一个元素是否出现集合里。牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

冲突解决:拉链法,线性探测法

使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构:

  • 数组
  • set (集合)
  • map(映射)

242.有效的字母异位词

力扣题目链接

【法一】直接用sort函数,字母排序比较

class Solution {
public:
    bool isAnagram(string s, string t) {
        sort(s.begin(),s.end());
        sort(t.begin(),t.end());
        if(s==t)
            return true;
        else
            return false;
    }
};

【法二】map计数 原理也是哈希计数(字母,出现次数)

class Solution {
public:
    bool isAnagram(string s, string t) {
        unordered_map<char,int> map;
        if (s.size() != t.size()) 
            return false;
        for(int i=0;i<s.size();i++){
            ++map[s[i]];
            --map[t[i]];
        }  
        for(unordered_map<char,int>::iterator it=map.begin();it!=map.end();it++){
            if(it->second!=0)
                return false;
        }
        return true;        
    }
};

【法三】哈希计数效率最高(字母对应数组下标,数组存储出现次数)

因为哈希表的计数排序时间复杂度为O(L+n),其中L是哈希表长,n是数据规模,而常用的排序算法为O(nlogn)。当数据规模n很大的时候,计数排序的性能就体现出来。

public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
            record[s[i] - 'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        // record数组所有元素都为零0,说明字符串s和t是字母异位词
        return true;
    }
};

349. 两个数组的交集

力扣题目链接

注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序

但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。

而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。

而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

此时就要使用另一种结构体了,set ,关于set,C++ 给提供了如下三种可用的数据结构:

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

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

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};
//for(数据类型 遍历变量 : 遍历对象)
//范围for循环不能用于循环体中有改变容器大小的操作。举例子:循环体内不能向vector容器添加元素。
		for (int i = 0; i < nums2.size();i++) {
			int num = nums2[i];
			if (nums_set.find(num) != nums_set.end()) {
				result_set.insert(num);
			}
		}

find() ,返回给定值值得定位器,如果没找到则返回end()。

insert(key_value); 将key_value插入到set中 ,返回值是****pair<set::iterator,bool>****,bool标志着插入是否成功,而iterator代表插入的位置,若key_value已经在set中,则iterator表示的key_value在set中的位置。

202. 快乐数

力扣题目链接

set.find(sum) != set.end()判断sum是否重复出现,使用unordered_set

找到返回值下标,没找到返回end

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<int> 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;
        }
    }
};

两数之和

力扣题目链接

两数之和,无序常用unordered_map哈希表存储,map(值,下标),map中存的也是一个个pair

找到互补元素,pair对组形式返回 {iter->second, i} {找到的互补元素下标,准备存的元素下标}

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map <int,int> map;
        for(int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target - nums[i]);
            if(iter != map.end()) {
                return {iter->second, i};
            }
            map.insert(pair<int, int>(nums[i], i));
        }
        return {};
    }
};

454.四数相加II

力扣题目链接

四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,只统计次数,不用返回元组,不需去重

分组 + 哈希表

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
        // 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
        for (int a : A) {
            for (int b : B) {
                umap[a + b]++;
            }
        }
        int count = 0; // 统计a+b+c+d = 0 出现的次数
        // 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
        for (int c : C) {
            for (int d : D) {
                if (umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

15. 三数之和

力扣题目链接

排序 + 双指针
本题的难点在于如何去除重复解。

  1. 可以加一个特判,对于数组长度 n,如果数组为 null 或者数组长度小于 3,返回 [][]。

    if (nums.size() < 3) return result;

  2. 对数组进行排序。

  3. 遍历排序后数组:

    • 若 nums[i]>0:因为已经排序好,所以后面不可能有三个数加和等于 0,直接返回结果。

    • 对于重复元素:跳过,避免出现重复解

    • 令左指针 L=i+1,右指针 R=n-1,当 L<RL<R 时,执行循环:
      当 nums[i]+nums[L]+nums[R]==0,执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 L,RL,R 移到下一位置,寻找新的解

      若和大于 0,说明 nums[R] 太大,RR 左移
      若和小于 0,说明 nums[L] 太小,LL 右移

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }         
            // 去重方法
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                } else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
    }
};

18. 四数之和

力扣题目链接

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针

类比三数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        for (int k = 0; k < nums.size(); k++) {
            // 去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if (nums[k] + nums[i] > target - (nums[left] + nums[right])) {
                        right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } else if (nums[k] + nums[i]  < target - (nums[left] + nums[right])) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // 去重逻辑应该放在找到一个四元组之后
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }

            }
        }
        return result;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中,可以使用哈希表来判断两个字符串是否是同构串。哈希表是一种数据结构,可以将键值对存储在其中,并通过哈希函数将键映射到特定的位置。在这个问题中,我们可以使用哈希表来存储每个字符在字符串中的位置,并比较两个字符串中对应位置的字符在哈希表中的映射是否相同。 首先,我们需要定义一个哈希函数来将字符串转换成可以进行取模的值。在给定的引用\[1\]中,使用了BKDR字符串哈希算法来实现这个功能。对于字符串类型的键,我们可以使用这个哈希函数来计算其哈希值。 接下来,我们可以使用一个自定义的哈希表类来实现哈希表的功能。在给定的引用\[2\]中,展示了一个封装了哈希表的unordered_set类的设计。这个类提供了插入、删除和查找元素的功能。 在哈希表的实现中,每个元素都有一个数据变量和一个状态变量。在给定的引用\[3\]中,展示了哈希表内每个元素的构成。这个哈希表类还提供了默认构造函数,可以自动生成一个默认的哈希表。 因此,我们可以使用C++中的哈希表来实现同构串的判断。通过将两个字符串中对应位置的字符映射到哈希表中,并比较其映射是否相同,我们可以确定这两个字符串是否是同构串。 #### 引用[.reference_title] - *1* *2* *3* [C++STL哈希表](https://blog.csdn.net/qq_62745420/article/details/127202722)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值