0
点赞
收藏
分享

微信扫一扫

数据结构--哈希表

大家好,我是下一站不是永远的博主,一名在校学生,本片主要讲解一种数据结构:哈希表

本节重点内容

一、前言
二、数组
三、哈希表
1、百度百科
2、问题引用
3、哈希函数
4、哈希表结构
5、举例分析
6、哈希冲突
7、哈希表的优缺点
四、面试题
1.1.只出现一次的数字
2.前k个高频元素
3.无重复字符的最长字符串
五、复试提问
C语言(总共21道题)
1.static关键字的作用?
2.const关键字的作用?
3.volatile关键字的作用?
4.extern关键字的作用?
5.sizeof关键字的作用?
下面开始本节课的学习

一、前言

      简而言之,数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。

“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。

例如:像评论区那样即有人说:“有人相爱,有人夜里看海,有人力扣第一题”

我们写一道例题来说

                                 数据结构--哈希表_数组

二、数组

在正式开始讲解哈希表之前,我们先回忆之前所学习的数组,它也是一种数据结构,数组有什么特点呢?

数组:是有序的元素序列,用于储存多个相同类型数据的集合

                                 数据结构--哈希表_数据结构_02

数组的优点:通过数组的下标快速定位到该位置下的数值,获取这个数值非常的快,O(1)级别时间复杂度,但是哈希表的出现就能够很好地解决这些问题。

三、哈希表

1、百度百科

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数(哈希函数),存放记录的数组叫做哈希表。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

2、问题引用

假如到了周末,你需要去图书馆借一本书,但是书是按照数组的方式来放的,而恰好书就在最后一个位置,我们需要从头开始找,是不是需要找好久呢?

0

1

2

3

4

5

6

.......

最后找的时间长了,你有些心烦,于是找到管理员,她很聪明,通过一种方式把书籍都记上号码,并且记录下来,所以当她找书的时候,最终在编号为10的位置上,就直接走过去找回来了。

1

2

3

4

5

6

...

.....

....

10(书)

3、哈希函数
  • 当给哈希函数传入相同的输入值时,返回值一样。
  • 当给哈希函数传入不同的输入值时,返回值可能一样,也可能不一样,也就是我们所说的哈希碰撞。
  • 很多不同的输入值所得到的返回值会均匀的分布在输出域上,这一点很重要。
4、哈希表结构

由哈希函数我们可以知道哈希表的结构是怎么样的。

                                 数据结构--哈希表_数据结构_03

我们输入一个值,通过哈希函数计算后 % 上数组的长度,就可以让值放入数组中,所以当我们使用哈希表结构的时候,它的时间复杂度在理想状态下是O(1),接下来给大家举例分析一下:

5、举例分析

                                 数据结构--哈希表_数据_04

当我们输入abc时,由哈希函数计算得到一个哈希值10,10求%后得到1,就把abc放到数组下标为1的位置;然后输入edg,再次由哈希函数计算得到一个哈希值13,13求%后得到4,就把edg放到数组下标为4的位置,后面的操作也是这样。

6、哈希冲突

​最后再来讲一讲哈希冲突:当我们输入future时,假如通过哈希函数计算得到哈希值是15,我们就发现跟love的位置冲突了。

解决这个方式有好几种,比如:拉链法、再次哈希法等等。

这个我准备在写HashMap源码的时候,通过讲解HashMap的底层实现原理时,说一说Java的API的哈希表是什么样的时候再说。​​

7、哈希表的优缺点
  • 优点:处理元素很快,添加、删除、修改、查找元素性能好
  • 缺点:元素在数组中是无序的,不能重复
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] arr = new int[2];
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {//如果为true,说明target-num[i-1]的值在数组中存在
arr[0] = map.get(nums[i]);
arr[1] = i;
return arr;
}
map.put(target - nums[i], i);//这条语句不能放到if语句的前面!
}
return arr;
}
}

四、面试题

1.只出现一次的数字

                                 数据结构--哈希表_数据_05

使用哈希表将每个数字出现的次数作为value,数字为key存入哈希表,再次循环哈希表,判断如果频率为1则找出

    public static int singleNumber(int[] nums) {
HashMap<Integer,Integer> hashMap = new HashMap<>();
for(int i = 0 ; i < nums.length ; i++){
if(hashMap.containsKey(nums[i])){
Integer count = hashMap.get(nums[i]);
hashMap.put(nums[i],++count);
}else{
hashMap.put(nums[i],1);
}
}
for (Integer key : hashMap.keySet()) {
if(hashMap.get(key)==1){
return key;
}
}
return 0;
}

抑或运算

1、因为任意数和0进行异或运算都等于其自身

2、任意数和自己做抑或都为零

3、最后剩下的那个数肯定和0进行抑或等于自身

class Solution {
public int singleNumber(int[] nums) {
int single = 0;
for(int num : nums){
single ^= num;
}
return single;
}
}
2.前k个高频元素

                                 数据结构--哈希表_数据_06

将数字和频率作为key和value存入哈希表,再次循环做一个下标为频率的list哈希表,list存入对应频率的数字,下标>=k的数字即是答案

    public static int[] topKFrequent(int[] nums, int k) {
ArrayList<Integer> restList = new ArrayList<>();
HashMap<Integer,Integer> hashMap = new HashMap<>();
int max = 1;
for(int num : nums){
if(hashMap.containsKey(num)){
Integer count = hashMap.get(num);
max = count+1 > max ? count+1 : max; //最高频率做下面数组的size
hashMap.put(num,++count);
}else{
hashMap.put(num,1);
}
}
List<Integer>[] list = new List[max+1];
for (Integer key : hashMap.keySet()) {
int index = hashMap.get(key);
if(list[index] == null){
list[index] = new ArrayList<>();
}
list[index].add(key);
}
for(int i = list.length-1 ; i >= k; i--){
List<Integer> integers = list[i];
if(integers != null){
for(int num : integers){
restList.add(num);
}
}

}
int[] resArr = new int[restList.size()];
for(int i = 0 ; i < resArr.length ; i++){
resArr[i] = restList.get(i);
}
return resArr;
}

以数字-频率为key和value做哈希表,在利用小顶堆,根据频率大小存入数字,因为是队列二叉树,频率最小的数字会放在根节点,每次存入的时候先和根节点进行比较再决定放不放入。

因为k 的取值范围是 [1, 数组中不相同的元素的个数],所以小顶堆的最大容量就是k,根据频率放入的数量就是要求的k

    public static int[] topKFrequent2(int[] nums, int k) {
HashMap<Integer,Integer> hashMap = new HashMap<>();
for(int num : nums){
if(hashMap.containsKey(num)){
Integer count = hashMap.get(num);
hashMap.put(num,++count);
}else{
hashMap.put(num,1);
}
}

//小顶堆
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//频率小的往上放
return hashMap.get(o1) - hashMap.get(o2);
}
});
for (Integer key : hashMap.keySet()) {
if(priorityQueue.size() < k){
priorityQueue.offer(key);
//对比根节点的频率大小决定是否放入
}else if(hashMap.get(key) > hashMap.get(priorityQueue.peek())){
priorityQueue.poll();
priorityQueue.offer(key);
}
}
int [] res = new int [k];
int ind = 0;
while(!priorityQueue.isEmpty()){
res[ind++] = priorityQueue.poll();
}

return res;
}
3.无重复字符的最长字符串

                                 数据结构--哈希表_数据_07

用哈希表来确定是否重复字符,记录下标,字符串长度等于重复的下标-最开始的下标,最开始的下标用滑动窗口确定

    public static int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer> hashMap = new HashMap<>();
int max = 0;
int left = 0;
//abcaa
for(int i = 0 ; i <s.length(); i++){
if(hashMap.containsKey(s.charAt(i))){
//为什么要加max,因为abba这样 a重复的时候不能回到一开始
left = Math.max(left,hashMap.get(s.charAt(i))+1);
}
hashMap.put(s.charAt(i),i);
max = Math.max(max,i-left+1);
}
return max;
}

五、复试提问

学了到现在 我们已经了解了C语言和数据结构的一部分了,然后我们学习的目的就是走向工作,下面我写几个面试官在过程中常用的几个问题。

C语言(总共21道题)

1.static关键字的作用?

static局部变量只被初始化一次,下一次依据上一次结果值;

在函数外定义的静态变量——静态全局变量,该变量的作用域只能在定义该变量的文件中,不能被其他文件通过extern引用。

static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

2.const关键字的作用?

const意味着“只读”

声明常变量,使得指定的变量不能被修改。

修饰函数形参,使得形参在函数内不能被修改,表示输入参数。

修饰函数返回值,使得函数的返回值不能被修改。

3.volatile关键字的作用?

volatile指定的关键字可能被系统、硬件、进程/线程改变,强制编译器每次从内存中取得该变量的值,而不是从被优化后的寄存器中读取。例如:硬件时钟;多线程中被多个任务共享的变量等。

4.extern关键字的作用?

用于修饰变量或函数,表明该变量或函数都是在别的文件中定义的,提示编译器在其他文件中寻找定义。

5.sizeof关键字的作用?

sizeof是在编译阶段处理,且不能被编译为机器码。sizeof的结果等于对象或类型所占的内存字节数

后面的我们继续学习6-10


举报

相关推荐

哈希表-数据结构

数据结构:哈希表

数据结构 哈希表

哈希表数据结构

哈希表----数据结构

数据结构-哈希表

0 条评论