哈希表
1. 相关概念:
概念
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。
原理:key —> 哈希函数 —> 内存地址(—–> key/value 对应的内存地址)
Java中的是
HashMap
哈希碰撞
不同key值通过一个哈希函数得到相同的内存地址
解决方法:
链地址法:产生hash冲突后在存储数据后面加一个指针,指向后面冲突的数据
2. 哈希表的四种功能
1. 访问(Access):没有
没有这个概念,因为没有下标,有的是key
2. 搜索(Search):O(1)
对key进行搜索,如果产生碰撞,时间复杂度是O(K),K是碰撞元素的个数
3. 插入(Insert): O(1)
通过key插入
4. 删除(Delete):O(1)
通过key删除
3.常用操作
两种方法:
- 数组,把数组索引当作哈希表的key,元素当作哈希表的value
HashMap
1.创建哈希表
1.数组方式
2.Java中
HashMap
方式,其中Integer
是key的类型,String
是value的类型
2.添加元素
1.数组:将值赋给对应的索引
2.
HashMap
:put(key,value)
3.更新元素
和添加一样,将原来的元素给覆盖了
1.数组:将修改的值赋给对应的索引
2.
HashMap
:put(key,value)
4.删除元素
remove(key)
5.获取元素
1.数组:直接获取索引
2.
HashMap
:get(key)
6.检查key是否存在
containsKey(key)
7.哈希表的长度
size()
8.哈希表是否还有元素
isEmpty()
4.Leetcode
217.存在重复元素
三种方法:
- HashMap
- 数组
- HashSet
方法一: HashMap
public boolean containsDuplicate(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
for (int k : map.keySet()) {
if (map.get(k) > 1) {
return true;
}
}
return false;
}
方法二:
- 对数组进行排序
- 设置一个指针(指向前一个元素)进行遍历
public boolean containsDuplicate1(int[] nums) {
Arrays.sort(nums);
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == nums[i + 1]) {
return true;
}
}
return false;
}
方法三:
HashSet
HashSet
是实现Set<E>
接口的一个实体类,数据是以哈希表的形式存放的,里面的不能包含重复数据。Set接口是一种一个不包含重复元素的 collection。
public boolean containsDuplicate2(int[] nums) {
/*
if (nums == null || nums.length == 0) {
return false;
}
*/
HashSet<Integer> set = new HashSet<>();
for (int num : nums) {
set.add(num);
}
return set.size() == nums.length ? false : true;
}
389.找不同
方法一:
运用数组,用到ASCII码,a=97,A=65
因为两个字符串都包含小写字母,所以数组长度是26
巧用ASCII码,让每个字符的ASCII码-a的ASCII码,得到的就是数组的下标
遍历两个字符串,一个加一一个减一
如果最后元素的结果<0,就返回该元素
package qi.hashmap;
/*
首先遍历字符串 s,对其中的每个字符都将计数值加 1;然后遍历字符串 t,对其中的每个字符都将计数值减 1。当发现某个字符计数值为负数时,说明该字符在字符串 tt中出现的次数大于在字符串 s 中出现的次数,因此该字符为被添加的字符。
*/
public class LeetCode389 {
public char findTheDifference(String s, String t) {
int[] cnt = new int[26];
for (int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
cnt[ch - 'a']++;
}
for (int i = 0; i < t.length(); ++i) {
char ch = t.charAt(i);
cnt[ch - 'a']--;
if (cnt[ch - 'a'] < 0) {
return ch;
}
}
return ' ';
}
}
方法二:
求和
定义两个变量接收两个字符中所有字符的ASCII相加的值
用长的t减去短的s,差值在转为行的ASCII码
public char findTheDifference1(String s, String t) {
int as=0;
int at=0;
for (int i = 0; i < s.length(); i++) {
as+=s.charAt(i);
}
for (int i = 0; i < t.length(); i++) {
at+=t.charAt(i);
}
return (char) (at-as);
}
496.下一个更大元素
用栈和哈希表实现
遍历nums2,当栈不为空并且nums2中的元素 > 栈顶元素
将栈顶元素弹出(作为key)与nums2中的元素(作为value)放入哈希表中
否则将元素压栈(num一定进栈)
如果栈不为空,将元素弹栈放入哈希表中,并且value为-1
遍历nums1,将nums1中的元素在哈希表中的value遍历出来
返回接收的数组
public class Leetcode496 {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] res = new int[nums1.length];
Stack<Integer> stack = new Stack<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums2) {
while (!stack.isEmpty() && num > stack.peek()) {
map.put(stack.pop(), num);
}
stack.push(num);
}
while (!stack.isEmpty()) {
map.put(stack.pop(), -1);
}
for (int i = 0; i < nums1.length; i++) {
res[i] = map.get(nums1[i]);
}
return res;
}
}
这里的while写成了if导致结果错误,
while是循环语句,if是判断语句,
用 if 的话会导致漏掉一些可能性