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
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
### 📊 Repository Stats

[![Last Commit](https://img.shields.io/github/last-commit/mathusanm6/LeetCode?style=for-the-badge&logo=git&logoColor=white&color=blue)](https://github.com/mathusanm6/LeetCode/commits/main)
[![C++ Solutions](https://img.shields.io/badge/C%2B%2B%20Solutions-7-blue?style=for-the-badge&logo=cplusplus&logoColor=white)](https://github.com/mathusanm6/LeetCode/tree/main/problems)
[![Python Solutions](https://img.shields.io/badge/Python%20Solutions-7-blue?style=for-the-badge&logo=python&logoColor=white)](https://github.com/mathusanm6/LeetCode/tree/main/problems)
[![C++ Solutions](https://img.shields.io/badge/C%2B%2B%20Solutions-8-blue?style=for-the-badge&logo=cplusplus&logoColor=white)](https://github.com/mathusanm6/LeetCode/tree/main/problems)
[![Python Solutions](https://img.shields.io/badge/Python%20Solutions-8-blue?style=for-the-badge&logo=python&logoColor=white)](https://github.com/mathusanm6/LeetCode/tree/main/problems)

</div>

Expand Down Expand Up @@ -205,3 +205,9 @@ This repository covers a comprehensive range of algorithmic patterns and data st
| # | Title | Solution | Time | Space | Difficulty | Tag | Note |
|---|-------|----------|------|-------|------------|-----|------|
| 1087 | [Brace Expansion](https://leetcode.com/problems/brace-expansion/) | [Python](./problems/brace_expansion/brace_expansion.py), [C++](./problems/brace_expansion/brace_expansion.cc) | _O(M^K + M log M)_ | _O(M^K)_ | Medium | | M = max choices per brace set, K = number of brace sets. M^K for generating combinations, M log M for sorting. |

## Graphs

| # | Title | Solution | Time | Space | Difficulty | Tag | Note |
|---|-------|----------|------|-------|------------|-----|------|
| 399 | [Evaluate Division](https://leetcode.com/problems/evaluate-division/) | [Python](./problems/evaluate_division/evaluate_division.py), [C++](./problems/evaluate_division/evaluate_division.cc) | _O(N + M)_ | _O(N)_ | Medium | | N = number of equations, M = number of queries. |
17 changes: 17 additions & 0 deletions problems/evaluate_division/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
problem:
number: 399
title: "Evaluate Division"
leetcode_url: "https://leetcode.com/problems/evaluate-division/"
difficulty: "medium"
tags: ["Graphs"]

solutions:
python: "problems/evaluate_division/evaluate_division.py"
cpp: "problems/evaluate_division/evaluate_division.cc"

complexity:
time: "O(N + M)"
space: "O(N)"

notes: "N = number of equations, M = number of queries."
readme_link: ""
78 changes: 78 additions & 0 deletions problems/evaluate_division/evaluate_division.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "evaluate_division.h"

#include <cstddef>
#include <string>
#include <unordered_map>
#include <vector>

using std::string;
using std::unordered_map;
using std::vector;

namespace {
std::unordered_map<std::string, std::string> parent;
std::unordered_map<std::string, double> weight;

// NOLINTBEGIN(misc-no-recursion)
string find(const string& node, unordered_map<string, string>& parent,
unordered_map<string, double>& weight) {
if (parent[node] != node) {
const string originalParent = parent[node];
parent[node] = find(originalParent, parent, weight);
weight[node] *= weight[originalParent];
}
return parent[node];
}
// NOLINTEND(misc-no-recursion)
} // namespace

vector<double> evaluateDivision(const vector<vector<string>>& equations,
const vector<double>& values,
const vector<vector<string>>& queries) {
// Clear previous state
parent.clear();
weight.clear();

const size_t numEquations = equations.size();

for (const auto& equation : equations) {
parent[equation[0]] = equation[0];
parent[equation[1]] = equation[1];
weight[equation[0]] = 1.0;
weight[equation[1]] = 1.0;
}

for (size_t i = 0; i < numEquations; ++i) {
const vector<string>& equation = equations[i];
const string& numerator = equation[0];
const string& denominator = equation[1];

const string parentNumerator = find(numerator, parent, weight);
const string parentDenominator = find(denominator, parent, weight);

if (parentNumerator == parentDenominator) {
continue;
}

parent[parentNumerator] = parentDenominator;
weight[parentNumerator] = values[i] * weight[denominator] / weight[numerator];
}

const size_t numQueries = queries.size();
vector<double> results(numQueries);

for (size_t i = 0; i < numQueries; ++i) {
const vector<string>& query = queries[i];
const string& numerator = query[0];
const string& denominator = query[1];

if (!parent.contains(numerator) || !parent.contains(denominator) ||
find(numerator, parent, weight) != find(denominator, parent, weight)) {
results[i] = -1.0;
} else {
results[i] = weight[numerator] / weight[denominator];
}
}

return results;
}
6 changes: 6 additions & 0 deletions problems/evaluate_division/evaluate_division.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <string>
#include <vector>

std::vector<double> evaluateDivision(const std::vector<std::vector<std::string>>& equations,
const std::vector<double>& values,
const std::vector<std::vector<std::string>>& queries);
43 changes: 43 additions & 0 deletions problems/evaluate_division/evaluate_division.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import List
from collections import defaultdict


def evaluateDivision(
equations: List[List[str]], values: List[float], queries: List[List[str]]
) -> List[float]:
def find(node: str) -> str:
if parent[node] != node:
original_parent = parent[node]
parent[node] = find(original_parent)
weight[node] *= weight[original_parent]
return parent[node]

weight = defaultdict(lambda: 1.0)
parent = defaultdict(str)

for numerator, denominator in equations:
parent[numerator] = numerator
parent[denominator] = denominator

for (numerator, denominator), value in zip(equations, values):
root_numerator = find(numerator)
root_denominator = find(denominator)

if root_numerator == root_denominator:
continue

parent[root_numerator] = root_denominator
weight[root_numerator] = value * weight[denominator] / weight[numerator]

results = []
for numerator, denominator in queries:
if (
numerator not in parent
or denominator not in parent
or find(numerator) != find(denominator)
):
results.append(-1.0)
else:
results.append(weight[numerator] / weight[denominator])

return results
42 changes: 42 additions & 0 deletions problems/evaluate_division/evaluate_division_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "evaluate_division.h"

#include <gtest/gtest.h>
#include <string>
#include <vector>

struct EvaluateDivisionCase {
std::string test_name;
std::vector<std::vector<std::string>> equations;
std::vector<double> values;
std::vector<std::vector<std::string>> queries;
std::vector<double> expected;
};

using EvaluateDivisionTest = ::testing::TestWithParam<EvaluateDivisionCase>;

TEST_P(EvaluateDivisionTest, TestCases) {
const EvaluateDivisionCase &testCase = GetParam();
const auto result = evaluateDivision(testCase.equations, testCase.values, testCase.queries);
EXPECT_EQ(result, testCase.expected);
}

INSTANTIATE_TEST_SUITE_P(
EvaluateDivisionTestCases, EvaluateDivisionTest,
::testing::Values(
EvaluateDivisionCase{
.test_name = "example1",
.equations = {{"a", "b"}, {"b", "c"}},
.values = {2.0, 3.0},
.queries = {{"a", "c"}, {"b", "a"}, {"a", "e"}, {"a", "a"}, {"x", "x"}},
.expected = {6.00000, 0.50000, -1.00000, 1.00000, -1.00000}},
EvaluateDivisionCase{.test_name = "example2",
.equations = {{"a", "b"}, {"b", "c"}, {"bc", "cd"}},
.values = {1.5, 2.5, 5.0},
.queries = {{"a", "c"}, {"c", "b"}, {"bc", "cd"}, {"cd", "bc"}},
.expected = {3.75000, 0.40000, 5.00000, 0.20000}},
EvaluateDivisionCase{.test_name = "example3",
.equations = {{"a", "b"}},
.values = {0.5},
.queries = {{"a", "b"}, {"b", "a"}, {"a", "c"}, {"x", "y"}},
.expected = {0.50000, 2.00000, -1.00000, -1.00000}}),
[](const testing::TestParamInfo<EvaluateDivisionCase> &info) { return info.param.test_name; });
33 changes: 33 additions & 0 deletions problems/evaluate_division/evaluate_division_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Test cases for the evaluate_division function."""

import pytest

from evaluate_division import evaluateDivision


@pytest.mark.parametrize(
"equations, values, queries, expected",
[
(
[["a", "b"], ["b", "c"]],
[2.0, 3.0],
[["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"]],
[6.00000, 0.50000, -1.00000, 1.00000, -1.00000],
), # Example 1
(
[["a", "b"], ["b", "c"], ["bc", "cd"]],
[1.5, 2.5, 5.0],
[["a", "c"], ["c", "b"], ["bc", "cd"], ["cd", "bc"]],
[3.75000, 0.40000, 5.00000, 0.20000],
), # Example 2
(
[["a", "b"]],
[0.5],
[["a", "b"], ["b", "a"], ["a", "c"], ["x", "y"]],
[0.50000, 2.00000, -1.00000, -1.00000],
), # Example 3
],
ids=["example_1", "example_2", "example_3"],
)
def test_evaluate_division(equations, values, queries, expected):
assert evaluateDivision(equations, values, queries) == expected