一、你不了解哈希表

1. 什么是哈希表?

  • 我们已经知道了一个数据项关键字映射存储位置
  • 我们查找过程,其实就是比较,不断比较关键字,从而才可以找到存储位置。
  • 现在你应该可以发现,他们就像函数一样,X=关键字,Y=存储位置。
  • 哈希表的难点就在于找映射关系:f(x) = y
  • 称函数为:哈希函数。
  • 按这个思想建立的表为哈希表
  • 哈希表的优点:在于它不需要不断比较,而是直接找到,一次就好。
  • 黑体字是重点

2. 冲突

  • 函数的数学定义有一点:不允许一个X对应多个Y
  • 哈希函数也是函数,所以还剩下一种情况:多个X对应一个Y
  • 所以哈希表要解决的冲突:不同的关键字可能得到同一存储地址
  • 错误可以解决,冲突不能完全避免。我们通常要设定一种处理冲突的方法。

3. 关键名词

  • 哈希表:一个关键字对应一个储存位置,形成的表。
  • 存储位置 = 哈希地址 = 散列地址
  • 函数关系 = 映射关系 = 对应关系 = 哈希构造 = 散列
  • H(x) = y

二、你已经知道了哈希表

(一)、哈希函数的构造方法

- 目标:构造好的哈希函数。
- 好的哈希函数:关键字经过哈希函数得到一个“随机的地址”,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。

1. 直接定址法

H ( k e y ) = k e y 或 H ( k e y ) = a ∗ k e y + b H(key) = key 或 H(key) = a*key + b H(key)=keyH(key)=akey+b

2. 数字分析法

太 灵 活 了 , 知 道 有 这 种 方 法 就 行 了 太灵活了,知道有这种方法就行了

3. 平方取中法——重要
取 关 键 字 平 方 后 的 中 间 几 位 为 哈 希 地 址 取关键字平方后的中间几位为哈希地址

  • 如:
  • 在这里插入图片描述

4. 折叠法

将 关 键 字 分 割 成 相 同 的 几 部 分 ( 最 后 一 部 分 的 位 数 可 以 不 同 ) , 然 后 取 这 几 部 分 叠 加 和 ( 舍 去 进 位 ) 作 为 哈 希 地 址 将关键字分割成相同的几部分(最后一部分的位数可以不同),然后取这几部分叠加和(舍去进位)作为哈希地址

  • 折叠发适合关键字位数多,且每一位上的数字分布大致均匀。
  • 如:
  • 在这里插入图片描述

5. 除留余数法——重要

H ( k e y ) = k e y M O D p , p < = m H(key) = key MOD p, p <= m H(key)=keyMODp,p<=m
m 为 哈 希 表 的 表 长 m为哈希表的表长 m

  • 它可以与其他方法一起用。
  • p的选择很重要。

(二)、处理冲突的方法

1. 开放定址法——重要

H i = ( H ( k e y ) + d i ) M O D m Hi = (H(key) + di) MOD m Hi=(H(key)+di)MODm

  • H(key)为哈希函数。
  • m为哈希表表长。
  • di为增量序列。
  1. di = 1, 2, 3, …, m-1 公式被称为线性探测再散列
  2. di = 1^2, -1^2, 2^2, -2^2, …, k^2, -k^2 (k<=m/2) 公式被称为二次探测再散列
  3. di = 伪随机序列 公式被称为随机探测再散列
  • 如:
  • 在这里插入图片描述

2. 在哈希法
3. 链地址法
4. 建立一个公共溢出区

三、哈希表的查找的代码

(一)、哈希表的数据结构

// 开放定址哈希表的储存结构
int hashsize[] = {997, ... }; // 哈希表容量递增表,一个合适的素数序列
typedef struct {
	ElemType * elem; // 动态分配数组
	int count; // 当前元素个数
	int sizeindex; // hashsize[sizeindex]为当前容量
}HashTable;

(二)、哈希表的Java代码

  • 哈希表的构造:
	/**
	 * 除数取余法,构造哈希表
	 * 
	 * @param keys
	 * @param values
	 * @param hashTableLength 哈希表表长,素数
	 */
	public Search(int[] keys, String[] values, int hashTableLength) {
		length = hashTableLength;
		searchTable = new DataItem[length];
		for (int i = 0; i < length; i++) {
			searchTable[i] = null;
		}

		int tempPosition;
		for (int i = 0; i < keys.length; i++) {
			tempPosition = keys[i] % hashTableLength;

			while (searchTable[tempPosition] != null) {
				tempPosition = (tempPosition + 1) % hashTableLength;
				System.out.println("冲突, 向前移动关键字" + keys[i]);
			}

			searchTable[tempPosition] = new DataItem(keys[i], values[i]);
		}
	}
  • 哈希查找:
	/**
	 * 哈希查找
	 * 
	 * @param key
	 * @return
	 */
	public String hashSearch(int key) {
		int tempPosition = key % length;

		while (searchTable[tempPosition] != null) {
			if (searchTable[tempPosition].key == key) {
				return searchTable[tempPosition].value;
			}
			tempPosition = (tempPosition + 1) % length;
		}

		return null;
	} // hashSearch
  • 测试函数:
	public static void main(String[] args) {
		int[] key = { 1, 3, 5, 6, 7, 9, 10 };
		String[] values = { "if", "then", "else", "switch", "case", "for", "while" };
	
		Search search = new Search(key, values, 13);

		System.out.println(search.toString());
		System.out.println("Search result of 10 is: " + search.hashSearch(10));
	}

