Day 5 哈希表1

哈希表的基本概念

1. 定义:一般哈希表都是用来快速判断一个元素是否出现集合里,但是哈希法也是牺牲了空间换取了时间。将元素映射到哈希表上就涉及到了hash function ,也就是哈希函数。

2. 解决哈希碰撞的方式是拉链法(用链表)和线性探测法。

3. 实现方式:

        红黑树是key有序的平衡二叉树。优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。

        map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的,因为key的存储方式使用红黑树实现的。

242.有效的字母异位词

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题属于查找,可以想到用哈希表进行。这道题可以利用数组作为哈希表。

解答:

将26个英文字母的ASCII码作为key,查找ASCII码的方法是用字符与'a'作差。

bool isAnagram(string s, string t) {
        int record[26] = {0};
        for(int i=0; i<s.size(); i++){
            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){
                return false;
            }
        }
        return true;
    }

349. 两个数组的交集

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题同样需要使用哈希表,但是数组不太行,因为不知道最大的元素是什么。即使通过遍历得知了最大元素,哈希表也会比较稀疏。因此想到使用链表,但是链表因为索引的时间复杂度为O(n)并不能充当哈希表。

解答:

1. 容器的选择:

使用数组来做哈希的题目,是因为题目都限制了数值的大小。如果哈希值比较少、特别分散、跨度非常大,就要使用另一种结构体了,set。关于set,C++ 给提供了如下三种可用的数据结构:

  • std::set:红黑树,key有顺序
  • std::multiset:红黑树,key有顺序,元素可以重复
  • std::unordered_set:哈希表,key无顺序,读写效率最高

根据这道题的需求,我们不需要让key有顺序,因此使用unordered_set容器。

2. 容器的使用:

(1)容器的复制

nums1.begin()nums1.end() 是指向容器 nums1 的迭代器。nums1 是一个 vector<int> 类型的对象。这两个函数通常用来获取容器的起始和结束位置的迭代器nums1.begin() 返回指向第一个元素的迭代器,如果 vector 为空,则 nums1.begin() 的值等同于 nums1.end()nums1.end()是一个指向 vector最后一个元素之后位置的迭代器,它并不指向任何有效的元素,而是用于标记容器的结束。当这两个迭代器一起使用时,它们通常表示容器的整个范围。例如,在代码 unordered_set<int> nums_set(nums1.begin(), nums1.end());

(2)容器的元素查找

在 C++ 中,nums_set.find(num) 尝试在 nums_set 中找到一个值为 num 的元素。如果找到了这个元素,find 方法返回一个指向该元素的迭代器。如果没有找到,它返回一个指向 unordered_set 结束位置的迭代器,即 nums_set.end()

(3)容器的遍历

for (int num : nums2) 是一个范围基于的 for 循环(也称为基于范围的 for 循环或范围 for 循环),简洁地遍历容器(如数组、向量、集合等)中的所有元素。

代码:

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){
            if(nums_set.find(num)!=nums_set.end()){
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(),result_set.end());
    }

 

202. 快乐数

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目解答:代码随想录

思路:

这道题的关键在于判断什么情况下会陷入无限循环。这里无限循环可以用哈希表来实现。

感悟:

识别无限循环的方法至今有两种,一种在链表中,用快慢指针识别。这里快慢指针还可以用来判断循环开始的位置。这里对循环节没有限制

另一种是哈希表。但这种方式需要循环节内部没有重复元素。换句话说,循环起始位置就可以识别循环的开始。对于这道题,哈希表是更好的方式。

代码:

bool isHappy(int n) {
        unordered_set <int> exist_set;
        int cal_num = n;
        do{
            int cal = 0;
            do{
                cal+=(cal_num%10)*(cal_num%10);
                cal_num/=10;
            }while(cal_num);
            if(exist_set.find(cal)==exist_set.end()){
                exist_set.insert(cal);
                cal_num = cal;
            }else{
                return false;
            }
        }while(cal_num!=1);
        return true;
    }

1. 两数之和

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

答案链接:代码随想录

思路:

这道题的思路是哈希表,但是需要dictionary的数据类型。每次从字典中找对称元素。

解答:

(1)什么时候使用哈希法:当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。 

(2)字典数据类型map:

(3)字典的插入:

map.insert(pair<int, int>(nums[i], i)) 这行代码用于向 map 中插入一个新的键值对。pair<int, int>(nums[i], i): 这创建了一个 std::pair 对象,它是一个将两个值组合成一个单一对象的简单容器。

代码:

