mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-27 06:23:08 +08:00
Change project structure to a Maven Java project + Refactor (#2816)
This commit is contained in:

committed by
GitHub

parent
8e533d2617
commit
9fb3364ccc
@ -0,0 +1,65 @@
|
||||
# HASHMAP DATA STRUCTURE
|
||||
|
||||
A hash map organizes data so you can quickly look up values for a given key.
|
||||
|
||||
## Strengths:
|
||||
- **Fast lookups**: Lookups take O(1) time on average.
|
||||
- **Flexible keys**: Most data types can be used for keys, as long as they're hashable.
|
||||
|
||||
## Weaknesses:
|
||||
- **Slow worst-case**: Lookups take O(n) time in the worst case.
|
||||
- **Unordered**: Keys aren't stored in a special order. If you're looking for the smallest key, the largest key, or all the keys in a range, you'll need to look through every key to find it.
|
||||
- **Single-directional lookups**: While you can look up the value for a given key in O(1) time, looking up the keys for a given value requires looping through the whole dataset—O(n) time.
|
||||
- **Not cache-friendly**: Many hash table implementations use linked lists, which don't put data next to each other in memory.
|
||||
|
||||
## Time Complexity
|
||||
| | AVERAGE | WORST |
|
||||
|--------|---------|-------|
|
||||
| Space | O(n) | O(n) |
|
||||
| Insert | O(1) | O(n) |
|
||||
| Lookup | O(1) | O(n) |
|
||||
| Delete | O(1) | O(n) |
|
||||
|
||||
## Internal Structure of HashMap
|
||||
Internally HashMap contains an array of Node and a node is represented as a class that contains 4 fields:
|
||||
- int hash
|
||||
- K key
|
||||
- V value
|
||||
- Node next
|
||||
|
||||
It can be seen that the node is containing a reference to its own object. So it’s a linked list.
|
||||
|
||||
## Performance of HashMap
|
||||
Performance of HashMap depends on 2 parameters which are named as follows:
|
||||
- Initial Capacity
|
||||
- Load Factor
|
||||
|
||||
|
||||
**Initial Capacity**: It is the capacity of HashMap at the time of its creation (It is the number of buckets a HashMap can hold when the HashMap is instantiated). In java, it is 2^4=16 initially, meaning it can hold 16 key-value pairs.
|
||||
|
||||
**Load Factor**: It is the percent value of the capacity after which the capacity of Hashmap is to be increased (It is the percentage fill of buckets after which Rehashing takes place). In java, it is 0.75f by default, meaning the rehashing takes place after filling 75% of the capacity.
|
||||
|
||||
**Threshold**: It is the product of Load Factor and Initial Capacity. In java, by default, it is (16 * 0.75 = 12). That is, Rehashing takes place after inserting 12 key-value pairs into the HashMap.
|
||||
|
||||
**Rehashing** : It is the process of doubling the capacity of the HashMap after it reaches its Threshold. In java, HashMap continues to rehash(by default) in the following sequence – 2^4, 2^5, 2^6, 2^7, …. so on.
|
||||
|
||||
If the initial capacity is kept higher then rehashing will never be done. But by keeping it higher increases the time complexity of iteration. So it should be chosen very cleverly to increase performance. The expected number of values should be taken into account to set the initial capacity. The most generally preferred load factor value is 0.75 which provides a good deal between time and space costs. The load factor’s value varies between 0 and 1.
|
||||
|
||||
```
|
||||
Note: From Java 8 onward, Java has started using Self Balancing BST instead of a linked list for chaining.
|
||||
The advantage of self-balancing bst is, we get the worst case (when every key maps to the same slot) search time is O(Log n).
|
||||
```
|
||||
|
||||
Java has two hash table classes: HashTable and HashMap. In general, you should use a HashMap.
|
||||
|
||||
While both classes use keys to look up values, there are some important differences, including:
|
||||
|
||||
- A HashTable doesn't allow null keys or values; a HashMap does.
|
||||
- A HashTable is synchronized to prevent multiple threads from accessing it at once; a HashMap isn't.
|
||||
|
||||
## When Hash Map operations cost O(n) time?
|
||||
|
||||
**Hash collisions**: If all our keys caused hash collisions, we'd be at risk of having to walk through all of our values for a single lookup (in the example above, we'd have one big linked list). This is unlikely, but it could happen. That's the worst case.
|
||||
|
||||
**Dynamic array resizing**: Suppose we keep adding more items to our hash map. As the number of keys and values in our hash map exceeds the number of indices in the underlying array, hash collisions become inevitable. To mitigate this, we could expand our underlying array whenever things start to get crowded. That requires allocating a larger array and rehashing all of our existing keys to figure out their new position—O(n) time.
|
||||
|
@ -0,0 +1,150 @@
|
||||
package com.thealgorithms.datastructures.hashmap.hashing;
|
||||
|
||||
public class HashMap {
|
||||
|
||||
private int hsize;
|
||||
private LinkedList[] buckets;
|
||||
|
||||
public HashMap(int hsize) {
|
||||
buckets = new LinkedList[hsize];
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
buckets[i] = new LinkedList();
|
||||
// Java requires explicit initialisaton of each object
|
||||
}
|
||||
this.hsize = hsize;
|
||||
}
|
||||
|
||||
public int hashing(int key) {
|
||||
int hash = key % hsize;
|
||||
if (hash < 0) {
|
||||
hash += hsize;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void insertHash(int key) {
|
||||
int hash = hashing(key);
|
||||
buckets[hash].insert(key);
|
||||
}
|
||||
|
||||
public void deleteHash(int key) {
|
||||
int hash = hashing(key);
|
||||
|
||||
buckets[hash].delete(key);
|
||||
}
|
||||
|
||||
public void displayHashtable() {
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
System.out.printf("Bucket %d :", i);
|
||||
System.out.println(buckets[i].display());
|
||||
}
|
||||
}
|
||||
|
||||
public static class LinkedList {
|
||||
|
||||
private Node first;
|
||||
|
||||
public LinkedList() {
|
||||
first = null;
|
||||
}
|
||||
|
||||
public void insert(int key) {
|
||||
if (isEmpty()) {
|
||||
first = new Node(key);
|
||||
return;
|
||||
}
|
||||
|
||||
Node temp = findEnd(first);
|
||||
temp.setNext(new Node(key));
|
||||
}
|
||||
|
||||
private Node findEnd(Node n) {
|
||||
if (n.getNext() == null) {
|
||||
return n;
|
||||
} else {
|
||||
return findEnd(n.getNext());
|
||||
}
|
||||
}
|
||||
|
||||
public Node findKey(int key) {
|
||||
if (!isEmpty()) {
|
||||
return findKey(first, key);
|
||||
} else {
|
||||
System.out.println("List is empty");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Node findKey(Node n, int key) {
|
||||
if (n.getKey() == key) {
|
||||
return n;
|
||||
} else if (n.getNext() == null) {
|
||||
System.out.println("Key not found");
|
||||
return null;
|
||||
} else {
|
||||
return findKey(n.getNext(), key);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(int key) {
|
||||
if (!isEmpty()) {
|
||||
if (first.getKey() == key) {
|
||||
first = null;
|
||||
} else {
|
||||
delete(first, key);
|
||||
}
|
||||
} else {
|
||||
System.out.println("List is empty");
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(Node n, int key) {
|
||||
if (n.getNext().getKey() == key) {
|
||||
if (n.getNext().getNext() == null) {
|
||||
n.setNext(null);
|
||||
} else {
|
||||
n.setNext(n.getNext().getNext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String display() {
|
||||
return display(first);
|
||||
}
|
||||
|
||||
private String display(Node n) {
|
||||
if (n == null) {
|
||||
return "null";
|
||||
} else {
|
||||
return n.getKey() + "->" + display(n.getNext());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return first == null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Node {
|
||||
|
||||
private Node next;
|
||||
private int key;
|
||||
|
||||
public Node(int key) {
|
||||
next = null;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Node getNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public int getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setNext(Node next) {
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
package com.thealgorithms.datastructures.hashmap.hashing;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class is an implementation of a hash table using linear probing It uses
|
||||
* a dynamic array to lengthen the size of the hash table when load factor > .7
|
||||
*/
|
||||
public class HashMapLinearProbing {
|
||||
|
||||
private int hsize; // size of the hash table
|
||||
private Integer[] buckets; // array representing the table
|
||||
private Integer AVAILABLE;
|
||||
private int size; // amount of elements in the hash table
|
||||
|
||||
/**
|
||||
* Constructor initializes buckets array, hsize, and creates dummy object
|
||||
* for AVAILABLE
|
||||
*
|
||||
* @param hsize the desired size of the hash map
|
||||
*/
|
||||
public HashMapLinearProbing(int hsize) {
|
||||
this.buckets = new Integer[hsize];
|
||||
this.hsize = hsize;
|
||||
this.AVAILABLE = Integer.MIN_VALUE;
|
||||
this.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Hash Function takes a given key and finds an index based on its data
|
||||
*
|
||||
* @param key the desired key to be converted
|
||||
* @return int an index corresponding to the key
|
||||
*/
|
||||
public int hashing(int key) {
|
||||
int hash = key % hsize;
|
||||
if (hash < 0) {
|
||||
hash += hsize;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* inserts the key into the hash map by wrapping it as an Integer object
|
||||
*
|
||||
* @param key the desired key to be inserted in the hash map
|
||||
*/
|
||||
public void insertHash(int key) {
|
||||
Integer wrappedInt = key;
|
||||
int hash = hashing(key);
|
||||
|
||||
if (isFull()) {
|
||||
System.out.println("Hash table is full");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
if (buckets[hash] == null || buckets[hash] == AVAILABLE) {
|
||||
buckets[hash] = wrappedInt;
|
||||
size++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash + 1 < hsize) {
|
||||
hash++;
|
||||
} else {
|
||||
hash = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes a key from the hash map and adds an available placeholder
|
||||
*
|
||||
* @param key the desired key to be deleted
|
||||
*/
|
||||
public void deleteHash(int key) {
|
||||
Integer wrappedInt = key;
|
||||
int hash = hashing(key);
|
||||
|
||||
if (isEmpty()) {
|
||||
System.out.println("Table is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
if (buckets[hash] != null && buckets[hash].equals(wrappedInt)) {
|
||||
buckets[hash] = AVAILABLE;
|
||||
size--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash + 1 < hsize) {
|
||||
hash++;
|
||||
} else {
|
||||
hash = 0;
|
||||
}
|
||||
}
|
||||
System.out.println("Key " + key + " not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the hash table line by line
|
||||
*/
|
||||
public void displayHashtable() {
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
if (buckets[i] == null || buckets[i] == AVAILABLE) {
|
||||
System.out.println("Bucket " + i + ": Empty");
|
||||
} else {
|
||||
System.out.println("Bucket " + i + ": " + buckets[i].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the index of location based on an inputed key
|
||||
*
|
||||
* @param key the desired key to be found
|
||||
* @return int the index where the key is located
|
||||
*/
|
||||
public int findHash(int key) {
|
||||
Integer wrappedInt = key;
|
||||
int hash = hashing(key);
|
||||
|
||||
if (isEmpty()) {
|
||||
System.out.println("Table is empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
try {
|
||||
if (buckets[hash].equals(wrappedInt)) {
|
||||
buckets[hash] = AVAILABLE;
|
||||
return hash;
|
||||
}
|
||||
} catch (Exception E) {
|
||||
}
|
||||
|
||||
if (hash + 1 < hsize) {
|
||||
hash++;
|
||||
} else {
|
||||
hash = 0;
|
||||
}
|
||||
}
|
||||
System.out.println("Key " + key + " not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void lengthenTable() {
|
||||
buckets = Arrays.copyOf(buckets, hsize * 2);
|
||||
hsize *= 2;
|
||||
System.out.println("Table size is now: " + hsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the load factor of the hash table if greater than .7,
|
||||
* automatically lengthens table to prevent further collisions
|
||||
*/
|
||||
public void checkLoadFactor() {
|
||||
double factor = (double) size / hsize;
|
||||
if (factor > .7) {
|
||||
System.out.println("Load factor is " + factor + ", lengthening table");
|
||||
lengthenTable();
|
||||
} else {
|
||||
System.out.println("Load factor is " + factor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* isFull returns true if the hash map is full and false if not full
|
||||
*
|
||||
* @return boolean is Empty
|
||||
*/
|
||||
public boolean isFull() {
|
||||
boolean response = true;
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
if (buckets[i] == null || buckets[i] == AVAILABLE) {
|
||||
response = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* isEmpty returns true if the hash map is empty and false if not empty
|
||||
*
|
||||
* @return boolean is Empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
boolean response = true;
|
||||
for (int i = 0; i < hsize; i++) {
|
||||
if (buckets[i] != null) {
|
||||
response = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.thealgorithms.datastructures.hashmap.hashing;
|
||||
/*
|
||||
* this is algo which implies common mathematical set theory concept
|
||||
* called intersection in which result is common values of both the sets
|
||||
* here metaphor of sets is HashMap
|
||||
|
||||
|
||||
Test Case:
|
||||
Scanner scn=new Scanner(System.in);
|
||||
int len =scn.nextInt();
|
||||
int arr[]=new int[len];
|
||||
int arr2[]=new int[len];
|
||||
|
||||
for(int i=0;i<2*len;i++) {
|
||||
|
||||
if(i<len)
|
||||
arr[i]=scn.nextInt();
|
||||
if(i>=len) {
|
||||
arr2[i-len]=scn.nextInt();
|
||||
}
|
||||
}
|
||||
System.out.println(Main(arr,arr2));
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
|
||||
public class Intersection {
|
||||
|
||||
public static ArrayList Main(int arr[],int arr2[]) {
|
||||
HashMap<Integer,Integer> hmap=new HashMap<>();
|
||||
HashMap<Integer,Integer> hmap2=new HashMap<>();
|
||||
for(int i=0;i<arr.length;i++) {
|
||||
if(hmap.containsKey(arr[i])) {
|
||||
int val=hmap.get(arr[i]);
|
||||
hmap.put(arr[i],val+1);
|
||||
}else
|
||||
hmap.put(arr[i],1);
|
||||
|
||||
}
|
||||
ArrayList<Integer> res=new ArrayList<>();
|
||||
for(int i=0;i<arr2.length;i++) {
|
||||
if(hmap.containsKey(arr2[i])&&hmap.get(arr2[i])>0) {
|
||||
int val=hmap.get(arr2[i]);
|
||||
hmap.put(arr2[i],val-1);
|
||||
res.add(arr2[i]);
|
||||
}
|
||||
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public Intersection() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.thealgorithms.datastructures.hashmap.hashing;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
int choice, key;
|
||||
|
||||
HashMap h = new HashMap(7);
|
||||
Scanner In = new Scanner(System.in);
|
||||
|
||||
while (true) {
|
||||
System.out.println("Enter your Choice :");
|
||||
System.out.println("1. Add Key");
|
||||
System.out.println("2. Delete Key");
|
||||
System.out.println("3. Print Table");
|
||||
System.out.println("4. Exit");
|
||||
|
||||
choice = In.nextInt();
|
||||
|
||||
switch (choice) {
|
||||
case 1: {
|
||||
System.out.println("Enter the Key: ");
|
||||
key = In.nextInt();
|
||||
h.insertHash(key);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
System.out.println("Enter the Key delete: ");
|
||||
key = In.nextInt();
|
||||
h.deleteHash(key);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
System.out.println("Print table");
|
||||
h.displayHashtable();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
In.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.thealgorithms.datastructures.hashmap.hashing;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
public class MainLinearProbing {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
int choice, key;
|
||||
|
||||
HashMapLinearProbing h = new HashMapLinearProbing(7);
|
||||
Scanner In = new Scanner(System.in);
|
||||
|
||||
while (true) {
|
||||
System.out.println("Enter your Choice :");
|
||||
System.out.println("1. Add Key");
|
||||
System.out.println("2. Delete Key");
|
||||
System.out.println("3. Print Table");
|
||||
System.out.println("4. Exit");
|
||||
System.out.println("5. Search and print key index");
|
||||
System.out.println("6. Check load factor");
|
||||
|
||||
choice = In.nextInt();
|
||||
|
||||
switch (choice) {
|
||||
case 1: {
|
||||
System.out.println("Enter the Key: ");
|
||||
key = In.nextInt();
|
||||
h.insertHash(key);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
System.out.println("Enter the Key delete: ");
|
||||
key = In.nextInt();
|
||||
h.deleteHash(key);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
System.out.println("Print table");
|
||||
h.displayHashtable();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
In.close();
|
||||
return;
|
||||
}
|
||||
case 5: {
|
||||
System.out.println("Enter the Key to find and print: ");
|
||||
key = In.nextInt();
|
||||
System.out.println("Key: " + key + " is at index: " + h.findHash(key));
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
h.checkLoadFactor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user