LeetCode 1207 哈希表

好久没摸算法了,今天开始每日来一题,做了每日打卡才发现自己手法生了。


  1. 独一无二的出现次数
    难度: 简单
    给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。
    如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false。
    示例 1:
    输入:arr = [1,2,2,1,1,3]
    输出:true
    解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/unique-number-of-occurrences

显然是哈希表,(嗯 看了题解才说显然,嗯显然我都忘完了),来来来 复习下 数据结构 哈希表:

目前最熟的应该是 我上学期学过的 算法分析 里面提过的 二叉平衡树、B+树,都是从根节点开始查找数值,(感觉挺麻烦的 hhh)

想这道题的时候,我也是想的,有没有什么方法可以让一个数组直接通过数值作为索引来查找。
那其实哈希表就是这样的。


什么是哈希表:

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。(这就是百度解释

按照我的理解就是,哈希表,就是将一个个数值(value)放在一个个格子里,并打上一个标签(key),但是这样一点也不专业,实质上的解释就是:
哈希表就是一个函数:hashtable(value,key)

  • 存入原理就是把key(预映射)加粗样式通过一个函数(散列函数)转换成一个整型数字(散列值),然后就将该数字对数组长度进行取余(压缩映射),取余结果就当作数组的下标(地址),将 value 存储在以该数字为下标(地址)的数组空间(存储空间)里。
  • 取出原理就是再次使用函数定位到存储地址获取存储的 value。

那这么看来,其实就和二维数组差不多嘞,但是其实是有区别的。
如果二维数组,直接一个key,一个value 存的话,空间浪费会很大,因为比如 存两个key的value,比如 [1,1] [999,1] 那就会开 int[2][1000]。
如果哈希表的话,假设散列函数为:H(key)=(1000-key)/100 这样存储就是:

keyindexvalue
191
99901

(这个 除号后面的 数,在实现的时候算法决定的)
所以我们发现哈希函数

  • 优点:按key 一对一查找效率很高;

但是呢显然我们也发现,就比如这个 H(key)=(1000-key)/100 函数,当出现 998 999 两个key,这样存储的位置是一样的,于是就出现了散列冲突

  • 散列冲突:不同的关键字经过散列函数的计算得到了相同的散列地址。

这个时候哈希函数的效率就是依靠散列函数来决定了。
好的散列函数=计算简单+分布均匀(根据key得到的散列地址分布均匀)

好了这些都是关于实现的,这里先不谈这些。


哈希表的使用 java

import java.util.HashMap;
import java.util.Map;
Map<String, Integer> Ages = new HashMap<String, Integer>(); 
// Key 为 String,Value 为 Integer

方法:

MethodDescribe
get (Object key)返回键映射的值,如果该键没有映射值,则返回null
put (key, value)将键和值建立映射,如果键是第一次存储,就直接存储元素,返回null; 如果键不是第一次存在,就用把以前的值替换掉,返回以前值
remve (key)移除键存在的映射关系,并将值移除,返回移除值
containsKey (key)是否存在该键
containsValue (key)是否存在该值
isEmpty ()表是否为空
clear ()移除所有映射,移除所有值
entrySet ()返回键值对的 Set 集合
keySet ()获取所有键 Set 集合
values ()获取所有值 Set 集合
size ()返回集合的映射数量

解题

来看看我咋写的:

class Solution {
    public boolean uniqueOccurrences(int[] arr) {
            int len = arr.length;
            boolean result = true;
            int[][] data = new int[2][2000];
            Stack stack = new Stack();
            // stack 存出现过的值 data 第一行存 出现次数
            for(int i=len-1; i>=0; i--){
                int v = arr[i]+1000;
                data[0][arr[i]+1000]++;
                if(data[0][v] == 1)
                    stack.push(v);
            }
            // data 第二行存 出现次数 计数
            while(!stack.empty()){
                int value = (int)stack.peek();
                stack.pop();
                int j = data[0][value];
                if(data[1][j] == 0)
                    data[1][j] = 1;
                else {
                    return false;
                }
            }
            return true;
    }
}

显然很麻烦啦:
在这里插入图片描述
来看官方的:

class Solution {
public:
    bool uniqueOccurrences(vector<int>& arr) {
        unordered_map<int, int> occur;
        for (const auto& x: arr) {
            occur[x]++;
        }
        unordered_set<int> times;
        for (const auto& x: occur) {
            times.insert(x.second);
        }
        return times.size() == occur.size();
    }
};
//来源:力扣(LeetCode)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值