知识点
-
Hash 的概念
-
List item
-
构造方法
-
冲突处理
为什么使用哈希表
我们上面所提到的查找算法,简单来说,就是判断现有数据集合中是否有这个元素,或者是否有满足条件的元素。
其中的 Hash 算法则可以帮助我们判断是否有这个元素,虽然功能简单,但是其 O(1) 时间复杂度是具有高性能的。通过在记录的存储地址和它的关键码之间建立一个确定的对应关系。这样,不经过比较,一次读取就能得到所查元素的查找方法。相比普通的查找算法来说,仅仅在比较的环节,就会大大减少查找或映射所需要的时间。
什么是哈希表(散列表)
我们采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间即称为散列表。下面用一张图给大家展示一下散列表的实现过程:
如果还是不太明白的话,我们可以理解为数学函数,Y=F(X),X 为自变量也就是这里的 Key, F( ) 对应图中的 H( ),也就是一个映射关系,Y 因变量也就是对应的值的 存放位置,此处一定要注意哦。
此处让我们思考一下:
- List item
散列技术仅仅是一种查找技术吗?
应该说,散列既是一种查找技术,也是一种存储技术。
散列是一种完整的存储结构吗?
散列只是通过记录的关键码定位该记录,没有完整地表达记录之间的逻辑关系,即通过关键码能推出 Key 值,但是通过关键码对应的值(即位置处的值)不能推出关键码,所以散列存储的关键码和值之间并不对称,因此散列主要是面向查找的存储结构。
散列表的缺陷
散列表并不是适用于所有的需求场景,那么哪些情况下不适合使用呢?
散列技术一般不适合在允许多个记录有同样关键码的情况下使用。
因为这种情况下,通常会有冲突存在,将会降低查找效率,体现不出散列表查找效率高的优点。
并且如果一定要在这个情况下使用的话,还需要想办法消除冲突,这将花费大量时间,那么就失去了 O(1) 时间复杂度的优势,所以在存在大量的冲突情况下,我们就要弃用散列表。
散列方法也不适用于范围查找,比如以下两个情况。
查找最大值或者最小值
因为散列表的值是类似函数的,映射函数一个变量只能对应一个值,不知道其他值,也不能查找最大值、最小值,RMQ(区间最值问题)可以采用 ST 算法、树状数组和线段树解决。
也不可能找到在某一范围内的记录
比如查找小于 N 的数有多少个,是不能实现的,原因也是映射函数一个变量只能对应一个值,不知道其他值。
题目:
弗里的语言
小发明家弗里想创造一种新的语言,众所周知,发明一门语言是非常困难的,首先你就要克服一个困难就是,有大量的单词需要处理,现在弗里求助你帮他写一款程序,判断是否出现重复的两个单词。
要求:有重复的单词,就输出重复单词, 没有重复单词,就输出 NO,多个重复单词输出最先出现的。
import java.util.Scanner;
import java.util.Stack;
public class Main {
static final int h=999983;
static String[] Value=new String [h];
static String[] UpValue=new String [h];
static int UpValueCount=0;
static int Hx(String s) {
return (s.hashCode()%h+h)%h;
}
static boolean isAt(String s)
{
int n=Hx(s);
if(Value[n]==null) return false;
else if(Value[n].equals(s)) return true;
else {
for(int i=0;i<UpValueCount;i++)
if(UpValue[i].equals(s)) return true;
return false;
}
}
static boolean in(String s)
{
int n=Hx(s);
if(Value[n]==null) {
Value[n]=s;
return true;
}
else if(Value[n].equals(s)) return false;
else {
for(int i=0;i<UpValueCount;i++)
if(UpValue[i].equals(s)) return false;
UpValue[UpValueCount++]=s;
return true;
}
}
public static void main(String[] args)
{
int n;
boolean flag=false;
Scanner in=new Scanner(System.in);
String ans="NO";
n=in.nextInt();
for(int i=0;i<n;i++)
{
String word;
word=in.next();
// System.out.println(Hx(word));
if(flag) continue;
if(isAt(word)){
flag=true;
ans=word;
}
else {
in(word);
}
}
System.out.println(ans);
}
}