Skip to content

Commit de89be7

Browse files
authored
Merge pull request #483 from sir-gon/feature/frequency_queries
Feature/frequency queries
2 parents 71cd7a6 + 0b7b1f9 commit de89be7

File tree

7 files changed

+221
-32
lines changed

7 files changed

+221
-32
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# [Dictionaries and Hashmaps: Frequency Queries](https://www.hackerrank.com/challenges/frequency-queries)
2+
3+
- Difficulty: `#medium`
4+
- Category: `#ProblemSolvingIntermediate` `#DictionariesAndHashmaps`
5+
6+
## First solution
7+
8+
The first solution is based on the idea of ​​storing the values
9+
​​of insert and delete operations in a dictionary.
10+
11+
For the "select" operation, a search loop is made to find the expected frequency,
12+
which in the best case is cut off when the value is found,
13+
but in the worst case it goes through the entire dictionary.
14+
15+
## Optimized solution
16+
17+
The optimized solution tries to reduce the worst case problem above,
18+
reducing the select operation to direct access to the expected frequency.
19+
20+
To achieve this, it is necessary to maintain an "inverted" dictionary
21+
where the frequency values ​​are the keys and the values ​​of each element
22+
are stored in a list of values ​​that have the same frequency.
23+
24+
To maintain this structure, any operation that alters the data (insert, delete),
25+
must update "in which frequency group" the element will be.
26+
27+
This solution transfers the cost of the search to the data update operations.

docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# [Dictionaries and Hashmaps: Frequency Queries](https://www.hackerrank.com/challenges/frequency-queries)
22

33
- Difficulty: `#medium`
4-
- Category: `#ProblemSolvingIntermediate`
4+
- Category: `#ProblemSolvingIntermediate` `#DictionariesAndHashmaps`
55

66
You are given queries. Each query is of the form two integers described below:
77

src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency_queries.js renamed to src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency_queries_bruteforce.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/**
22
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries.md]]
3+
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries-solution-notes.md]]
34
*/
45

