I denne vejledning lærer du, hvad hash-tabel er. Du finder også arbejdseksempler på hash-tabeloperationer i C, C ++, Java og Python.
Hash-tabel er en datastruktur, der repræsenterer data i form af nøgleværdipar . Hver nøgle kortlægges til en værdi i hash-tabellen. Tasterne bruges til indeksering af værdier / data. En lignende tilgang anvendes af et associerende array.
Data er repræsenteret i et nøgleværdipar ved hjælp af nøgler som vist i nedenstående figur. Hver data er knyttet til en nøgle. Nøglen er et heltal, der peger på dataene.
1. Direkte adressetabel
Direkte adressetabel bruges, når mængden af plads, der bruges af tabellen, ikke er et problem for programmet. Her antager vi det
- nøglerne er små heltal
- antallet af taster er ikke for stort, og
- ingen data har den samme nøgle
En pulje af heltal tages kaldet univers U = (0, 1,… ., n-1)
.
Hver slot i en direkte adressetabel T(0… n-1)
indeholder en markør til det element, der svarer til dataene.
Indekset for arrayet T
er selve nøglen, og indholdet af T
er en markør til sættet (key, element)
. Hvis der ikke er noget element til en nøgle, efterlades det som NULL
.
Nogle gange er selve nøglen dataene.
Pseudokode til operationer
directAddressSearch(T, k) return T(k) directAddressInsert(T, x) T(x.key) = x directAddressDelete(T, x) T(x.key) = NIL
Begrænsninger i en direkte adressetabel
- Værdien af nøglen skal være lille.
- Antallet af nøgler skal være lille nok, så det ikke krydser størrelsesgrænsen for en matrix.
2. Hash-tabel
I en hash-tabel behandles tasterne for at producere et nyt indeks, der kortlægges til det krævede element. Denne proces kaldes hashing.
Lad h(x)
være en hash-funktion og k
være en nøgle.h(k)
beregnes, og det bruges som et indeks for elementet.
Begrænsninger ved et Hash-bord
- Hvis det samme indeks produceres af hash-funktionen til flere nøgler, opstår der konflikt. Denne situation kaldes kollision.
For at undgå dette vælges en passende hash-funktion. Men det er umuligt at fremstille alle unikke nøgler, fordi|U|>m
. Således forhindrer en god hash-funktion muligvis ikke kollisionerne fuldstændigt, men det kan reducere antallet af kollisioner.
Vi har dog andre teknikker til at løse kollision.
Fordele ved hash-tabel i forhold til direkte adressetabel:
De vigtigste problemer med direkte adressetabel er størrelsen på arrayet og den muligvis store værdi af en nøgle. Hashfunktionen reducerer indeksområdet, og dermed reduceres arrayets størrelse også.
For eksempel Hvis k = 9845648451321
, så h(k) = 11
(ved hjælp af en eller anden hash-funktion). Dette hjælper med at gemme spildt hukommelse, mens du giver indekset 9845648451321
til arrayet
Kollisionsopløsning ved kæde
I denne teknik, hvis en hash-funktion producerer det samme indeks for flere elementer, gemmes disse elementer i det samme indeks ved hjælp af en dobbeltkoblet liste.
Hvis j
er pladsen til flere elementer, indeholder den en markør til hovedet på listen over elementer. Hvis der ikke er noget element til stede, j
indeholder NIL
.
Pseudokode til operationer
chainedHashSearch(T, k) return T(h(k)) chainedHashInsert(T, x) T(h(x.key)) = x //insert at the head chainedHashDelete(T, x) T(h(x.key)) = NIL
Python, Java, C og C ++ implementering
Python Java C C ++ # Python program to demonstrate working of HashTable hashTable = ((),) * 10 def checkPrime(n): if n == 1 or n == 0: return 0 for i in range(2, n//2): if n % i == 0: return 0 return 1 def getPrime(n): if n % 2 == 0: n = n + 1 while not checkPrime(n): n += 2 return n def hashFunction(key): capacity = getPrime(10) return key % capacity def insertData(key, data): index = hashFunction(key) hashTable(index) = (key, data) def removeData(key): index = hashFunction(key) hashTable(index) = 0 insertData(123, "apple") insertData(432, "mango") insertData(213, "banana") insertData(654, "guava") print(hashTable) removeData(123) print(hashTable)
// Java program to demonstrate working of HashTable import java.util.*; class HashTable ( public static void main(String args()) ( Hashtable ht = new Hashtable(); ht.put(123, 432); ht.put(12, 2345); ht.put(15, 5643); ht.put(3, 321); ht.remove(12); System.out.println(ht); ) )
// Implementing hash table in C #include #include struct set ( int key; int data; ); struct set *array; int capacity = 10; int size = 0; int hashFunction(int key) ( return (key % capacity); ) int checkPrime(int n) ( int i; if (n == 1 || n == 0) ( return 0; ) for (i = 2; i < n / 2; i++) ( if (n % i == 0) ( return 0; ) ) return 1; ) int getPrime(int n) ( if (n % 2 == 0) ( n++; ) while (!checkPrime(n)) ( n += 2; ) return n; ) void init_array() ( capacity = getPrime(capacity); array = (struct set *)malloc(capacity * sizeof(struct set)); for (int i = 0; i < capacity; i++) ( array(i).key = 0; array(i).data = 0; ) ) void insert(int key, int data) ( int index = hashFunction(key); if (array(index).data == 0) ( array(index).key = key; array(index).data = data; size++; printf(" Key (%d) has been inserted ", key); ) else if (array(index).key == key) ( array(index).data = data; ) else ( printf(" Collision occured "); ) ) void remove_element(int key) ( int index = hashFunction(key); if (array(index).data == 0) ( printf(" This key does not exist "); ) else ( array(index).key = 0; array(index).data = 0; size--; printf(" Key (%d) has been removed ", key); ) ) void display() ( int i; for (i = 0; i < capacity; i++) ( if (array(i).data == 0) ( printf(" array(%d): / ", i); ) else ( printf(" key: %d array(%d): %d ", array(i).key, i, array(i).data); ) ) ) int size_of_hashtable() ( return size; ) int main() ( int choice, key, data, n; int c = 0; init_array(); do ( printf("1.Insert item in the Hash Table" "2.Remove item from the Hash Table" "3.Check the size of Hash Table" "4.Display a Hash Table" " Please enter your choice: "); scanf("%d", &choice); switch (choice) ( case 1: printf("Enter key -: "); scanf("%d", &key); printf("Enter data -: "); scanf("%d", &data); insert(key, data); break; case 2: printf("Enter the key to delete-:"); scanf("%d", &key); remove_element(key); break; case 3: n = size_of_hashtable(); printf("Size of Hash Table is-:%d", n); break; case 4: display(); break; default: printf("Invalid Input"); ) printf("Do you want to continue (press 1 for yes): "); scanf("%d", &c); ) while (c == 1); )
// Implementing hash table in C++ #include #include using namespace std; class HashTable ( int capacity; list *table; public: HashTable(int V); void insertItem(int key, int data); void deleteItem(int key); int checkPrime(int n) ( int i; if (n == 1 || n == 0) ( return 0; ) for (i = 2; i capacity = size; table = new list(capacity); ) void HashTable::insertItem(int key, int data) ( int index = hashFunction(key); table(index).push_back(data); ) void HashTable::deleteItem(int key) ( int index = hashFunction(key); list::iterator i; for (i = table(index).begin(); i != table(index).end(); i++) ( if (*i == key) break; ) if (i != table(index).end()) table(index).erase(i); ) void HashTable::displayHash() ( for (int i = 0; i < capacity; i++) ( cout << "table(" << i << ")"; for (auto x : table(i)) cout < " << x; cout << endl; ) ) int main() ( int key() = (231, 321, 212, 321, 433, 262); int data() = (123, 432, 523, 43, 423, 111); int size = sizeof(key) / sizeof(key(0)); HashTable h(size); for (int i = 0; i < n; i++) h.insertItem(key(i), data(i)); h.deleteItem(12); h.displayHash(); )
Gode hashfunktioner
En god hash-funktion har følgende egenskaber.
- Det skal ikke generere nøgler, der er for store, og skovlpladsen er lille. Plads er spildt.
- De genererede nøgler skal hverken være meget tætte eller for langt inden for rækkevidde.
- Kollisionen skal minimeres så meget som muligt.
Nogle af metoderne til hashing er:
Opdelingsmetode
Hvis k
er en nøgle og m
er størrelsen på hash-tabellen, h()
beregnes hash-funktionen som:
h(k) = k mod m
For eksempel Hvis størrelsen på en hash-tabel er 10
og k = 112
derefter h(k) = 112
mod 10 = 2
. Værdien af m
må ikke være beføjelserne til 2
. Dette skyldes, at beføjelserne 2
i binært format er 10, 100, 1000,…
. Når vi finder k mod m
, får vi altid p-bits af lavere ordre.
if m = 22, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 01 if m = 23, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 001 if m = 24, k = 17, then h(k) = 17 mod 22 = 10001 mod 100 = 0001 if m = 2p, then h(k) = p lower bits of m
Multiplication Method
h(k) = ⌊m(kA mod 1)⌋
where,
kA mod 1
gives the fractional partkA
,⌊ ⌋
gives the floor valueA
is any constant. The value ofA
lies between 0 and 1. But, an optimal choice will be≈ (√5-1)/2
suggested by Knuth.
Universal Hashing
In Universal hashing, the hash function is chosen at random independent of keys.
Open Addressing
Multiple values can be stored in a single slot in a normal hash table.
By using open addressing, each slot is either filled with a single key or left NIL
. All the elements are stored in the hash table itself.
Unlike chaining, multiple elements cannot be fit into the same slot.
Open addressing is basically a collision resolving technique. Some of the methods used by open addressing are:
Linear Probing
In linear probing, collision is resolved by checking the next slot.h(k, i) = (h'(k) + i) mod m
where,
i = (0, 1,… .)
h'(k)
is a new hash function
If a collision occurs at h(k, 0)
, then h(k, 1)
is checked. In this way, the value of i
is incremented linearly.
The problem with linear probing is that a cluster of adjacent slots is filled. When inserting a new element, the entire cluster must be traversed. This adds to the time required to perform operations on the hash table.
Quadratic Probing
In quadratic probing, the spacing between the slots is increased (greater than one) by using the following relation.h(k, i) = (h'(k) + c1i + c2i2) mod m
where,
c1
og er positive hjælpekonstanter,c2
i = (0, 1,… .)
Dobbelt hashing
Hvis der opstår en kollision efter anvendelse af en hash-funktion h(k)
, beregnes en anden hash-funktion til at finde den næste plads.h(k, i) = (h1(k) + ih2(k)) mod m
Hash tabel applikationer
Hash-tabeller implementeres hvor
- konstant tidsopslag og indsættelse er påkrævet
- kryptografiske applikationer
- indekseringsdata er påkrævet