(三)、输出样例

  • Search类源码:
package search;

public class Search {

	/** 查找表 */
	DataItem[] searchTable;

	/** 表长度 */
	int length;

	class DataItem {

		/** 关键字域 */
		int key;

		/** 其他域 */
		String value;

		DataItem(int key, String value) {
			this.key = key;
			this.value = value;
		}

		public String toString() {
			return "[" + key + ", " + value + "]";
		}
	} // 数据项

	/**
	 * 构造查找表
	 * 
	 * @param keys
	 * @param values
	 */
	public Search(int[] keys, String[] values) {
		// TODO
		if (keys.length != values.length) {
			throw new RuntimeException("键 与 值不对应!");
		}
		length = keys.length;
		searchTable = new DataItem[length];

		for (int i = 0; i < length; i++) {
			searchTable[i] = new DataItem(keys[i], values[i]);
		}
	} // structure

	/**
	 * 除数取余法,构造哈希表
	 * 
	 * @param keys
	 * @param values
	 * @param hashTableLength 哈希表表长,素数
	 */
	public Search(int[] keys, String[] values, int hashTableLength) {
		length = hashTableLength;
		searchTable = new DataItem[length];
		for (int i = 0; i < length; i++) {
			searchTable[i] = null;
		}

		int tempPosition;
		for (int i = 0; i < keys.length; i++) {
			tempPosition = keys[i] % hashTableLength;

			while (searchTable[tempPosition] != null) {
				tempPosition = (tempPosition + 1) % hashTableLength;
				System.out.println("冲突, 向前移动关键字" + keys[i]);
			}

			searchTable[tempPosition] = new DataItem(keys[i], values[i]);
		}
	}

	/**
	 * 打印查找表
	 */
	public String toString() {
		String resultString = "I am a search table with " + length + " data items.\r\n";
		for (int i = 0; i < length; i++) {
			resultString += searchTable[i] + " ";
		}
		return resultString;
	}// toString

	/**
	 * 顺序查找
	 * 
	 * @param key
	 * @return key相对应的值
	 */
	public String sequentialSearch(int key) {
		// TODO
		searchTable[0].key = key; // "哨兵"
		int i;
		for (i = length - 1; searchTable[i].key != key; i--)
			;
		return searchTable[i].value;
	} // sequentialSearch

	/**
	 * 折半查找
	 * 
	 * @param key
	 * @return
	 */
	public String binarySearch(int key) {
		// TODO
		int low = 0;
		int high = length - 1;
		int mid = (low + high) / 2;

		while (low <= high) {
			mid = (low + high) / 2;
			if (searchTable[mid].key == key) {
				return searchTable[mid].value;
			} else if (searchTable[mid].key < key) {
				low = mid + 1;
			} else {
				high = mid - 1;
			}
		} // Of while

		return null;
	}// binarySearch

	/**
	 * 哈希查找
	 * 
	 * @param key
	 * @return
	 */
	public String hashSearch(int key) {
		int tempPosition = key % length;

		while (searchTable[tempPosition] != null) {
			if (searchTable[tempPosition].key == key) {
				return searchTable[tempPosition].value;
			}
			tempPosition = (tempPosition + 1) % length;
		}

		return null;
	} // hashSearch

	public static void main(String[] args) {
		// TODO
		System.out.println("\r\n-------sequentialSearchTest-------");
		// 1 声明
		int[] keys1 = { -1, 5, 3, 6, 10, 7, 1, 9 };
		String[] values1 = { "null", "if", "then", "else", "switch", "case", "for", "while" };
		// 2 对象
		Search search1 = new Search(keys1, values1);
		// 3 打印
		System.out.println(search1.toString());
		// 4 测试
		long startTime1 = System.nanoTime(); // 获取开始时间
		System.out.println("Search result of 10 is: " + search1.sequentialSearch(10));
		long endTime1 = System.nanoTime(); // 获取结束时间
		System.out.println("查找时间(纳秒): " + (endTime1 - startTime1));

		System.out.println("\r\n-------binarySearchTest-------");
		// 1 声明
		int[] keys2 = { 1, 3, 5, 6, 7, 9, 10 };
		String[] values2 = { "if", "then", "else", "switch", "case", "for", "while" };
		// 2 对象
		Search search2 = new Search(keys2, values2);
		// 3 打印
		System.out.println(search2.toString());
		// 4 测试
		long startTime2 = System.nanoTime(); // 获取开始时间
		System.out.println("Search result of 10 is: " + search2.binarySearch(10));
		long end2Time2 = System.nanoTime(); // 获取结束时间
		System.out.println("查找时间(纳秒): " + (end2Time2 - startTime2));

		System.out.println("\r\n-------hashSearchTest-------");
		// 1 声明
		int[] key3 = { 1, 3, 5, 6, 7, 9, 10 };
		String[] values3 = { "if", "then", "else", "switch", "case", "for", "while" };
		// 2 对象
		Search search3 = new Search(key3, values3, 13);
		// 3 打印
		System.out.println(search3.toString());
		// 4 测试
		long startTime3 = System.nanoTime(); // 获取开始时间
		System.out.println("Search result of 10 is: " + search3.hashSearch(10));
		long endTime3 = System.nanoTime(); // 获取结束时间
		System.out.println("查找时间(纳秒): " + (endTime3 - startTime3));
	}

} // Search
  • 输出:

在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