5-
// Complete the freqQuery function below.
66
export function freqQuery(queries) {
77
const result = [];
88
const dataMap = {};
@@ -12,6 +12,9 @@ export function freqQuery(queries) {
1212
const __DELETE__ = 2;
1313
const __SELECT__ = 3;
1414

15+
const __NOT_FOUND__ = 0;
16+
const __FOUND__ = 1;
17+
1518
queries.forEach((query) => {
1619
const [operation, data] = query;
1720

@@ -24,18 +27,15 @@ export function freqQuery(queries) {
2427
case __DELETE__:
2528
dataMap[data] = Math.max(0, current - 1);
2629
break;
27-
case __SELECT__:
28-
for (const [key, value] of Object.entries(dataMap)) {
29-
console.log(key, value);
30-
if (value === data) {
31-
result.push(1);
32-
break;
33-
}
34-
}
35-
if (result.length === 0) {
36-
result.push(0);
30+
case __SELECT__: {
31+
const uniqueDatavalues = new Set(Object.values(dataMap));
32+
if (uniqueDatavalues.has(data)) {
33+
result.push(__FOUND__);
34+
} else {
35+
result.push(__NOT_FOUND__);
3736
}
3837
break;
38+
}
3939
default:
4040
throw new Error('Invalid operation');
4141
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
import { logger as console } from '../../../logger.js';
3+
4+
import { freqQuery } from './frequency_queries_bruteforce.js';
5+
import SMALL_TEST_CASES from './frequency_queries_testcases.json';
6+
7+
describe('frequency_queries', () => {
8+
it('freqQuery test cases', () => {
9+
expect.assertions(4);
10+
11+
SMALL_TEST_CASES.forEach((value) => {
12+
const answer = freqQuery(value.input);
13+
14+
console.debug(`freqQuery(${value.input}) solution found: ${answer}`);
15+
16+
expect(answer).toStrictEqual(value.expected);
17+
});
18+
});
19+
20+
it('freqQuery border case', () => {
21+
expect.assertions(1);
22+
23+
expect(() => {
24+
freqQuery([[4, 1]]);
25+
}).toThrow('Invalid operation');
26+
});
27+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries.md]]
3+
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries-solution-notes.md]]
4+
*/
5+
6+
export function updateFrequency(frequencyMap, data, currentFreq, newFreq) {
7+
const freqMap = frequencyMap;
8+
9+
if (newFreq > 0) {
10+
if (freqMap?.[newFreq]) {
11+
freqMap[newFreq].push(data);
12+
} else {
13+
freqMap[newFreq] = [data];
14+
}
15+
}
16+
17+
if (freqMap?.[currentFreq]) {
18+
freqMap[currentFreq] = freqMap[currentFreq].filter((f) => f !== data);
19+
20+
if (freqMap[currentFreq].length === 0) {
21+
delete freqMap?.[currentFreq];
22+
}
23+
}
24+
25+
return freqMap;
26+
}
27+
28+
export function freqQuery(queries) {
29+
const result = [];
30+
const dataMap = {};
31+
const freqMap = {};
32+
33+
const __INITIAL__ = 0;
34+
const __INSERT__ = 1;
35+
const __DELETE__ = 2;
36+
const __SELECT__ = 3;
37+
38+
const __NOT_FOUND__ = 0;
39+
const __FOUND__ = 1;
40+
41+
queries.forEach((query) => {
42+
const [operation, data] = query;
43+
44+
const currentFreq = dataMap?.[data] ?? __INITIAL__;
45+
let newFreq = currentFreq + 1;
46+
47+
switch (operation) {
48+
case __INSERT__:
49+
// map of values
50+
dataMap[data] = currentFreq + 1;
51+
52+
// map of frequencies
53+
newFreq = currentFreq + 1;
54+
break;
55+
case __DELETE__:
56+
// map of values
57+
dataMap[data] = Math.max(0, currentFreq - 1);
58+
59+
// map of frequencies
60+
newFreq = currentFreq - 1;
61+
62+
break;
63+
case __SELECT__: {
64+
if (freqMap?.[data]) {
65+
result.push(__FOUND__);
66+
} else {
67+
result.push(__NOT_FOUND__);
68+
}
69+
break;
70+
}
71+
default:
72+
throw new Error('Invalid operation');
73+
}
74+
75+
if (operation === __INSERT__ || operation === __DELETE__) {
76+
updateFrequency(freqMap, data, currentFreq, newFreq);
77+
}
78+
});
79+
80+
return result;
81+
}
82+
83+
export default { freqQuery };

src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency_queries.test.js renamed to src/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency_queries_optimized.test.js

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,17 @@
11
import { describe, expect, it } from '@jest/globals';
22
import { logger as console } from '../../../logger.js';
33

4-
import { freqQuery } from './frequency_queries.js';
5-
6-
const TEST_CASES = [
7-
{
8-
title: 'Sample Test Case 0',
9-
input: [
10-
[1, 5],
11-
[1, 6],
12-
[3, 2],
13-
[1, 10],
14-
[1, 10],
15-
[1, 6],
16-
[2, 5],
17-
[3, 2]
18-
],
19-
expected: [0, 1]
20-
}
21-
];
4+
import { freqQuery } from './frequency_queries_optimized.js';
5+
import TEST_CASES from './frequency_queries_testcases.json';
226

237
describe('frequency_queries', () => {
248
it('freqQuery test cases', () => {
25-
expect.assertions(1);
9+
expect.assertions(4);
2610

2711
TEST_CASES.forEach((value) => {
2812
const answer = freqQuery(value.input);
2913

30-
console.debug(`checkMagazine(${value.input}) solution found: ${answer}`);
14+
console.debug(`freqQuery(${value.input}) solution found: ${answer}`);
3115

3216
expect(answer).toStrictEqual(value.expected);
3317
});
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
[
2+
{
3+
"title": "Sample Test Case 0",
4+
"input": [
5+
[1, 5],
6+
[1, 6],
7+
[3, 2],
8+
[1, 10],
9+
[1, 10],
10+
[1, 6],
11+
[2, 5],
12+
[3, 2]
13+
],
14+
"expected": [0, 1]
15+
},
16+
{
17+
"title": "Sample Test Case 1",
18+
"input": [
19+
[3, 4],
20+
[2, 1003],
21+
[1, 16],
22+
[3, 1]
23+
],
24+
"expected": [0, 1]
25+
},
26+
{
27+
"title": "Sample Test Case 2",
28+
"input": [
29+
[1, 3],
30+
[2, 3],
31+
[3, 2],
32+
[1, 4],
33+
[1, 5],
34+
[1, 5],
35+
[1, 4],
36+
[3, 2],
37+
[2, 4],
38+
[3, 2]
39+
],
40+
"expected": [0, 1, 1]
41+
},
42+
{
43+
"title": "Sample Test Case 3",
44+
"input": [
45+
[1, 3],
46+
[1, 38],
47+
[2, 1],
48+
[1, 16],
49+
[2, 1],
50+
[2, 2],
51+
[1, 64],
52+
[1, 84],
53+
[3, 1],
54+
[1, 100],
55+
[1, 10],
56+
[2, 2],
57+
[2, 1],
58+
[1, 67],
59+
[2, 2],
60+
[3, 1],
61+
[1, 99],
62+
[1, 32],
63+
[1, 58],
64+
[3, 2]
65+
],
66+
"expected": [1, 1, 0]
67+
}
68+
]

0 commit comments

Comments
 (0)