数据结构之Hash表
一.数据结构概念
数据结构是计算机存储,组织数据的方式.
包括三个组成部分: 数据的逻辑结构 ,数据的存储结构 ,数据运算结构
就个人理解,数据结构就是存储数据的容器,简单的数组显然不能适应大量数据存储,查找等功能的实现。因此,合理,高效,也就是空间和时间效率都必须高效,也就成了计算机人员所研究的重点对象。
接下来,就简单介绍Hash表作为一种数据结构的原理,具体代码实现.
二.Hash原理
1.数组,链表 , 数等数据结构有一个共同点:数据的位置和其关键字之间不存在一个确定关系,它们之间的区别仅在于:关键字和给定值比较的顺序不同.
2.将数据位置和关键字联系----以Hash(key) 函数计算key在表中的位置.
接下来我将以除留余数法---H(key)=key MOD p(p
3.冲突.
可以想象,数据数量一旦增多,必然发生不同key值通过Hash函数计算的地址值相同的情况,我们称这种情况成为——冲突。
为解决冲突,即为产生冲突的关键字寻找下一个哈希地址,可以采取多种方法:
(开放定址法 , 再Hash法 ,链地址法 等)
此时,我选择最后一种方法来讲解,其余的方法读者有兴趣课自行找寻资料进行学习.
4.链地址法——结合数组和链表,即通过Hash函数计算得到相同地址的元素链在第一个元素的后面。此时,存储形式就是一个个节点了,包括数据的Key值和指针域。
5.数据结构的用途就是实现对数据的操作:增----删----查----改 ,效率当然越高越好.
就我们所知的:
数组查找方便,通过下表可直接定位,但增,删的效率较慢,因为改变的代价是其他数据也要进行相应的移动。
链表在插入,删除等操作上显然体现了其优势,因为只用改变数据链域中的指向即可。但链表的查找操作却仍是需要逐一遍历,效率较低。
Hash的结构很容易解释其平衡查找和删除插入操作的效率问题
6.阀值.
首先,Hash表仍是以数组为基础的,即数组大小初始就要设定且不能改变,当我们无法预知数据总量时,无法准确估计数组大小。若数组初始设定的过大,则浪费内存,若太小,则会出现数组下的链表过长,甚至长于数组的长度,此时,查找效率明显降低。
为解决这个问题,我们需要设定一个阀值,用作控制平衡的量度.
此时,我设定的阀值的是:某一条链上的节点总数和数组大小的比值。这这是个数值,更是个规则。建设Hash表过程中,不能超过这个规则的设定。一旦超过,则需要ReHash。
7.ReHash
用更大的数组重新计算地址,并按照上述规则建设Hash表,直至满足阀值的要求。
三.代码实现
public class MyHash {
private static int size;// 数组的初始大小
private static int newSize;//新数组的大小
Node[] array;//初始数组
Node[] newArray;//新数组
static int length = 0;// 当前存储的节点数
double p = 0.7;//阀值
static int time = 1;//reHash的次数统计
public static void main(String[] agr) {//主函数
MyHash hash = new MyHash();
hash.test();
}
public MyHash() {//构造函数
size = 16;
array = new Node[size];// 初始化数组
}
private void test() {// 测试方法
// 添加
Random random = new Random();//创造随机数
long start1 = System.currentTimeMillis();//当前时间
for (int i = 0; i < 100000; i++) {
int num = random.nextInt(10000);//循环10万次
add(num);//添加至Hash链表中
}
System.out.println();
long start2= System.currentTimeMillis() ;
// 检查是否超过阀值,若超过则reHash
overP();
System.out.println("遍历数组------------------------->");
long start3 = System.currentTimeMillis();
for (int j = 0; j < size; j++) {
System.out.println("第" + j + "个位置");
Node node = array[j];
System.out.print(node.getKey() + " ");
node = node.getNext();
while (node != null) {
System.out.print(node.getKey() + " ");
node = node.getNext();
}
System.out.println();
}
System.out.println("实现Hash创建所用时间为:"
+ (start2-start1));
System.out.println("实现Hash搜索所用时间为:"
+ (System.currentTimeMillis() - start3));
}
/**
* 根据key值计算地址
*
* @return:地址
*/
public int Hash(int key, int size) {//求余数
return key % size;
}
/**
* 将节点加入到Hash表中
*/
public void add(int key) {
int add = Hash(key, size);// 先计算地址,判断该位置是否为空
Node node = array[add];
if (node == null) {// 为空,即可以加入该key值
array[add] = new Node();
array[add].setKey(key);
length++;
} else {// 不为空,用链地址法处理冲突
while (node.getNext() != null) {
node = node.getNext();
}
Node next = new Node();
node.setNext(next);
next.setKey(key);
length++;
}
}
/**
* 查找
* @param key
*/
public boolean find(int key) {
int add = Hash(key, size);// 计算地址
if (array[add].getKey() == key) {
return true;
} else {
Node next = array[add].getNext();
while (next != null) {
while (next.getKey() != key) {
next = next.getNext();
}
return true;
}
return false;
}
}
/**
* 改
*/
public void alter(int key, int cover) {
int index = Hash(key, size);
if (array[index].getKey() == key) {
array[index].setKey(cover);
} else {
Node next = array[index].getNext();
while (next.getKey() != key) {
next = next.getNext();
}
next.setKey(cover);
}
}
/**
* 判断是否超过阀值
*/
public void overP() {
for (int i = 0; i < size; i++) {
int num = 0;
if (array[i] != null) {
Node next = array[i].getNext();
while (next != null) {// 某一串,有下个节点
num++;
next = next.getNext();
}
}
if (num >= (0.7 * size)) {// 某一串的个数超过阀值
System.out.println("超过阀值,重新Hash----------------------->");
reHash();
}
break;
}
}
/**
* 再Hash
*/
private void reHash() {
time++;
newSize = time * size;
newArray = new Node[newSize];
for (int i = 0; i < size; i++) {
Node node = array[i];// 原数组
while (node != null) {
int add = Hash(node.getKey(), newSize);// 计算新地址
Node newNode = newArray[add];
if (newNode == null) {// 数组上为空
newArray[add] = new Node();
newArray[add].setKey(node.getKey());// 设置Key值
} else {// 数组该位置不为空,找下一个
while (newNode.getNext() != null) {
newNode = newNode.getNext();
}
Node newNext = new Node();
newNode.setNext(newNext);
newNode.setKey(node.getKey());
}
node = node.getNext();// /循环至下一个
}
}
size = newSize;
array = newArray;// 将新数组赋给原先数组
overP();
}
}
运行结果如下: