Hash表是什么?
Hash表是一种数据结构,可以提供快速的插入操作和查找操作。
优点:运算速度快,查找操作比树块。
缺点:基于数组的,创建后难以扩展,而且当Hash表基本被填满时,性能下降很严重,此外,Hash表无法顺序遍历。
如果不需要遍历数据,并且可以提前预测数据量大小,Hash表非常合适。
解决冲突的方法:
开放地址法:
包括线性探测、二次探测和再哈希法。
线性探测,就是冲突后移动到相邻的下一个位置,直到找到空位。此方法的缺点是数据容易聚集,使得聚集的数据范围越来越多。
二次探测:解决数据聚集的问题。二次探测中,冲突后移动的步数依次是x+i2,其中x为哈希运算值,i为依次去1,2,3,4……直到找到空位。二次探测消除了线性探测的聚集问题(原始聚集),但是却产生了另外一种聚集问题(二次聚集),因为二次探测算法产生的探测序列总是固定的。
再哈希法:为了消除原始聚集和二次聚集所提出的方法。再哈希法利用一个哈希函数来根据位置来改变步长。再哈希法要求表的容量是一个质数,防止函数只会循环探查几个固定的区域,找不到空位而崩溃。
链地址法: 在每个哈希表中国设置链表。
哈希函数:
哈希函数使用频繁,因此惩罚和除法不可取(可以采用移位操作)。
哈希函数的目的是得到关键字值的范围,用一种方法成数组的下标,哈希函数应该随机分布在整个哈希表。
随机关键字:index = key%arraySize,效果较好
非随机关键字:对于非随机分布的数据(如车牌、身份证号等),因为此类数据会集中在某一范围,应该采用合适的哈希法使结果随机分布在数组内。
几点要求:
不要使用无用数据
要体现所有数据
使用质数作为取模的技术
哈希化字符串:
将短小的字符串转换为数字,方法是每个数位乘以一个对应的常数的幂值。
哈希表解决冲突方法对比
方法 |
查找/插入 |
装填因子增大 |
线性探测 |
(1+1/(1-loadFactor)2)/2(成功查找) (1+1/(1-loadFactor))/2 (不成功查找) |
性能下降很严重(指数增长) |
二次探测 |
Log2(1-loadFactor)/ loadFactor(成功查找) 1/(1-loadFactor)(不成功查找) |
性能下降较严重(指数增长) |
再哈希法 |
Log2(1-loadFactor)/ loadFactor(成功查找) 1/(1-loadFactor)(不成功查找) |
性能下降较严重(指数增长) |
链地址法 |
1+ loadFactor/2(无序) 1+loadFactor(有序) |
线性增长 |
1.开放地址法:容易产生堆积问题;不适于大规模的数据存储;对于小型的哈希表,再哈希法效果较好。如果装填因子低于0.5,线性探测更容易实现,而且性能几乎不下降。
2.链地址法:处理冲突简单,且无堆积现象,平均查找长度短;适合填入项数未知的情况。
哈希表知识点汇总:
- 哈希表基于数组
- 关键字值范围比数组容量大
- 关键字值通过哈希函数映射为数组下标
- 一个关键字哈希化到已占用的数组单元,叫做冲突
- 冲突解决方法:开发地址法和链地址法
- 开放地址法中,冲突的数据放在数组的其他位置
- 开放地址法包括:线性探测、二次探测和再哈希法
- 找到一个特定项所需要的步数叫做探测长度
- 线性探测步长总是为1
- 线性探测中,会出现首次聚集
- 二次探测中,步长是步数的平方,能够消除首次聚集,但是产生二次聚集
- 二次聚集的危害相对小,造成二次聚集的原因是是步长只依赖与哈希值,与关键字无关
- 再哈希法中,步长通过第二个哈希函数得到,依赖于关键字,能够消除二次聚集
- 装填因子=数据项/数组容量
- 开放地址法最大装填因子应该在0.5附近,采用再哈希法查找探测长度是2.0
- 开放地址法中,装填因子接近1,查找时间接近无限,因此·采用开放地址法中哈希表不能太满
- 链地址法中,每个数组单元包含一个链表,相同位置的数据都插入此链表
- 链地址法中,装填因子为1比较合适,此时成功的探测长度是1.5,不成功是2.0
- 链地址法探测长度随着装填因子变大线性增长
- 字符串哈希化,每个字符乘以常数不同次幂,求和,去模(防止溢出)
- 哈希表的熔炼通常是个质数,这在二次探测和再哈希法中很重要
Hash表的实现:
线性地址法:
1 package dataStructure; 2 //线性地址法 3 import java.util.Scanner; 4 5 class DataItem{ 6 private int iData; //key 7 public DataItem(int data){//构造 8 iData = data; 9 } 10 public int getKey(){ 11 return iData; 12 } 13 } 14 15 //线性探测法 16 public class HashTable { 17 18 19 private DataItem[] hashArray; //hash表 20 private int arraySize; //表大小 21 private DataItem nonItem; //空节点 22 23 public HashTable(int size){//构造 24 arraySize = size; 25 hashArray = new DataItem[arraySize]; 26 nonItem = new DataItem(-1);//删除的节点置为-1 27 } 28 29 public void displayTable(){ 30 System.out.print("Table: "); 31 for(int j=0; j<arraySize; j++){ 32 if(hashArray[j] != null) 33 System.out.print(hashArray[j].getKey() + " "); 34 else 35 System.out.print("** "); 36 } 37 System.out.println(" "); 38 } 39 40 public int hashFunc(int key){ 41 return key % arraySize; //哈希函数 42 } 43 44 public DataItem find(int key){ 45 int hashVal = hashFunc(key); 46 47 while(hashArray[hashVal] != null){ 48 if(hashArray[hashVal].getKey() == key) 49 return hashArray[hashVal]; 50 ++hashVal; 51 hashVal %= arraySize; 52 } 53 return null; 54 } 55 56 public void insert(DataItem item){ 57 int key = item.getKey(); 58 int hashVal = hashFunc(key); 59 60 while(hashArray[hashVal] != null && hashArray[hashVal].getKey() != -1){ 61 ++hashVal; 62 hashVal %= arraySize; 63 } 64 hashArray[hashVal] = item; 65 } 66 67 public DataItem delete(int key){ 68 int hashVal = hashFunc(key); 69 while(hashArray[hashVal] != null){ 70 if(hashArray[hashVal].getKey() == key){ 71 DataItem temp = hashArray[hashVal]; 72 hashArray[hashVal] = nonItem; 73 return temp; //弹出删除的item 74 } 75 ++hashVal; 76 hashVal %= arraySize; 77 } 78 return null; 79 } 80 81 82 /** 83 * @param args 84 */ 85 public static void main(String[] args) { 86 87 int aKey, size, n, keysPerCell; 88 System.out.println("Enter size of hash table: "); 89 Scanner out = new Scanner(System.in); 90 size = out.nextInt(); 91 System.out.println("Enter initial number of items: "); 92 n = out.nextInt(); 93 keysPerCell = 10; 94 HashTable theHashTable = new HashTable(size); 95 96 for(int j=0; j<n; j++){ //插入元素 97 DataItem aDataItem; 98 aKey = (int) (Math.random() * keysPerCell * size); 99 aDataItem = new DataItem(aKey); 100 theHashTable.insert(aDataItem); 101 } 102 103 while(true){ 104 System.out.println("Enter first letter of "); 105 System.out.println("show, intsert, delete, or find: "); 106 Scanner in = new Scanner(System.in); 107 char choice = in.next().charAt(0); 108 switch(choice){ 109 case 's': 110 theHashTable.displayTable(); 111 break; 112 case 'i': 113 System.out.print("Enter key value to insert: "); 114 Scanner keyIn = new Scanner(System.in); 115 aKey = keyIn.nextInt(); 116 theHashTable.insert(new DataItem(aKey)); 117 break; 118 case 'd': 119 System.out.print("Enter key value to delete: "); 120 Scanner keyDel = new Scanner(System.in); 121 aKey = keyDel.nextInt(); 122 theHashTable.delete(aKey); 123 break; 124 case 'f': 125 System.out.print("Enter key value to find: "); 126 Scanner keyFind = new Scanner(System.in); 127 aKey = keyFind.nextInt(); 128 DataItem aDataItem; 129 aDataItem = theHashTable.find(aKey); 130 if(aDataItem != null){ 131 System.out.print("Found " + aKey); 132 } 133 else 134 System.out.print("Could not find " + aKey); 135 break; 136 default: 137 System.out.print("Invalid entry "); 138 } 139 } 140 } 141 142 }
再哈希法:
1 package dataStructure; 2 3 import java.util.Scanner; 4 //再hash法 5 6 public class HashTable2 { 7 private DataItem[] hashArray; //hash表 8 private int arraySize; //表大小 9 private DataItem nonItem; //空节点 10 11 public HashTable2(int size){//构造 12 arraySize = size; 13 hashArray = new DataItem[arraySize]; 14 nonItem = new DataItem(-1);//删除的节点置为-1 15 } 16 17 public void displayTable(){ 18 System.out.print("Table: "); 19 for(int j=0; j<arraySize; j++){ 20 if(hashArray[j] != null) 21 System.out.print(hashArray[j].getKey() + " "); 22 else 23 System.out.print("** "); 24 } 25 System.out.println(" "); 26 } 27 28 public int hashFunc(int key){ 29 return key % arraySize; //哈希函数 30 } 31 32 public int hashFunc2(int key){ 33 return 5-key%5; 34 } 35 36 public void insert(DataItem item){ 37 int key = item.getKey(); 38 int hashVal = hashFunc(key); 39 int stepSize = hashFunc2(key); 40 41 while(hashArray[hashVal] != null && hashArray[hashVal].getKey() != -1){ 42 hashVal += stepSize; 43 hashVal %= arraySize; 44 } 45 hashArray[hashVal] = item; 46 } 47 48 public DataItem delete(int key){ 49 int hashVal = hashFunc(key); 50 int stepSize = hashFunc2(key); 51 52 while(hashArray[hashVal] != null){ 53 if(hashArray[hashVal].getKey() == key){ 54 DataItem temp = hashArray[hashVal]; 55 hashArray[hashVal] = nonItem; 56 return temp; //弹出删除的item 57 } 58 hashVal += stepSize; 59 hashVal %= arraySize; 60 } 61 return null; 62 } 63 64 public DataItem find(int key){ 65 int hashVal = hashFunc(key); 66 int stepSize = hashFunc2(key); 67 68 while(hashArray[hashVal] != null){ 69 if(hashArray[hashVal].getKey() == key) 70 return hashArray[hashVal]; 71 hashVal += stepSize; 72 hashVal %= arraySize; 73 } 74 return null; 75 } 76 77 public static void main(String[] args) { 78 79 int aKey, size, n, keysPerCell; 80 System.out.println("Enter size of hash table: "); 81 Scanner out = new Scanner(System.in); 82 size = out.nextInt(); 83 System.out.println("Enter initial number of items: "); 84 n = out.nextInt(); 85 keysPerCell = 10; 86 HashTable2 theHashTable = new HashTable2(size); 87 88 for(int j=0; j<n; j++){ //插入元素 89 DataItem aDataItem; 90 aKey = (int) (Math.random() * keysPerCell * size); 91 aDataItem = new DataItem(aKey); 92 theHashTable.insert(aDataItem); 93 } 94 95 while(true){ 96 System.out.println("Enter first letter of "); 97 System.out.println("show, intsert, delete, or find: "); 98 Scanner in = new Scanner(System.in); 99 char choice = in.next().charAt(0); 100 switch(choice){ 101 case 's': 102 theHashTable.displayTable(); 103 break; 104 case 'i': 105 System.out.print("Enter key value to insert: "); 106 Scanner keyIn = new Scanner(System.in); 107 aKey = keyIn.nextInt(); 108 theHashTable.insert(new DataItem(aKey)); 109 break; 110 case 'd': 111 System.out.print("Enter key value to delete: "); 112 Scanner keyDel = new Scanner(System.in); 113 aKey = keyDel.nextInt(); 114 theHashTable.delete(aKey); 115 break; 116 case 'f': 117 System.out.print("Enter key value to find: "); 118 Scanner keyFind = new Scanner(System.in); 119 aKey = keyFind.nextInt(); 120 DataItem aDataItem; 121 aDataItem = theHashTable.find(aKey); 122 if(aDataItem != null){ 123 System.out.print("Found " + aKey); 124 } 125 else 126 System.out.print("Could not find " + aKey); 127 break; 128 default: 129 System.out.print("Invalid entry "); 130 } 131 } 132 } 133 }
链地址法:
1 package dataStructure; 2 3 import java.util.Scanner; 4 5 //链地址法 6 class Link{ 7 private int iData; 8 public Link next; 9 public Link(int data){ 10 iData = data; 11 } 12 13 public int getKey(){ 14 return iData; 15 } 16 17 public void displayLink(){ 18 System.out.print(iData + " "); 19 } 20 21 } 22 23 class SortedList{ 24 private Link first; 25 public void sortedList(){ 26 first = null; 27 } 28 29 public void insert(Link theLink){ 30 int key = theLink.getKey(); 31 Link previous = null; 32 Link current = first; 33 34 while(current != null && key > current.getKey()){//找到要插入的位置 35 previous = current; 36 current = current.next; 37 } 38 if(previous == null)//如果链表为空,放在第一个位置 39 first = theLink; 40 else 41 previous.next = theLink; 42 theLink.next = current; 43 } 44 45 public void delete(int key){ 46 Link previous = null; 47 Link current = first; 48 49 while(current != null && key != current.getKey()){//移动到要删除的位置 50 previous = current; 51 current = current.next; 52 } 53 54 if(previous == null)//要删除的是第一个 55 first = first.next; 56 else 57 previous.next = current.next; 58 } 59 60 public Link find(int key){ 61 Link current = first; 62 63 while(current != null && current.getKey() <= key){ 64 if(current.getKey() == key) 65 return current; 66 current = current.next; 67 } 68 return null; 69 } 70 71 public void displayList(){ 72 System.out.println("List first to last:"); 73 Link current = first; 74 while(current != null){ 75 current.displayLink(); 76 current = current.next; 77 } 78 System.out.println(); 79 } 80 } 81 82 public class HashTable3 { 83 private SortedList[] hashArray; 84 private int arraySize; 85 86 public HashTable3(int size){ 87 arraySize = size; 88 hashArray = new SortedList[arraySize]; 89 for(int j=0; j<arraySize; j++) 90 hashArray[j] = new SortedList(); 91 } 92 93 public void displayTable(){ 94 for(int j=0; j<arraySize; j++){ 95 System.out.print(j + " ");//打印号 96 hashArray[j].displayList(); 97 } 98 } 99 100 public int hashFunc(int key){ 101 return key%arraySize; 102 } 103 104 public void insert(Link theLink){ 105 int key = theLink.getKey(); 106 int hashVal = hashFunc(key); 107 hashArray[hashVal].insert(theLink); 108 } 109 110 public void delete(int key){ 111 int hashVal = hashFunc(key); 112 hashArray[hashVal].delete(key); 113 } 114 115 public Link find(int key){ 116 int hashVal = hashFunc(key); 117 Link theLink = hashArray[hashVal].find(key); 118 return theLink; 119 } 120 121 public static void main(String[] args) { 122 123 int aKey, size, n, keysPerCell; 124 System.out.println("Enter size of hash table: "); 125 Scanner out = new Scanner(System.in); 126 size = out.nextInt(); 127 System.out.println("Enter initial number of items: "); 128 n = out.nextInt(); 129 keysPerCell = 10; 130 HashTable3 theHashTable = new HashTable3(size); 131 132 for(int j=0; j<n; j++){ //插入元素 133 Link aDataItem; 134 aKey = (int) (Math.random() * keysPerCell * size); 135 aDataItem = new Link(aKey); 136 theHashTable.insert(aDataItem); 137 } 138 139 while(true){ 140 System.out.println("Enter first letter of "); 141 System.out.println("show, intsert, delete, or find: "); 142 Scanner in = new Scanner(System.in); 143 char choice = in.next().charAt(0); 144 switch(choice){ 145 case 's': 146 theHashTable.displayTable(); 147 break; 148 case 'i': 149 System.out.print("Enter key value to insert: "); 150 Scanner keyIn = new Scanner(System.in); 151 aKey = keyIn.nextInt(); 152 theHashTable.insert(new Link(aKey)); 153 break; 154 case 'd': 155 System.out.print("Enter key value to delete: "); 156 Scanner keyDel = new Scanner(System.in); 157 aKey = keyDel.nextInt(); 158 theHashTable.delete(aKey); 159 break; 160 case 'f': 161 System.out.print("Enter key value to find: "); 162 Scanner keyFind = new Scanner(System.in); 163 aKey = keyFind.nextInt(); 164 Link aDataItem; 165 aDataItem = theHashTable.find(aKey); 166 if(aDataItem != null){ 167 System.out.print("Found " + aKey); 168 } 169 else 170 System.out.print("Could not find " + aKey); 171 break; 172 default: 173 System.out.print("Invalid entry "); 174 } 175 } 176 } 177 }