vector<int> twoSum(vector<int>& nums, int target) {
        std::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 {};
    }
  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一段Java代码实现: ```java import java.io.File; import java.io.FileNotFoundException; import java.util.Arrays; import java.util.HashMap; import java.util.Scanner; public class Billionaire { private String name; private String arriveDate; private String city; private double wealth; public Billionaire(String name, String arriveDate, String city, double wealth) { this.name = name; this.arriveDate = arriveDate; this.city = city; this.wealth = wealth; } public String getName() { return name; } public String getArriveDate() { return arriveDate; } public String getCity() { return city; } public double getWealth() { return wealth; } public static void main(String[] args) { // 从文件中读取亿万富翁信息 String filename = "billionaires.txt"; Billionaire[] billionaires = readBillionairesFromFile(filename); // 按照到达日期排序 Arrays.sort(billionaires, (a, b) -> a.getArriveDate().compareTo(b.getArriveDate())); // 计算每个城市每天的财富总和 HashMap<String, Double> cityWealthMap = new HashMap<>(); String prevDate = ""; for (Billionaire billionaire : billionaires) { String cityDateKey = billionaire.getArriveDate() + "-" + billionaire.getCity(); if (!cityWealthMap.containsKey(cityDateKey)) { cityWealthMap.put(cityDateKey, 0.0); } if (!prevDate.equals("")) { double wealthPerDay = billionaire.getWealth() / daysBetween(prevDate, billionaire.getArriveDate()); for (String dateKey : getDateKeysBetween(prevDate, billionaire.getArriveDate(), billionaire.getCity())) { cityWealthMap.put(dateKey, cityWealthMap.get(dateKey) + wealthPerDay); } } prevDate = billionaire.getArriveDate(); } // 输出每个城市每天的财富总和 for (String cityDateKey : cityWealthMap.keySet()) { System.out.println(cityDateKey + ": " + cityWealthMap.get(cityDateKey)); } // 输出按照到达日期排序后的亿万富翁列 for (Billionaire billionaire : billionaires) { System.out.println(billionaire.getName() + ": " + billionaire.getArriveDate() + " " + billionaire.getCity() + " " + billionaire.getWealth()); } } // 从文件中读取亿万富翁信息 private static Billionaire[] readBillionairesFromFile(String filename) { try { Scanner scanner = new Scanner(new File(filename)); int n = scanner.nextInt(); scanner.nextLine(); // 读取换行符 Billionaire[] billionaires = new Billionaire[n]; for (int i = 0; i < n; i++) { String line = scanner.nextLine(); String[] parts = line.split(" "); billionaires[i] = new Billionaire(parts[0], parts[1], parts[2], Double.parseDouble(parts[3])); } scanner.close(); return billionaires; } catch (FileNotFoundException e) { e.printStackTrace(); return null; } } // 计算两个日期之间相差的天数 private static int daysBetween(String date1, String date2) { int year1 = Integer.parseInt(date1.substring(0, 4)); int month1 = Integer.parseInt(date1.substring(5, 7)); int day1 = Integer.parseInt(date1.substring(8, 10)); int year2 = Integer.parseInt(date2.substring(0, 4)); int month2 = Integer.parseInt(date2.substring(5, 7)); int day2 = Integer.parseInt(date2.substring(8, 10)); int days1 = getDaysFromYearStart(year1, month1, day1); int days2 = getDaysFromYearStart(year2, month2, day2); return Math.abs(days2 - days1); } // 计算某年某月某日是该年的第几天 private static int getDaysFromYearStart(int year, int month, int day) { int[] daysInMonth = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int days = day; for (int i = 1; i < month; i++) { days += daysInMonth[i]; } if (month > 2 && isLeapYear(year)) { days++; } return days; } // 判断某年是否为闰年 private static boolean isLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } // 获取两个日期之间的所有日期对应的哈键 private static String[] getDateKeysBetween(String date1, String date2, String city) { int year1 = Integer.parseInt(date1.substring(0, 4)); int month1 = Integer.parseInt(date1.substring(5, 7)); int day1 = Integer.parseInt(date1.substring(8, 10)); int year2 = Integer.parseInt(date2.substring(0, 4)); int month2 = Integer.parseInt(date2.substring(5, 7)); int day2 = Integer.parseInt(date2.substring(8, 10)); int days1 = getDaysFromYearStart(year1, month1, day1); int days2 = getDaysFromYearStart(year2, month2, day2); int diff = days2 - days1; String[] dateKeys = new String[diff + 1]; for (int i = 0; i <= diff; i++) { int currYear = year1; int currMonth = month1; int currDay = day1 + i; if (currDay > 31 && (currMonth == 1 || currMonth == 3 || currMonth == 5 || currMonth == 7 || currMonth == 8 || currMonth == 10 || currMonth == 12)) { currDay -= 31; currMonth++; } else if (currDay > 30 && (currMonth == 4 || currMonth == 6 || currMonth == 9 || currMonth == 11)) { currDay -= 30; currMonth++; } else if (currDay > 28 && currMonth == 2 && !isLeapYear(currYear)) { currDay -= 28; currMonth++; } else if (currDay > 29 && currMonth == 2 && isLeapYear(currYear)) { currDay -= 29; currMonth++; } if (currMonth > 12) { currMonth = 1; currYear++; } dateKeys[i] = String.format("%04d-%02d-%02d-%s", currYear, currMonth, currDay, city); } return dateKeys; } } ``` 请注意,上述代码只是一个示例,可能需要根据实际情况进行修改和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值