有关哈希和队列的基础问题

什么是哈希

哈希是指将任意长度的输入通过哈希函数转换成固定长度的输出的过程或结果。哈希函数是一种算法,它将输入数据映射到唯一的输出值,这个输出通常被称为哈希值或散列值。

哈希函数具有以下特点:

  1. 固定长度输出:无论输入数据的大小,哈希函数的输出长度是固定不变的。常见的哈希函数如 MD5、SHA-1、SHA-256 都有不同长度的输出。
  2. 唯一性:不同的输入会得到不同的输出。即使输入数据只有微小的变化,哈希值也会有显著变化。
  3. 不可逆性:从哈希值无法还原出原始输入数据。这意味着无法通过哈希值逆向推导出原始数据的内容。
  4. 雪崩效应:输入数据的微小变化会导致哈希值的明显变化,即"雪崩效应"。这意味着即使输入数据只有一个比特的变化,哈希值也会发生巨大的改变。

现在假设数组array存放的是1到15这些数,现在要存在一个大小是7的Hash表中,该如何存呢?我们存储的位置计算公式是index=number%7,则最终会得到下面这样:

我们可以看到哈希表要想实现随机长度输入固定长度输出的话,那么每个位置存的值就不止一个。假如我要测试13在不在这里结构里,则同样使用上面的公式来进行,很明显13模7=6,我们直接访问array[6]这个位置,很明显是在的,所以返回true。假如我要测试20在不在这里结构里,则同样使用上面的公式来进行,很明显20模7=6,我们直接访问array[6]这个位置,但是只有6和13,所以返回false。这就是简单的哈希表存储和查找的过程。

哈希碰撞

我们会发现不同的值经过哈希运算可能会等到相同的值,这就导致在哈希表中一个位置存了多个值,我们管这种现象叫做哈希碰撞或哈希冲突。对于数组来说一个位置肯定不能存多个值的,这个该如何解决呢?这里介绍两种常见的方法:

  1. 链地址法(Java里的ConcurrentHashMap):链地址法是将哈希表中具有相同哈希值的元素,用链表连接起来存储到同一个桶(bucket)中。 当哈希函数将不同的元素映射到同一个位置时,我们只需要在相应的桶中连接新元素即可(即进行链表插入操作。

链地址法的优点是实现简单,可以动态地调整哈希表的大小,对于散列分布比较均匀的数据,效率通常较高。缺点是需要额外的空间来存储链表指针,空间开销比较大,并且当链表变得过长时,会降低哈希表的查找效率。

  1. 开放地址法(Java里的Threadloca):开放地址法是将哈希冲突的元素依次存储到哈希表中的其他空闲位置。当哈希函数将元素映射到某个桶中时,如果该桶已经被占用,算法就会寻找下一个可用桶,直到找到一个空闲位置为止。

开放地址法的优点是没有链表指针,空间利用率高,并且对于小型表格比链地址法联盟更快。其缺点在于实现复杂,删除元素也更加困难,并且当哈希表填满或者哈希冲突较多时,性能下降较快,查找速度会变得非常缓慢。

另外还有再哈希法(布隆过滤器)、建立公共溢出区这些反方法。

注:虽然哈希函数设计上应该尽量避免碰撞,但在实际应用中,由于输入空间大于输出空间,因此无法完全避免碰撞的发生。

什么是队列

队列是一种常见的数据结构,它按照先进先出(FIFO)的原则管理数据。它类似于日常生活中的排队,首先进入队列的元素会首先被处理或移除,而最后进入队列的元素则会被推迟处理。

队列实现

使用链表结构实现队列:

队列由两个主要操作组成:

  1. 入队(push):将新元素添加到队列的末尾。
  2. 出队(pull):从队列的头部移除并返回队列中的第一个元素。

除了这两个基本操作,队列还常常包含以下辅助操作:

  1. 判空(isEmpty):检查队列是否为空。
  2. 队首元素(front):获取队列的第一个元素,但不将其移除。
  3. 队列长度(size):获取队列中元素的个数。
public class LinkQueue{
	private Node = front;
	private Node = rear;
	private int = size;
	public LinkQueue(){
		this.front = new Node(0);
		this.rear = new Node(0);
	}

	//入队
	public void push(int value){
		Node newNode = new Node(value);
		Node temp = front;
		while (temp.next != null){
			temp = temp.next;
		}
		temp.next = newNode;
		rear = newNode;
		size++;
	}

	//出队

	public int pull(){
		if (front.next =null){
			System.out.println("队列已空");
		}
		Node firstNode = front.next;
		front.next = firstNode.next;
		size--;
		return firstNode.data;
	}
	//遍历队列
	public void traverse(){
		Node temp = front.next;
		while (temp !=null){
			System.out.print(temp.data +"\t");
			temp = temp.next;
		}
	}
	static class Node{
		public int = data;
		public Node - next;
		public Node(int data){
			this.data = data;
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值