Hash(哈希算法)和Hash Table(散列表)

哈希算法,又称散列算法,将任意长度输入转化为固定长度输出。在实际应用中,如Java的HashCode,通过计算对象的hashcode并取模来存储和查找对象,提高了效率。当出现冲突时,引入桶的概念,用链表解决冲突,从而实现散列表。散列表是基于哈希算法的数据结构,能快速存取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哈希(Hash)原本是个人名,由于他提出哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来确定唯一的输入值。简单的说就是一种将任意内容的输入转换成相同长度输出的加密方式。

那我们在实际应用中怎么去使用呢?

我们利用预映射=》hash值来作文章。我们拿java来说明。

HashCode

首先是大家经常使用到的hashcode,hashcode是hash算法的一个简单实现,理论上它是内存地址生成的。现在我们就通过hashcode来延伸hash算法的作用。

存储

例如内存中有这样的位置
0 1 2 3 4 5  

如果我们有六个对象要分别存放在上面六个位置上面,正常的话,我们挨个去查看,位置上是否有东西存储了。这样的话,如果大数据量,遍历的消耗将非常高。但如果用Hash算法那就会使效率提高很多。比如我们通过计算hashcode%6的值,然后把我们的对象存放在取得余数的那个位置。比如我们的hashcode为1,1除6的余数为1,那么我们就把该对象存在1这个位置,如果hashcode是3,求得的余数是3,那么我们就把该对象放在3这个位置。

查找

这样,以后在查找该类时就可以通过hashcode除6求余数直接找到存放的位置了。

这样就会有一个问题,重复了怎么办。比如一个hashcode是7,余数也是1,但是1位置被占用了。 

这时候我们推出来一个桶的概念。首先通过hashcode进行equals判断,如果存在,就放弃对象,如果不存在,我们来维护这个桶。这个桶,我们可以以链表的形式组织。

比如:

class Node {
	private Object value;// the value of current node
	private Node next;// the next node of current node
	public Node(Object value) {
		this.value = value;
	}
	public Object getValue() {
		return value;
	}
	public Node getNext() {
		return next;
	}
	public void setNext(Node next) {
		this.next = next;
	}
}

node为桶里面的元素,这样我们存放一个对象的逻辑就为:

  1. 根据hashcode算出存储位置
  2. 查看该存储位置上是否存在元素(Node)
  3. 如果存在,则比较node的hashcode和当前对象的hashcode是否相等,如果相等,直接返回,不添加;如果不相等,查看当前node还有没有下一个node
  4. 如果存在下一个node,进行第三步操作;不存在,新增一个node,将对象设置到node的value属性上,返回。

这个链表就是一个桶。

Hash Table(散列表)

这边的Hash Table不是java中的HashTable类,而是泛指散列表。通过HashCode、存储、查找、桶等概念,我们就可以自己实现一个基于hash算法的hash table了。

一下是一个简单散列表代码:

package com.rick.hash;
/**
 * 
 * @author Rick Ping
 * Hash Table 
 */
public class HashArithmetic {
	private Node[] array;
	private int size =0;//the number of stored object  
	
	
	public HashArithmetic(int length) {
		//initialize the array size
		array = new Node[length];
	}

	private static int hash(Object o) { 
		//generate a optimal HashCode from the java object's HashCode
		// refer to hash() of java.util.HashMap
		int h = o.hashCode();
		h += ~(h << 9);
		h ^= (h >>> 14);
		h += (h << 4);
		h ^= (h >>> 10);
		return h;
	}

	private int indexFor(int hashCode) {
		//get the index from HashCode
		// refer to indexFor() of java.util.HashMap
		return hashCode & (array.length - 1);
	}
	/**
	 * store object by hash arithmetic
	 * @param value
	 */
	public void add(Object value) {
		//get index
		int index = indexFor(hash(value));

		Node node = array[index];
		Node newNode =new Node(value);
		if (node == null) {
			//add directly if this place is null
			array[index] = newNode;
			size ++;
		} else {
			//iterate this tub(linked list).
			while( !node.getValue().equals(value) && node.getNext() != null){
				node = node.getNext();
			}
			if(!node.getValue().equals(value)){
				//add node into liked list(tub)
				node.setNext(newNode);
				size++;
			}
		}
	}

	public boolean contains(Object value) {
		int index = indexFor(hash(value));
		Node node = array[index];
		while(node != null && !node.getValue().equals(value)) {
			node = node.getNext();
		}
		if(node != null && node.getValue().equals(value)){
			return true;
		}else{
			return false;
		}
	}

	public boolean remove(Object value) {
		int index = indexFor(hash(value));
		Node  root = array[index];
		Node parent = root;
		Node current = root;
		while(current != null && !current.getValue().equals(value)){
			parent = current;
			current = current.getNext();
		}
		if(current != null && current.getValue().equals(value)){
			if(parent.equals(root)){
				 array[index] = current.getNext();
				 
			}else{
				parent.setNext(current.getNext());
			}
			size--;
			return true;	
		}else{
			return false;
		}
		
	}
	public int size(){
		return size;
	}
	public static void main(String[] args) {
		HashArithmetic hashTable = new HashArithmetic(5); 
		Object [] values = {"Rick","Mike","Mike","Stanley","Williams"}; 
		for (int i = 0; i < values.length; i++) { 
			hashTable.add(values[i]); 
		} 
		
		System.out.println("Hash talbe size:"+hashTable.size()+",added item size:"+values.length);
		
		hashTable.remove("Williams"); 
		System.out.println("Hash talbe size after remove [Williams]:"+hashTable.size()); 
		
		System.out.println(hashTable.contains("Rick")); 
		System.out.println(hashTable.contains("Mike")); 
		System.out.println(hashTable.contains("Stanley"));
		System.out.println(hashTable.contains("Williams")); 
	}
}

class Node {
	private Object value;// the value of current node
	private Node next;// the next node of current node
	public Node(Object value) {
		this.value = value;
	}
	public Object getValue() {
		return value;
	}
	public Node getNext() {
		return next;
	}
	public void setNext(Node next) {
		this.next = next;
	}
}


运行结果如下:

Hash talbe size:4,added item size:5
Hash talbe size after remove [Williams]:3
true
true
true
false


以上就是个人学习、了解的Hash算法以及hash算法在数据结构-散列表上的应用


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值