0 前言

本专题将基于LeetCode的题目来分析最佳解决方案背后的一些数据结构与算法原理。
本文将基于题目#1两数之和来分析JAVA中HashMap原理及高性能原因。

1 题目描述

两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

你可以按任意顺序返回答案。

示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 103
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

2 最佳方案及疑问

最容易想到的解决方案是暴力枚举,循环嵌套,空间复杂度只有O(1),但时间复杂度是O(N ^2)。更优的方案是基于哈希表(如JAVA中HashMap)来加快数据查询的效率,用空间来换时间,使时间和空间的复杂度均为O(N)

具体描述及代码可见:https://leetcode-cn.com/probl...

此处的疑问是,为什么用哈希表就可以明显提升查询效率呢?

3 哈希表原理分析

1、为什么会有哈希表这种结构、它由什么组成?

我们已知的结构有数组和链表,两者各有优缺点:

  • 数组:数组存储结构是连续的,空间复杂度大,但查询的时间复杂度小。其寻址(通过下标搜索)效率高,一般的插入和删除效率低。即,查询快,增删慢
  • 链表:链表存储结构是离散的,空间复杂度小。其寻址(通过下标搜索)效率低,一般的插入和删除效率高。即,查询慢,增删快

哈希表是基于数组和链表构建的,目的是为了充分发挥这两个结构的优势。

对于一个哈希表,其包含四个部分的内容:

  • key:即键值
  • value:即数值
  • hash:key对应的hash值,基于hash进行某种操作后得到的是表的index
  • next:当index冲突时,存放冲突的数据,链表结构

2、为什么哈希表的查询效率会高于数组呢?

因为哈希表的结构也有点类似于字典。字典通过key可以快速查询到value,哈希表一样可以通过key快速查询到value,但其中间进行了散列变换(即hash计算),通过hash值来确定value所在的index,从而实现数据的获取。

一个确定的key可以计算得到唯一的hash值,而index是hash通过某种操作得到的,难以难免hash值的冲突,为了解决这个冲突才引入了链表,来高效的存储hash值相同的值。

3、 hashMap.containsKey(value)的时间复杂度是多少?

HashMap在不同版本的JDK之间有着如下的区别:

  • JDK 1.8以前 HashMap由数组和链表构成。
  • JDK 1.8之后 HashMap由数组、链表和红黑树构成。

红黑树的特点是查询快,增删慢。
红黑树 作为一种二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。由于链表的查询时间复杂度为O(n),所以当链表很长时转换成红黑树将很好的提高效率!

红黑树的引入是为了解决,当冲突发生时,链表太长而带来的查询性能下降的问题。在JDK 1.8中规定,当链表长度大于8后,链表转为红黑树结构。转换之前最坏的时间复杂度为O(N),转换之后的最坏时间复杂度为O(logn)

HashMap的原理讲解推荐视频(p6-p9):https://www.bilibili.com/vide...
红黑树的原理推荐:https://www.cnblogs.com/yyxt/...

综上:
JDK 1.8以前,hashMap.containsKey(value)最好情况便是O(1),最坏情况是O(n)
JDK 1.8以后,hashMap.containsKey(value)最好情况便是O(1),最坏情况是O(lgn)

4 HashMap源码分析

这篇文章讲解的很清晰,不再赘述。
https://blog.csdn.net/qingtia...

如果该文章看不懂,可以先看看上文说到的视频。


阿飞爱学习
1 声望5 粉丝