1.哈希查询算法由两部分组成
哈希函数将要查询的键(key)转换成数组的角标。
面对的问题:多个不同的键(key)经过hash之后转换成相同数组角标
有两种方案解决冲突:各自的链(separate chaining)、直线的探测(linear probing)
2.单独的链
两个或多个键经过哈希函数得到相同的角标值。将这些碰撞的项链接到单独的链表中,这种方法称之为单独的链(separate chaining)。
(1) 通过hash函数发现那个列表包含key;
(2)按照顺序列表的顺序查询key;
3.直线的探查
另一个实现哈表的方法是将N个键值对存放在一个长度为M(M>N)的哈希表中,依靠表中的空元素项去解决冲突。这样的方法被称为**开放寻址(open-addressing)**的哈希方法。
最简单的开发寻址方法被称之为直线的探查(linear probing): 当这儿有一个冲突(通过哈希函数得到的表格角标已经被其他不同的key所占用),那么我们就需要检查表格中下一个项(将角标加1)。
**直线的探查(linear probing)**会遇到三种情况:
键等于查询的键:键存在
空位置(这个角标上没有键):没有查到
键不等于查询的键:试着查询下一个
该算法的实现思路如下:
通过哈希函数计算出键(key0)所在表格的角标,检查查询到的键(key)是否和刚做哈希函数的键(key0)相等,继续(通过增加角标,当达到表格结尾的时候就返回表格的头)直到查到键(key0)或者查到一个空的表格项。
HashFunctionTest.java
package com.hef.algorithms.chapter3.item34;
/**
* @Date 2020/2/5
* @Author lifei
*/
public class HashFunctionTest {
private static final int R = 31;
public static void main(String[] args) {
int s = stringHashTestFive("S");
System.out.println(s);
System.out.println(("S".hashCode()&0x7fffffff)%5);
}
private static int stringHashTestFive(String str){
return stringHashTest(str, 5);
}
private static int stringHashTest(String str, int M){
int hash = 0;
for (int i=0;i<str.length();i++){
hash = (R+str.charAt(i))%M;
}
return hash;
}
}
LinearProbingHashST.java
package com.hef.algorithms.chapter3.item34;
/**
* @Date 2020/2/7
* @Author lifei
*/
public class LinearProbingHashST<Key, Value> {
// number of key-value pairs in the table
private int N;
// size of linear-probing table
private int M = 16;
// the keys
private Key[] keys;
// the values
private Value[] vals;
public LinearProbingHashST(){
keys = (Key[]) new Object[M];
vals = (Value[]) new Object[M];
}
private LinearProbingHashST(int cap){
keys = (Key[]) new Object[cap];
vals = (Value[]) new Object[cap];
}
private int hash(Key key){
return (key.hashCode() & 0x7fffffff) % M;
}
//
private void resize(int cap){
LinearProbingHashST<Key, Value> t;
t = new LinearProbingHashST<>(cap);
for (int i = 0; i < M; i++) {
if (keys[i]!=null){
t.put(keys[i], vals[i]);
}
}
keys = t.keys;
vals = t.vals;
M = t.M;
}
public void put(Key key, Value val){
// double M
if (N >= M/2) resize(2*M);
int i;
for (i = hash(key); keys[i]!=null; i = (i+1) % M) {
if (keys[i].equals(key)){
vals[i] = val;
return;
}
}
keys[i] = key;
vals[i] = val;
N++;
}
public Value get(Key key){
for (int i=hash(key); keys[i]!=null; i = (i+1)%M){
if (keys[i].equals(key)){
return vals[i];
}
}
return null;
}
public boolean contains(Key key){
for (int i = hash(key); keys[i]!=null; i=(i+1)%M) {
if (keys[i].equals(key)){
return true;
}
}
return false;
}
public void delete(Key key){
if (!contains(key)) return;
int i = hash(key);
while (!key.equals(keys[i])){
i = (i+1)%M;
}
keys[i] = null;
vals[i] = null;
i = (i+1)%M;
while (keys[i]!=null){
Key keyToRedo = keys[i];
Value valToRedo = vals[i];
keys[i] = null;
vals[i] = null;
N--;
put(keyToRedo, valToRedo);
i = (i+1)%M;
}
N--;
if (N>0 && N==M/8) resize(M/2);
}
}
SeparateChainingHashST.java
package com.hef.algorithms.chapter3.item34;
import java.util.Iterator;
/**
* @Date 2020/2/6
* @Author lifei
*/
public class SeparateChainingHashST<Key, Value> {
// number of key-value paris
private int N;
// hash table size
private int M;
// array of ST objects
private SequentialSearchST<Key, Value>[] st;
public SeparateChainingHashST(){
this(997);
}
// Create M linked lists
public SeparateChainingHashST(int M){
this.M = M;
st = (SequentialSearchST<Key, Value>[]) new SequentialSearchST[M];
for (int i = 0; i < M; i++) {
st[i] = new SequentialSearchST();
}
}
private int hash(Key key){
return (key.hashCode() & 0x7fffffff) % M;
}
public Value get(Key key){
return (Value) st[hash(key)].get(key);
}
public void put(Key key, Value val){
st[hash(key)].put(key,val);
N++;
}
public Iterable<Key> keys(){
return new KeyIterable();
}
private class KeyIterable implements Iterable<Key>{
@Override
public Iterator<Key> iterator() {
return new KeyIterator();
}
}
private class KeyIterator implements Iterator<Key>{
private int tempIndex = 0;
private Iterator<Key> currentIterator;
public KeyIterator(){
for (int i=0;i<M;i++) {
if (!st[i].isEmpty()){
currentIterator = st[i].keys().iterator();
tempIndex=i;
break;
}
}
}
@Override
public boolean hasNext() {
if (currentIterator==null) return false;
return currentIterator.hasNext();
}
@Override
public Key next() {
Key key = currentIterator.next();
if (!currentIterator.hasNext()){
currentIterator = nextIterator();
}
return key;
}
private Iterator<Key> nextIterator(){
for (int i = tempIndex+1; i < M; i++) {
if (!st[i].isEmpty()){
tempIndex = i;
return st[i].keys().iterator();
}
}
return null;
}
}
public static void main(String[] args) {
SeparateChainingHashST<String, Integer> separateChainingHashST = new SeparateChainingHashST<>();
separateChainingHashST.put("aa",23);
separateChainingHashST.put("world", 12);
separateChainingHashST.put("hello", 33);
Iterable<String> iterable = separateChainingHashST.keys();
Iterator<String> iterator = iterable.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
}
SequentialSearchST.java
package com.hef.algorithms.chapter3.item34;
import java.util.Iterator;
/**
* 顺序查询的key-value 表
*
* @Date 2020/2/6
* @Author lifei
*/
public class SequentialSearchST<Key, Value> {
private Node first;
private class Node {
Key key;
Value val;
Node next;
public Node(Key key, Value val, Node next) {
this.key = key;
this.val = val;
this.next = next;
}
}
public boolean isEmpty(){
if (first==null) return true;
return false;
}
/**
* Search for key, return associated value
*
* @param key
* @return
*/
public Value get(Key key) {
for (Node x = first; x != null; x = x.next) {
if (key.equals(x.key)) {
return x.val;
}
}
return null;
}
/**
* Search for key. Update value if found; grow table if new
*
* @param key
* @param val
*/
public void put(Key key, Value val) {
for (Node x = first; x != null; x = x.next) {
// Search hit: update val
if (key.equals(x.key)) {
x.val = val;
return;
}
}
// Search miss: add new node
first = new Node(key, val, first);
}
public Iterable<Key> keys() {
return new KeyIterable();
}
private class KeyIterable implements Iterable<Key> {
@Override
public Iterator<Key> iterator() {
return new KeyIterator();
}
}
private class KeyIterator implements Iterator<Key> {
private Node current = first;
@Override
public boolean hasNext() {
if (current == null) return false;
return true;
}
@Override
public Key next() {
Key key = current.key;
current = current.next;
return key;
}
}
public static void main(String[] args) {
SequentialSearchST<String, Integer> sequentialSearchST = new SequentialSearchST<>();
sequentialSearchST.put("aa", 23);
sequentialSearchST.put("bb", 33);
sequentialSearchST.put("cc", 12);
System.out.println(sequentialSearchST.first.key);
Iterable<String> keys = sequentialSearchST.keys();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
}
}
https://github.com/hefrankeleyn/AlgorithmsBook/blob/master/AlgorithmsPro/src/main/java/com/hef/algorithms/chapter3/item34/SequentialSearchST.java