Showing posts with label Hashing. Show all posts
Showing posts with label Hashing. Show all posts

Thursday, June 30, 2022

Problem's asked during Competitive Programming for Hashing

 So this blog will be dedicated to all the questions asked in technical rounds of competitive programming. These questions are like bible/gita for programmers you should be aware of these concepts because the solution can be given using hashing only. I would suggest solve these problems using hashing concepts.

Problems are below:

1. Count Non-Repeated Elements 

Hashing is very useful to keep track of the frequency of the elements in a list.

You are given an array of integers. You need to print the count of non-repeated elements in the array.

Example 1:

Input:

10

1 1 2 2 3 3 4 5 6 7

Output: 

4

Explanation: 

4, 5, 6 and 7 are the 

elements with frequency 1 and rest 

elements are repeated so the number 

of non-repeated elements are 4.

2. Print Non-Repeated Elements 

Hashing is very useful to keep track of the frequency of the elements in a list.

You are given an array of integers. You need to print the non-repeated elements as they appear in the array.

Example 1:

Input:

n = 10

arr[] = {1,1,2,2,3,3,4,5,6,7}

Output: 4 5 6 7

Explanation: 4, 5, 6 and 7 are the only 

elements which is having only 1 

frequency and hence, Non-repeating.

3.First Repeating Element 

Given an array arr[] of size n, find the first repeating element. The element should occurs more than once and the index of its first occurrence should be the smallest.

Example 1:

Input:

n = 7

arr[] = {1, 5, 3, 4, 3, 5, 6}

Output: 2

Explanation: 

5 is appearing twice and 

its first appearence is at index 2 

which is less than 3 whose first 

occuring index is 3.

4.Intersection of two arrays 

Given two arrays a[] and b[] respectively of size n and m, the task is to print the count of elements in the intersection (or common elements) of the two arrays.

For this question, the intersection of two arrays can be defined as the set containing distinct common elements between the two arrays. 

Example 1:

Input:

n = 5, m = 3

a[] = {89, 24, 75, 11, 23}

b[] = {89, 2, 4}

Output: 1

Explanation: 

89 is the only element 

in the intersection of two arrays.

5.Union of two arrays 

Given two arrays a[] and b[] of size n and m respectively. The task is to find union between these two arrays.

Union of the two arrays can be defined as the set containing distinct elements from both the arrays. If there are repetitions, then only one occurrence of element should be printed in the union.

Example 1:

Input:

5 3

1 2 3 4 5

1 2 3

Output: 

5

Explanation: 

1, 2, 3, 4 and 5 are the

elements which comes in the union set

of both arrays. So count is 5.

6.Check if two arrays are equal or not 

This problem is part of GFG SDE Sheet. Click here to view more.   

Given two arrays A and B of equal size N, the task is to find if given arrays are equal or not. Two arrays are said to be equal if both of them contain same set of elements, arrangements (or permutation) of elements may be different though.

Note : If there are repetitions, then counts of repeated elements must also be same for two array to be equal.

Example 1:

Input:

N = 5

A[] = {1,2,5,4,0}

B[] = {2,4,5,0,1}

Output: 1

Explanation: Both the array can be 

rearranged to {0,1,2,4,5}

7.Subarray with 0 sum 

Given an array of positive and negative numbers. Find if there is a subarray (of size at-least one) with 0 sum.

Example 1:

Input:

5

4 2 -3 1 6

Output: 

Yes

Explanation: 

2, -3, 1 is the subarray 

with sum 0.

8.Winner of an election 

Given an array of names (consisting of lowercase characters) of candidates in an election. A candidate name in array represents a vote casted to the candidate. Print the name of candidate that received Max votes. If there is tie, print lexicographically smaller name.

Example 1:

Input:

n = 13

Votes[] = {john,johnny,jackie,johnny,john 

jackie,jamie,jamie,john,johnny,jamie,

johnny,john}

Output: john 4

Explanation: john has 4 votes casted for 

him, but so does johny. john is 

lexicographically smaller, so we print 

john and the votes he received.

9.Subarray range with given sum 

Given an unsorted array of integers and a sum. The task is to count the number of subarray which adds to the given sum.

Example 1:

Input:

n = 5

arr[] = {10,2,-2,-20,10}

sum = -10

Output: 3

Explanation: Subarrays with sum -10 are: 

[10, 2, -2, -20], [2, -2, -20, 10] and 

[-20, 10].


10.Positive Negative Pair 

Given an array of distinct integers, find all the pairs having both negative and positive values of a number in the array.

Example 1:

Input:

n = 8

arr[] = {1,3,6,-2,-1,-3,2,7}

Output: -1 1 -3 3 -2 2

Explanation: 1, 3 and 2 are present 

pairwise positive and negative. 6 and 

7 have no pair.


