哈希表——线性探测

本文深入探讨了哈希表这一高效数据结构的工作原理,包括其如何通过关键码值进行快速访问,以及开放地址法之线性探测的冲突解决策略。文章详细介绍了哈希表的插入、查找和删除操作,并通过具体代码示例展示了哈希映射函数的使用。

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

/**
 * 哈希表是根据关键码值来进行访问的数据结构
 * 也就是说 他通过关键码值映射到表中的一个位置来访问记录,来加快查找的速度,
 * 这个映射叫做散列函数,这个存放记录的数组叫做散列表
 * 1、哈希表可以快速的插入,查找,删除
 * 2、哈希表基于数组实现,一旦确定,不容易维护
 * 3、没有一种简单的方式实现遍历,因此若是需要这个功能,那么需要选择其他的数据结构
 * @author hp
 *
 */

public class DataItem {
  private int iData;
  public DataItem(int i){
	  iData = i;
  }
  public int getkey(){
	  return iData;
  }
}



public class HashTable {
/**
 * 完成哈希存储的查找的功能
 * 开放地址法之线性探测
 * 线性探测就是按照当前的位置冲突,它会按照数组的下标一步步的查找空白的单元
 * 缺点:当哈希表越来越满的时候,聚集会变的越来越严重,这会导致产生非常长的探测长度
 * 意味着存储序列的最会单元会非常耗时
 */
	private DataItem [] hashArray; 
	private int arraySize;
	private DataItem nonItem;
	//hashtabe 的初始化
	public HashTable(int size){
	  arraySize = size;
	  hashArray = new DataItem[arraySize];
	  nonItem = new DataItem(-1);
	}
	//用来遍历整个哈希表
	public void displayTable(){
		System.out.println("table: ");
		for(int i=0;i<hashArray.length;i++){
			if(hashArray[i]!=null){
				System.out.println(hashArray[i].getkey()+" ");
			}else{
				System.out.println("** ");
			}
		}
	}
	//hash映射函数
	public int hashFun(int key){
		return key%arraySize;
	}
	//哈希插入
	public void insert(DataItem item){
		int key = item.getkey();  //将存储对象的数据的键值传递给映射函数,得到数字的下标
		int hashVal= hashFun(key);
		
		while(hashArray[hashVal] !=null && hashArray[hashVal].getkey() !=-1){ //判断得到的位置上有没有数据,如果有数据就执行此循环
			++hashVal;
			hashVal %=arraySize;
		}
		hashArray[hashVal] = item;
	}
	//哈希删除
	public DataItem delete(int key){
		int hashVal = hashFun(key);
		while(hashArray[hashVal] != null ){ //如果key的位置有数据
			if(hashArray[hashVal].getkey() == key){
				DataItem temp  = hashArray[hashVal]; //取走数据以后将这个位置上的数据设置为空
				hashArray[hashVal] = nonItem;
				return temp;  //返回key位置的数据
			}
			++hashVal;    //如果这个位置上数据不是要找的数据,就向前遍历这个数据
			hashVal%=arraySize;
		}
		return null;
	}
	
	//哈希查找
	public DataItem find(int key){
		int hashVal = hashFun(key);
		while(hashArray[hashVal]!=null){
			if(hashArray[hashVal].getkey() == key){
				return hashArray[hashVal];
			}
			++hashVal;
			hashVal%=arraySize;
		}
		return null;
	}
	//测试
	public static void main(String[] args) {
		HashTable hs = new HashTable(5);
		DataItem i1 = new DataItem(2);
		hs.insert(i1);
		DataItem i2 = new DataItem(1);
		hs.insert(i2);
		DataItem i3 = new DataItem(4);
		hs.insert(i3);
		DataItem i4 = new DataItem(3);
		hs.insert(i4);
		hs.displayTable();
		System.out.println(hs.delete(0));
		
	}
}

 

### 哈希表中的线性探测与拉链法 #### 线性探测法概述 当发生哈希冲突时,线性探测是一种简单的解决方案。该方法从初始冲突位置开始,依次向后寻找下一个可用的空位来存储新元素[^2]。具体来说,对于键`key`,其原始哈希值为`hash0 = key % M`;一旦发现`hash0`已被占用,则按照公式`hc(key, i) = (hash0 + i) % M`继续尝试插入,其中`i`是从1递增至`M-1`的整数值。 然而,这种方法存在明显的缺点——容易形成所谓的“群集”或“堆积”,即多个连续的位置被占据,使得后续插入操作不得不跳过这些区域去寻找更远的地方放置数据项,从而降低了性能效率。 ```python def linear_probe_insert(hash_table, size, value): index = value % size original_index = index while True: if hash_table[index] is None or hash_table[index] == 'DELETED': break # 如果遍历到了数组末尾则回到开头重新找 index = (index + 1) % size if index == original_index: raise Exception('Hash table full') hash_table[index] = value ``` #### 拉链法简介 相比之下,拉链法则通过创建一个额外的数据结构(通常是链表)来管理具有相同索引的不同记录。每当遇到新的条目想要存入某个已经被其他对象使用的槽位时,不是像在线性探查那样四处移动寻找空间,而是简单地把这个新成员附加到对应链接末端即可[^1]。 这种方式有效地解决了因频繁碰撞而导致的空间浪费问题,并且由于每条链条独立运作互不影响,因此即使某些特定桶变得非常拥挤也不会显著影响整体查询速度。不过值得注意的是,在最坏的情况下(比如几乎所有的输入都映射到了同一个bucket上),时间复杂度仍会退化成O(n),这里n代表了总节点数目。 ```python class LinkedListNode: def __init__(self, val=None): self.val = val self.next = None class HashTableWithChaining: def __init__(self, capacity=8): self.capacity = capacity self.table = [None] * self.capacity def insert(self, value): idx = value % self.capacity node = LinkedListNode(value) if not self.table[idx]: self.table[idx] = node else: current = self.table[idx] while current.next: current = current.next current.next = node ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值