问题描述
提示:这里我们默认有MyMap这个类,代码中我也会给出来:
书上代码用的是分离链接法来解决地址冲突的,这里要求我们改用线性探测法来实现。将散列表初始大小设置为4,使用 f(key) = key % size , size是散列表的大小。
难点分析:
提示:线性探测法,当前位置有元素就找下一个,如果有,又找下一个,知道找到位置放下元素:
首先,是放元素,我们将键值对放到Entry中,散列表中储存Entry对象,所以我们将根据Hash函数得到的散列表的下标,对下标进行冲突的解决(可能Hash得到的下标是相同的)。
添加元素:如果散列表当前下标有元素,就找下一个,这样一直下去,直到找到一个位置放好当前元素。
查找:根据输入的键,转化成散列表的下标,通过下标,慢慢寻找,找到退出,返回找到的值,或者达到一个空单元退出,返回null。
删除:将当前散列表的当前下标的元素标记为空就好。
解决方案:
提示:这里直接改造的书上的代码(java语言程序设计<进阶版>):
import java.util.Scanner;
import java.util.Set;
public class Lab27_01 {
public static void main(String[] args) {
// //题目要求的测试程序
// MyHashMapForLinear<Integer, Integer> map = new MyHashMapForLinear<>();
// Scanner input = new Scanner(System.in);
//
// System.out.print("Enter integer keys, input ends with a negative value: ");
// int key = input.nextInt();
// while (key >= 0) {
// map.put(key, key);
// key = input.nextInt();
// }
//
// System.out.print("Enter key1: ");
// int key1 = input.nextInt();
// System.out.println("Is " + key1 + " in the map? " + map.containsKey(key1));
//
// System.out.print("Enter key2: ");
// int key2 = input.nextInt();
// System.out.println("Is " + key2 + " in the map? " + map.containsKey(key2));
//
// System.out.println("The map size is " + map.size());
//
// map.remove(2);
// System.out.println("After removing key 2 from the map, is key 2 in the map? " + map.containsKey(2));
// System.out.println("The map size is " + map.size());
//书上的测试程序
MyHashMapForLinear<String, Integer> map = new MyHashMapForLinear<>();
map.put("Smith", 30);
map.put("Anderson", 31);
map.put("Lewis", 29);
map.put("Cook", 29);
map.put("Smith", 65);
System.out.println("Entries in map: " + map);
System.out.println("The age for " + "Lewis is " +
map.get("Lewis"));
System.out.println("Is Smith in the map? " +
map.containsKey("Smith"));
System.out.println("Is age 33 in the map? " +
map.containsValue(33));
map.remove("Smith");
System.out.println("Entries in map: " + map);
map.clear();
System.out.println("Entries in map: " + map);
}
}
interface MyMapForLab27_01<K, V> {
/**
* Remove all of the entries from this map
*/
public void clear();
/**
* Return true if the specified key is in the map
*/
public boolean containsKey(K key);
/**
* Return true if this map contains the specified value
*/
public boolean containsValue(V value);
/**
* Return a set of entries in the map
*
* @return
*/
public Set<Entry<K, V>> entrySet();
/**
* Return the first value that matches the specified key
*/
public V get(K key);
/**
* Return true if this map contains no entries
*/
public boolean isEmpty();
/**
* Return a set consisting of the keys in this map
*/
public java.util.Set<K> keySet();
/**
* Add an entry (key, value) into the map
*/
public V put(K key, V value);
/**
* Remove the entries for the specified key
*/
public void remove(K key);
/**
* Return the number of mappings in this map
*/
public int size();
/**
* Return a set consisting of the values in this map
*/
public java.util.Set<V> values();
/**
* Define inner class for Entry
*/
public static class Entry<K, V> {
K key;
V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
@Override
public String toString() {
return "[" + key + ", " + value + "]";
}
}
}
class MyHashMapForLinear<K, V> implements MyMapForLab27_01<K, V> {
// Define the default hash table size. Must be a power of 2
private static int DEFAULT_INITIAL_CAPACITY = 4;//初始散列表大小为4
// Define the maximum hash table size. 1 << 30 is same as 2^30
private static int MAXIMUM_CAPACITY = 1 << 30;
// Current hash table capacity. Capacity is a power of 2
private int size;// 散列表的大小
// Define default load factor
private static float DEFAULT_MAX_LOAD_FACTOR = 0.5f;//装填因子的阈值改为0.5
// Specify a load factor used in the hash table
private float loadFactorThreshold;
// The number of entries in the map
private int len = 0;// 实际包含的条目数
MyMapForLab27_01.Entry<K, V>[] table;// 每个条目一个键值对
/**
* Construct a map with the default capacity and load factor
*/
public MyHashMapForLinear() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_LOAD_FACTOR);
}
/**
* Construct a map with the specified initial capacity and
* default load factor
*/
public MyHashMapForLinear(int initialCapacity) {
this(initialCapacity, DEFAULT_MAX_LOAD_FACTOR);
}
/**
* Construct a map with the specified initial capacity
* and load factor
*/
public MyHashMapForLinear(int initialCapacity, float loadFactorThreshold) {
if (initialCapacity > MAXIMUM_CAPACITY)
this.size = MAXIMUM_CAPACITY;
else
this.size = trimToPowerOf2(initialCapacity);
this.loadFactorThreshold = loadFactorThreshold;
table = new MyMapForLab27_01.Entry[size];
}
@Override
/** Remove all of the entries from this map */
public void clear() {
table = new MyMapForLab27_01.Entry[size];
len = 0;
}
@Override
/** Return true if the specified key is in the map */
public boolean containsKey(K key) {
if (get(key) != null)
return true;
else
return false;
}
@Override
/** Return true if this map contains the value */
// 修改此方法
public boolean containsValue(V value) {
for (int i = 0; i < size; i++) {
if (table[i] != null) {
Entry<K, V> bucket = table[i];
if (bucket.getValue().equals(value))
return true;
}
}
return false;
}
@Override
/** Return a set of entries in the map */
// 修改此方法
public Set<MyMapForLab27_01.Entry<K, V>> entrySet() {
java.util.Set<MyMapForLab27_01.Entry<K, V>> set =
new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
Entry<K, V> bucket = table[i];
set.add(bucket);
}
}
return set;
}
@Override
/** Return the value that matches the specified key */
//改变此方法---->挨个查找,为空退出
public V get(K key) {
int bucketIndex = hash(key.hashCode());
for (int i = bucketIndex; i < size; i++) {
if (table[i % size] != null) {
Entry<K, V> bucket = table[i % size];
if (bucket.getKey().equals(key))
return bucket.getValue();
} else
return null;
}
return null;
}
@Override
/** Return true if this map contains no entries */
public boolean isEmpty() {
return len == 0;
}
@Override
/** Return a set consisting of the keys in this map */
public java.util.Set<K> keySet() {
java.util.Set<K> set = new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
Entry<K, V> bucket = table[i];
set.add(bucket.getKey());
}
}
return set;
}
@Override
/** Add an entry (key, value) into the map */
public V put(K key, V value) {
Set<K> set = keySet();
if (set.contains(key)) {// 如果散列表中包含该键
int bucketIndex = hash(key.hashCode());
for (int i = bucketIndex; i < size; i++) {
if (table[i % size] != null) {
Entry<K, V> bucket = table[i % size];
if (bucket.getKey().equals(key)) {
V oldValue = bucket.getValue();
bucket.value = value;
return oldValue;
}
} else
break;
}
}
// Check load factor
if (len >= size * loadFactorThreshold) {
if (size == MAXIMUM_CAPACITY)
throw new RuntimeException("Exceeding maximum capacity");
rehash();
}
int bucketIndex = hash(key.hashCode());
if (table[bucketIndex] == null) {
table[bucketIndex] = new Entry<>(key, value);
len++;
return value;
}
while (table[bucketIndex % size] != null) bucketIndex++;
table[bucketIndex % size] = new MyMapForLab27_01.Entry<>(key, value);
len++; // Increase size
return value;
}
@Override
/** Remove the entries for the specified key */
public void remove(K key) {
int bucketIndex = hash(key.hashCode());
Set<K> set = keySet();
if (set.contains(key)) {
for (int i = bucketIndex; i < size; i++) {
if (table[i % size] != null) {
Entry<K, V> bucket = table[i % size];
if (bucket.getKey().equals(key)) {
table[i % size] = null;
len--;
break;
} else
break;
}
}
}
}
@Override
/** Return the number of entries in this map */
public int size() {
return len;
}
@Override
/** Return a set consisting of the values in this map */
public java.util.Set<V> values() {
java.util.Set<V> set = new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
Entry<K, V> bucket = table[i];
set.add(bucket.getValue());
}
}
return set;
}
/**
* Hash function
* 改变 hash 函数, 并且保证返回为正数
*/
private int hash(int hashCode) {
return Math.abs(supplementalHash(hashCode) % size);
}
/**
* Ensure the hashing is evenly distributed
*/
private static int supplementalHash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Return a power of 2 for initialCapacity
*/
private int trimToPowerOf2(int initialCapacity) {
int capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
return capacity;
}
/**
* Rehash the map
*/
private void rehash() {
java.util.Set<Entry<K, V>> set = entrySet(); // Get entries
size <<= 1; // Double capacity
table = new Entry[size]; // Create a new hash table
len = 0; // Reset size to 0
for (Entry<K, V> entry : set) {
put(entry.getKey(), entry.getValue()); // Store to new table
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < size; i++)
if (table[i] != null)
builder.append(table[i]);
builder.append("]");
return builder.toString();
}
}
二次探测法和其十分类似,线性探测能看懂,二次探测一般没问题
import java.util.LinkedList;
import java.util.Set;
public class Lab27_02 {
public static void main(String[] args) {
//书上的测试程序
MyHashMapForQuad<String, Integer> map = new MyHashMapForQuad<>();
map.put("Smith", 30);
map.put("Anderson", 31);
map.put("Lewis", 29);
map.put("Cook", 29);
map.put("Smith", 65);
System.out.println("Entries in map: " + map);
System.out.println("The age for " + "Lewis is " +
map.get("Lewis"));
System.out.println("Is Smith in the map? " +
map.containsKey("Smith"));
System.out.println("Is age 33 in the map? " +
map.containsValue(33));
map.remove("Smith");
System.out.println("Entries in map: " + map);
map.clear();
System.out.println("Entries in map: " + map);
}
}
interface MyMapForLab27_02<K, V> {
/**
* Remove all of the entries from this map
*/
public void clear();
/**
* Return true if the specified key is in the map
*/
public boolean containsKey(K key);
/**
* Return true if this map contains the specified value
*/
public boolean containsValue(V value);
/**
* Return a set of entries in the map
*
* @return
*/
public Set<Entry<K, V>> entrySet();
/**
* Return the first value that matches the specified key
*/
public V get(K key);
/**
* Return true if this map contains no entries
*/
public boolean isEmpty();
/**
* Return a set consisting of the keys in this map
*/
public java.util.Set<K> keySet();
/**
* Add an entry (key, value) into the map
*/
public V put(K key, V value);
/**
* Remove the entries for the specified key
*/
public void remove(K key);
/**
* Return the number of mappings in this map
*/
public int size();
/**
* Return a set consisting of the values in this map
*/
public java.util.Set<V> values();
/**
* Define inner class for Entry
*/
public static class Entry<K, V> {
K key;
V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
@Override
public String toString() {
return "[" + key + ", " + value + "]";
}
}
}
class MyHashMapForQuad<K, V> implements MyMapForLab27_02<K, V> {
// Define the default hash table size. Must be a power of 2
private static int DEFAULT_INITIAL_CAPACITY = 4; // 初始大小
// Define the maximum hash table size. 1 << 30 is same as 2^30
private static int MAXIMUM_CAPACITY = 1 << 30;
// Current hash table capacity. Capacity is a power of 2
private int size;
// Define default load factor
private static float DEFAULT_MAX_LOAD_FACTOR = 0.5f;// 装填因子改为,0.5
// Specify a load factor used in the hash table
private float loadFactorThreshold;
// The number of entries in the map
private int len = 0; // 实际条目数
// Hash table is an array with each cell that is a linked list
Entry<K, V>[] table;
/**
* Construct a map with the default capacity and load factor
*/
public MyHashMapForQuad() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_LOAD_FACTOR);
}
/**
* Construct a map with the specified initial capacity and
* default load factor
*/
public MyHashMapForQuad(int initialCapacity) {
this(initialCapacity, DEFAULT_MAX_LOAD_FACTOR);
}
/**
* Construct a map with the specified initial capacity
* and load factor
*/
public MyHashMapForQuad(int initialCapacity, float loadFactorThreshold) {
if (initialCapacity > MAXIMUM_CAPACITY)
this.size = MAXIMUM_CAPACITY;
else
this.size = trimToPowerOf2(initialCapacity);
this.loadFactorThreshold = loadFactorThreshold;
table = new MyMapForLab27_02.Entry[size];
}
@Override
/** Remove all of the entries from this map */
public void clear() {
table = new MyMapForLab27_02.Entry[size];
len = 0;
}
@Override
/** Return true if the specified key is in the map */
public boolean containsKey(K key) {
if (get(key) != null)
return true;
else
return false;
}
@Override
/** Return true if this map contains the value */
// 修改此方法-----> 直接用values来判断就好
public boolean containsValue(V value) {
Set<V> set = values();
return set.contains(value);
}
@Override
/** Return a set of entries in the map */
public Set<MyMapForLab27_02.Entry<K, V>> entrySet() {
java.util.Set<MyMapForLab27_02.Entry<K, V>> set =
new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[i];
set.add(bucket);
}
}
return set;
}
@Override
/** Return the value that matches the specified key */
//改变此方法---->二次探测查找,为空退出
public V get(K key) {
int bucketIndex = hash(key.hashCode());
for (int i = 0; i < size; i++) {
if (table[(bucketIndex + i * i)] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[(bucketIndex + i * i) % size];
if (bucket.getKey().equals(key))
return bucket.getValue();
} else
return null;
}
return null;
}
@Override
/** Return true if this map contains no entries */
public boolean isEmpty() {
return len == 0;
}
@Override
/** Return a set consisting of the keys in this map */
public java.util.Set<K> keySet() {
java.util.Set<K> set = new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[i];
set.add(bucket.getKey());
}
}
return set;
}
@Override
/** Add an entry (key, value) into the map */
public V put(K key, V value) {
Set<K> set = keySet();
if (set.contains(key)) {// 散列表中包含该键
int bucketIndex = hash(key.hashCode());
for (int i = 0; i < size; i++) {
if (table[(bucketIndex + i * i)] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[(bucketIndex + i * i)];
if (bucket.getKey().equals(key)) {
V oldValue = bucket.getValue();
bucket.value = value;
return oldValue;
}
} else
break;
}
}
// Check load factor
if (len >= size * loadFactorThreshold) {
if (size == MAXIMUM_CAPACITY)
throw new RuntimeException("Exceeding maximum capacity");
rehash();
}
int bucketIndex = hash(key.hashCode());
if (table[bucketIndex] == null) {
table[bucketIndex] = new MyMapForLab27_02.Entry<>(key, value);
len++;
return value;
}
int i = 0;
while (table[(bucketIndex + i * i) % size] != null) i++;
table[(bucketIndex + i * i) % size] = new MyMapForLab27_02.Entry<>(key, value);
len++; // Increase size
return value;
}
@Override
/** Remove the entries for the specified key */
public void remove(K key) {
int bucketIndex = hash(key.hashCode());
Set<K> set = keySet();
if (set.contains(key)) {
for (int i = 0; i < size; i++) {
if (table[(bucketIndex + i * i) % size] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[(bucketIndex + i * i) % size];
if (bucket.getKey().equals(key)) {
table[(bucketIndex + i * i) % size] = null;
len--;
break;
} else
break;
}
}
}
}
@Override
/** Return the number of entries in this map */
public int size() {
return len;
}
@Override
/** Return a set consisting of the values in this map */
public java.util.Set<V> values() {
java.util.Set<V> set = new java.util.HashSet<>();
for (int i = 0; i < size; i++) {
if (table[i] != null) {
MyMapForLab27_02.Entry<K, V> bucket = table[i];
set.add(bucket.getValue());
}
}
return set;
}
/**
* Hash function
* 改变 hash 函数, 并且保证返回为正数
*/
private int hash(int hashCode) {
return Math.abs(supplementalHash(hashCode) % size);
}
/**
* Ensure the hashing is evenly distributed
*/
private static int supplementalHash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Return a power of 2 for initialCapacity
*/
private int trimToPowerOf2(int initialCapacity) {
int capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
return capacity;
}
/**
* Rehash the map
*/
private void rehash() {
java.util.Set<MyMapForLab27_02.Entry<K, V>> set = entrySet(); // Get entries
size <<= 1; // Double capacity
table = new MyMapForLab27_02.Entry[size]; // Create a new hash table
len = 0; // Reset size to 0
for (MyMapForLab27_02.Entry<K, V> entry : set) {
put(entry.getKey(), entry.getValue()); // Store to new table
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < size; i++)
if (table[i] != null)
builder.append(table[i]);
builder.append("]");
return builder.toString();
}
}