11.Zero Sum Subarrays 

You are given an array arr[] of size n. Find the total count of sub-arrays having their sum equal to 0.

Example 1:

Input:

n = 6

arr[] = {0,0,5,5,0,0}

Output: 6

Explanation: The 6 subarrays are 

[0], [0], [0], [0], [0,0], and [0,0].

12.Subarrays with equal 1s and 0s 

Given an array containing 0s and 1s. Find the number of subarrays having equal number of 0s and 1s.

Example 1:

Input:

n = 7

A[] = {1,0,0,1,0,1,1}

Output: 8

Explanation: The index range for the 8 

sub-arrays are: (0, 1), (2, 3), (0, 3), (3, 4), 

(4, 5) ,(2, 5), (0, 5), (1, 6)

13.Sort an array according to the other 

This problem is part of GFG SDE Sheet. Click here to view more.   

Given two integer arrays A1[ ] and A2[ ] of size N and M respectively. Sort the first array A1[ ] such that all the relative positions of the elements in the first array are the same as the elements in the second array A2[ ].

See example for better understanding.

Note: If elements are repeated in the second array, consider their first occurance only.

Example 1:


Input:

N = 11 

M = 4

A1[] = {2, 1, 2, 5, 7, 1, 9, 3, 6, 8, 8}

A2[] = {2, 1, 8, 3}

Output: 

2 2 1 1 8 8 3 5 6 7 9

Explanation: Array elements of A1[] are

sorted according to A2[]. So 2 comes first

then 1 comes, then comes 8, then finally 3

comes, now we append remaining elements in

sorted order.


14. Sorting Elements of an Array by Frequency 

Medium Accuracy: 47.44% Submissions: 32039 Points: 4

Given an array of integers, sort the array according to frequency of elements. That is elements that have higher frequency come first. If frequencies of two elements are same, then smaller number comes first.

Example 1:

Input:

N = 5

A[] = {5,5,4,6,4}

Output: 4 4 5 5 6

Explanation: The highest frequency here is

2. Both 5 and 4 have that frequency. Now

since the frequencies are same then 

smallerelement comes first. So 4 4 comes 

firstthen comes 5 5. Finally comes 6.

The output is 4 4 5 5 6.

Note : if you are able to solve these problems then consider your self good for Hashing.


Tuesday, June 28, 2022

Hashing - Peeling Concept and Knowledge for Interview

 Hashing is a method of storing and retrieving data from a database efficiently.


Suppose that we want to design a system for storing employee records keyed using phone numbers. And we want the following queries to be performed efficiently:
  1. Insert a phone number and the corresponding information.
  2. Search a phone number and fetch the information.
  3. Delete a phone number and the related information.
We can think of using the following data structures to maintain information about different phone numbers.

  1. An array of phone numbers and records.
  2. A linked list of phone numbers and records.
  3. A balanced binary search tree with phone numbers as keys.
  4. A direct access table.
For arrays and linked lists, we need to search in a linear fashion, which can be costly in practice. If we use arrays and keep the data sorted, then a phone number can be searched in O(Logn) time using Binary Search, but insert and delete operations become costly as we have to maintain sorted order.

With a balanced binary search tree, we get a moderate search, insert and delete time. All of these operations can be guaranteed to be in O(Logn) time.

Another solution that one can think of is to use a direct access table where we make a big array and use phone numbers as indexes in the array. An entry in the array is NIL if the phone number is not present, else the array entry stores pointer to records corresponding to the phone number. Time complexity wise this solution is the best of all, we can do all operations in O(1) time. For example, to insert a phone number, we create a record with details of the given phone number, use the phone number as an index and store the pointer to the record created in the table.
This solution has many practical limitations. The first problem with this solution is that the extra space required is huge. For example, if the phone number is of n digits, we need O(m * 10n) space for the table where m is the size of a pointer to the record. Another problem is an integer in a programming language may not store n digits.

Due to the above limitations, the Direct Access Table cannot always be used. Hashing is the solution that can be used in almost all such situations and performs extremely well as compared to above data structures like Array, Linked List, Balanced BST in practice. With hashing, we get O(1) search time on average (under reasonable assumptions) and O(n) in the worst case.

Hashing is an improvement over Direct Access Table. The idea is to use a hash function that converts a given phone number or any other key to a smaller number and uses the small number as an index in a table called a hash table.

Hash Function: A function that converts a given big phone number to a small practical integer value. The mapped integer value is used as an index in the hash table. In simple terms, a hash function maps a big number or string to a small integer that can be used as an index in the hash table.

A good hash function should have following properties:

  1. It should be efficiently computable.
  2. It should uniformly distribute the keys (Each table position be equally likely for each key).

For example, for phone numbers, a bad hash function is to take the first three digits. A better function will consider the last three digits. Please note that this may not be the best hash function. 

