Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# [Dictionaries and Hashmaps: Frequency Queries](https://www.hackerrank.com/challenges/frequency-queries)

- Difficulty: `#medium`
- Category: `#ProblemSolvingIntermediate` `#DictionariesAndHashmaps`

## First solution

The first solution is based on the idea of ​​storing the values
​​of insert and delete operations in a dictionary.

For the "select" operation, a search loop is made to find the expected frequency,
which in the best case is cut off when the value is found,
but in the worst case it goes through the entire dictionary.

## Optimized solution

The optimized solution tries to reduce the worst case problem above,
reducing the select operation to direct access to the expected frequency.

To achieve this, it is necessary to maintain an "inverted" dictionary
where the frequency values ​​are the keys and the values ​​of each element
are stored in a list of values ​​that have the same frequency.

To maintain this structure, any operation that alters the data (insert, delete),
must update "in which frequency group" the element will be.

This solution transfers the cost of the search to the data update operations.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from '@jest/globals';
import { logger as console } from '../../../logger';

import { freqQuery } from './frequency_queries_bruteforce';
import SMALL_TEST_CASES from './frequency_queries_testcases.json';

describe('frequency_queries', () => {
it('freqQuery test cases', () => {
expect.assertions(4);

SMALL_TEST_CASES.forEach((value) => {
const answer = freqQuery(value.input);

console.debug(`freqQuery(${value.input}) solution found: ${answer}`);

expect(answer).toStrictEqual(value.expected);
});
});

it('freqQuery border case', () => {
expect.assertions(1);

expect(() => {
freqQuery([[4, 1]]);
}).toThrow('Invalid operation');
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries.md]]
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries-solution-notes.md]]
*/

// Complete the freqQuery function below.
Expand All @@ -12,6 +13,9 @@ export function freqQuery(queries: number[][]): number[] {
const __DELETE__ = 2;
const __SELECT__ = 3;

const __NOT_FOUND__ = 0;
const __FOUND__ = 1;

queries.forEach((query) => {
const [operation, data] = query;

Expand All @@ -24,18 +28,15 @@ export function freqQuery(queries: number[][]): number[] {
case __DELETE__:
data_map[data] = Math.max(0, current - 1);
break;
case __SELECT__:
for (const [key, value] of Object.entries(data_map)) {
console.log(key, value);
if (value == data) {
result.push(1);
break;
}
}
if (result.length == 0) {
result.push(0);
case __SELECT__: {
const uniqueDatavalues = new Set(Object.values(data_map));
if (uniqueDatavalues.has(data)) {
result.push(__FOUND__);
} else {
result.push(__NOT_FOUND__);
}
break;
}
default:
throw new Error('Invalid operation');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
import { describe, expect, it } from '@jest/globals';
import { logger as console } from '../../../logger';

import { freqQuery } from './frequency_queries';

const TEST_CASES = [
{
title: 'Sample Test Case 0',
input: [
[1, 5],
[1, 6],
[3, 2],
[1, 10],
[1, 10],
[1, 6],
[2, 5],
[3, 2]
],
expected: [0, 1]
}
];
import { freqQuery } from './frequency_queries_optimized';
import TEST_CASES from './frequency_queries_testcases.json';

describe('frequency_queries', () => {
it('freqQuery test cases', () => {
expect.assertions(1);
expect.assertions(4);

TEST_CASES.forEach((value) => {
const answer = freqQuery(value.input);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* @link Problem definition [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries.md]]
* @see Solution Notes: [[docs/hackerrank/interview_preparation_kit/dictionaries_and_hashmaps/frequency-queries-solution-notes.md]]
*/

export function updateFrequency(
frequencyMap: Record<number, number[]>,
data: number,
currentFreq: number,
newFreq: number
): Record<number, number[]> {
const freqMap = frequencyMap;

if (newFreq > 0) {
if (freqMap?.[newFreq]) {
freqMap[newFreq].push(data);
} else {
freqMap[newFreq] = [data];
}
}

if (freqMap?.[currentFreq]) {
freqMap[currentFreq] = freqMap[currentFreq].filter(
(f: number) => f !== data
);

if (freqMap[currentFreq].length === 0) {
delete freqMap?.[currentFreq];
}
}

return freqMap;
}

export function freqQuery(queries: number[][]): number[] {
const result: number[] = [];
const dataMap: Record<number, number> = {};
const freqMap: Record<number, number[]> = {};

const __INITIAL__ = 0;
const __INSERT__ = 1;
const __DELETE__ = 2;
const __SELECT__ = 3;

const __NOT_FOUND__ = 0;
const __FOUND__ = 1;

queries.forEach((query) => {
const [operation, data] = query;

const currentFreq = dataMap?.[data] ?? __INITIAL__;
let newFreq = currentFreq + 1;

switch (operation) {
case __INSERT__:
// map of values
dataMap[data] = currentFreq + 1;

// map of frequencies
newFreq = currentFreq + 1;
break;
case __DELETE__:
// map of values
dataMap[data] = Math.max(0, currentFreq - 1);

// map of frequencies
newFreq = currentFreq - 1;

break;
case __SELECT__: {
if (freqMap?.[data]) {
result.push(__FOUND__);
} else {
result.push(__NOT_FOUND__);
}
break;
}
default:
throw new Error('Invalid operation');
}

if (operation === __INSERT__ || operation === __DELETE__) {
updateFrequency(freqMap, data, currentFreq, newFreq);
}
});

return result;
}

export default { freqQuery };
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[
{
"title": "Sample Test Case 0",
"input": [
[1, 5],
[1, 6],
[3, 2],
[1, 10],
[1, 10],
[1, 6],
[2, 5],
[3, 2]
],
"expected": [0, 1]
},
{
"title": "Sample Test Case 1",
"input": [
[3, 4],
[2, 1003],
[1, 16],
[3, 1]
],
"expected": [0, 1]
},
{
"title": "Sample Test Case 2",
"input": [
[1, 3],
[2, 3],
[3, 2],
[1, 4],
[1, 5],
[1, 5],
[1, 4],
[3, 2],
[2, 4],
[3, 2]
],
"expected": [0, 1, 1]
},
{
"title": "Sample Test Case 3",
"input": [
[1, 3],
[1, 38],
[2, 1],
[1, 16],
[2, 1],
[2, 2],
[1, 64],
[1, 84],
[3, 1],
[1, 100],
[1, 10],
[2, 2],
[2, 1],
[1, 67],
[2, 2],
[3, 1],
[1, 99],
[1, 32],
[1, 58],
[3, 2]
],
"expected": [1, 1, 0]
}
]