The implementation of hash is often called hashing ). Hashed columns only support INSERT, SEARCH, and DELETE operations, which are executed at the constant average time. Operations that require any sorting information between elements will not be effectively supported.
A hash is a promotion of the General array concept. If space permits, an array can be provided to reserve a location for each possible keyword, and direct addressing technology can be used.
When the number of actually stored keywords is smaller than the total number of possible keywords, it is more effective to use a scattered list to compare direct addressing. In the hash list, instead of directly using the keyword as the array subscript, the subscript is calculated based on the keyword.
The ing between keywords and subscripts is called a hash function.
1. Hash Function
A good hash function should satisfy the assumption of simple transplantation of hash: each keyword is classified as possible to be hashed to any one of m slots, it is irrelevant to the slot to which other keywords have been hashed.
1.1 generally, the keywords in the hash list are natural numbers.
1.11 division hash
The key word k is divided by the remainder of the slot m to map to a slot.
hash(k)=k mod m
When applying the Division hash, pay attention to the choice of m. m should not be the power of 2. Generally, the prime number is not very close to the power of 2.
1.12 multiplication and discretization
The multiplication method consists of two steps. The first step is to multiply the constant A (0 <A <1) with the keyword k, and then multiply the fractional part by m, obtain the bottom of the result (floor ).
hash(k)=floor(m(kA mod 1))
One advantage of multiplication is that there is no special requirement on m selection. Generally, it is a power of 2.
Generally, it is ideal to use A = (√ 5-1)/2 = 0.618.
1.13 global hash
Randomly select the hash function to make it independent from the keywords to be stored. At the beginning of execution, a random hash function is selected from a family of carefully designed functions. randomization ensures
No input always leads to the worst case.
1.2 If the keyword is a string, the hash function needs to be carefully selected.
1.2.1Adds the ASCII values of characters in a string.
def _hash(key,m): hashVal=0 for _ in key: hashVal+=ord(_) return hashVal%m
Because the maximum ascii code is 127, when the table is large, the function does not properly allocate keywords.
1.2.2Take the first three characters of the keyword.
The value 27 indicates the number of letters in the English alphabet plus a space.
hash(k)=k[0]+27*k[1]+729*k[2]
1.2.3All characters are extended to n polynomial using the Horna law.
Replace 27 with 32, which can be used for bitwise operations.
def _hash(key,m): hashval=0 for _ in key: hashval=(hashval<<5)+ord(_) return hashval%m
2. Separated links
Hash faces a problem. When two keywords are hashed to the same value, they are called collision ). The first method to resolve a conflict is usually called the separate Link Method (separate chaining ).
In this way, all elements hashed to the same value are retained to a linked list, and a pointer pointing to the linked list header is retained in the slot.
To execute FIND, use the hash function to determine the table to be checked, traverse the table, and return the location of the keyword.
To execute INSERT, first determine whether the element is in the table. If it is a new element, insert the front or end of the table.
To execute the DELETE operation, find this element and DELETE it from the linked list.
The ratio of the number of elements in the hash to the size of the hash is called load factor λ.
If an unsuccessful query is executed, the average number of links to be traversed is λ, and 1 + (λ/2) is used for successful search ).
The general practice of separating link hashes is to make λ close to 1 as much as possible.
Code:
class _ListNode(object): def __init__(self,key): self.key=key self.next=Noneclass HashMap(object): def __init__(self,tableSize): self._table=[None]*tableSize self._n=0 #number of nodes in the map def __len__(self): return self._n def _hash(self,key): return abs(hash(key))%len(self._table) def __getitem__(self,key): j=self._hash(key) node=self._table[j] while node is not None and node.key!=key : node=node.next if node is None: raise KeyError,'KeyError'+repr(key) return node def insert(self,key): try: self[key] except KeyError: j=self._hash(key) node=self._table[j] self._table[j]=_ListNode(key) self._table[j].next=node self._n+=1 def __delitem__(self,key): j=self._hash(key) node=self._table[j] if node is not None: if node.key==key: self._table[j]=node.next self._-=1 else: while node.next!=None: pre=node node=node.next if node.key==key: pre.next=node.next self._n-=1 break
3. Open address Method
In the open address hashing algorithm, if a conflict occurs, try to select another unit until an empty unit is found.
H (k, I) = (h '(k) + f (I) mod m, I =,... m-1, where f (0) = 0
3.1 linear probing
Function f (I) is a linear function of I.
H (k, I) = (h '(k) + I) mod m
It is equivalent to probe each unit one by one
Linear detection has a problem called a single cluster. As the number of occupied slots increases, the average search time also increases. When I have a full slot before an empty slot, the empty slot will be occupied by the next one.
The slot probability is (I + 1)/m. The sequence of consecutive slots is getting longer and longer, and the average query time is also increased.
If half of a table is filled, linear detection is not a good solution.
3.2 flat Detection
Square detection can cancel a cluster issue during linear detection.
H (k, I) = (h '(k) + c1i + c2i2) mod m
In the square test, if half of the table is empty and the table size is a prime number, a new element can be inserted.
Square detection may cause secondary cluster problems.
3.3 double hash
Double hash is one of the best ways to enable the addressing method.
H (k, I) = (h1 (k) + ih2 (k) mod m
To search for the entire hash, the value h2 (k) must be in the same quality as m. One way to ensure that this condition is true is to take the power of m to 2 and design a total generation of an odd number of h2. Another method is to take m as the prime number and design a always generate
H2 of a positive integer smaller than m.
For example:
H1 (k) = k mod m, h2 (k) = 1 + (k mod m'), M' is an integer slightly smaller than m.
Given an open address hash list of the loading factor λ, inserting an element requires at most 1/(1-λ) exploration.
Given an open address hash list of the fill factor λ <1, the expected number of probes in a successful search is (1/λ) ln (1/1-λ ).
4. Hash again
If the table element is too full, the running time of the operation will start to be too long. One solution is to create a table about twice the size of a table when it reaches a certain fill factor, and use a related new hash function,
Scan the entire original hash, calculate the new hash value of each element, and insert it into the new table.
In order to avoid the error of opening the Index HASH search, the delete operation should adopt lazy deletion.
Code
Class HashEntry (object): def _ init _ (self, key, value): self. key = key self. value = valueclass HashTable (object): _ DELETED = HashEntry (None, None) # used to delete def _ init _ (self, tablesize): self. _ table = tablesize * [None] self. _ n = 0 def _ len _ (self): return self. _ n def _ getitem _ (self, key): found, j = self. _ findSlot (key) if not found: raise KeyError return self. _ table [j]. value def _ setitem _ (self, key, value): found, j = self. _ findSlot (key) if not found: self. _ table [j] = HashEntry (key, value) self. _ n + = 1 if self. _ n> len (self. _ table) // 2: self. _ rehash () else: self. _ table [j]. value = value def _ delitem _ (self, key): found, j = self. _ findSlot (key) if found: self. _ table [j] = HashTable. _ DELETED # Delete def _ rehash (self): oldList = self. _ table newsize = 2 * len (self. _ table) + 1 self. _ table = newsize * [None] self. _ n = 0 for entry in oldList: if entry is not None and entry is not HashTable. _ DELETED: self [entry. key] = entry. value self. _ n + = 1 def _ findSlot (self, key): slot = self. _ hash1 (key) step = self. _ hash2 (key) firstSlot = None while True: if self. _ table [slot] is None: if firstSlot is None: firstSlot = slot return (False, firstSlot) elif self. _ table [slot] is HashTable. _ DELETED: firstSlot = slot elif self. _ table [slot]. key = key: return (True, slot) slot = (slot + step) % len (self. _ table) def _ hash1 (self, key): return abs (hash (key) % len (self. _ table) def _ hash2 (self, key): return 1 + abs (hash (key) % (len (self. _ table)-2)