There may be better ways.

Hash Table: An array that stores pointers to records corresponding to a given phone number. An entry in hash table is NIL if no existing phone number has hash function value equal to the index for the entry.

Collision Handling: Since a hash function gets us a small number for a big key, there is a possibility that two keys result in the same value. The situation where a newly inserted key maps to an already occupied slot in the hash table is called collision and must be handled using some collision handling technique. 

Following are the ways to handle collisions:

  • Chaining: The idea is to make each cell of the hash table point to a linked list of records that have the same hash function value. Chaining is simple, but it requires additional memory outside the table.

  • Open Addressing: In open addressing, all elements are stored in the hash table itself. Each table entry contains either a record or NIL. When searching for an element, we one by one examine the table slots until the desired element is found or it is clear that the element is not present in the table.

Open Addressing: Like separate chaining, open addressing is a method for handling collisions. In Open Addressing, all elements are stored in the hash table itself. So at any point, the size of the table must be greater than or equal to the total number of keys (Note that we can increase table size by copying old data if needed).

Important Operations:

  • Insert(k): Keep probing until an empty slot is found. Once an empty slot is found, insert k.
  • Search(k): Keep probing until the slot's key doesn't become equal to k or an empty slot is reached.
  • Delete(k): Delete operation is interesting. If we simply delete a key, then the search may fail. So slots of the deleted keys are marked specially as "deleted".
Insert can insert an item in a deleted slot, but the search doesn't stop at a deleted slot.

Open Addressing is done in the following ways:

  1. Linear Probing: In linear probing, we linearly probe for the next slot. For example, the typical gap between the two probes is 1 as taken in the below example also.
    let hash(x) be the slot index computed using a hash function and S be the table size.

  1. If slot hash(x) % S is full, then we try (hash(x) + 1) % S
    If (hash(x) + 1) % S is also full, then we try (hash(x) + 2) % S
    If (hash(x) + 2) % S is also full, then we try (hash(x) + 3) % S
    ..................................................
    ..................................................

Let us consider a simple hash function as “key mod 7” and a sequence of keys as 50, 700, 76, 85, 92, 73, 101.


Clustering: The main problem with linear probing is clustering, many consecutive elements form groups and it starts taking time to find a free slot or to search an element.

  1. Quadratic Probing We look for i2'th slot in i'th iteration.
    let hash(x) be the slot index computed using hash function.
    If slot hash(x) % S is full, then we try (hash(x) + 1*1) % S
    If (hash(x) + 1*1) % S is also full, then we try (hash(x) + 2*2) % S
    If (hash(x) + 2*2) % S is also full, then we try (hash(x) + 3*3) % S
    ..................................................
    ..................................................
  2. Double Hashing We use another hash function hash2(x) and look for i*hash2(x) slot in i'th rotation.
    let hash(x) be the slot index computed using hash function.
    If slot hash(x) % S is full, then we try (hash(x) + 1*hash2(x)) % S
    If (hash(x) + 1*hash2(x)) % S is also full, then we try (hash(x) + 2*hash2(x)) % S
    If (hash(x) + 2*hash2(x)) % S is also full, then we try (hash(x) + 3*hash2(x)) % S
    ..................................................
    ..................................................
Comparison of above three:

  • Linear probing has the best cache performance but it suffers from clustering. One more advantage of Linear probing that it is easy to compute.
  • Quadratic probing lies between the two in terms of cache performance and clustering.
  • Double hashing has poor cache performance but no clustering. Double hashing requires more computation time as two hash functions need to be computed.
S.No.Seperate ChainingOpen Addressing
1.Chaining is Simpler to implement.Open Addressing requires more computation.
2.In chaining, Hash table never fills up, we can always add more elements to chain.In open addressing, table may become full.
3.Chaining is Less sensitive to the hash function or load factors.Open addressing requires extra care for to avoid clustering and load factor.
4.Chaining is mostly used when it is unknown how many and how frequently keys may be inserted or deleted.Open addressing is used when the frequency and number of keys is known.
5.Cache performance of chaining is not good as keys are stored using linked list.Open addressing provides better cache performance as everything is stored in the same table.
6.Wastage of Space (Some Parts of hash table in chaining are never used).In Open addressing, a slot can be used even if an input doesn't map to it.
7.Chaining uses extra space for links.No links in Open addressing

Performance of Open Addressing: Like Chaining, the performance of hashing can be evaluated under the assumption that each key is equally likely to be hashed to any slot of the table (simple uniform hashing).

 m = Number of slots in the hash table
n = Number of keys to be inserted in the hash table

Load factor α = n/m ( < 1 )

Expected time to search/insert/delete < 1/(1 - α)

So Search, Insert and Delete take (1/(1 - α)) time

Next implementation will come in coming blog.