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
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,20 @@ This repository covers a comprehensive range of algorithmic patterns and data st
- [Two Pointers](#two-pointers)
- [Sliding Window](#sliding-window)
- [Stack](#stack)
- [Matrix](#matrix)
- [Intervals](#intervals)
- [Binary Search](#binary-search)
- [Linked List](#linked-list)
- [Binary Tree General](#binary-tree-general)
- [Binary Tree BFS](#binary-tree-bfs)
- [Binary Search Tree](#binary-search-tree)
- [Graph General](#graph-general)
- [Dynamic Programming](#dynamic-programming)
- [Trees](#trees)
- [Heap / Priority Queue](#heap-/-priority-queue)
- [Backtracking](#backtracking)
- [Heap](#heap)
- [Tries](#tries)
- [Graphs](#graphs)
- [Advanced Graphs](#advanced-graphs)
- [1-D Dynamic Programming](#1-d-dynamic-programming)
- [2-D Dynamic Programming](#2-d-dynamic-programming)
- [Greedy](#greedy)
- [Trie](#trie)
- [Intervals](#intervals)
- [Math & Geometry](#math--geometry)
- [Bit Manipulation](#bit-manipulation)

## Arrays & Hashing

Expand All @@ -189,3 +191,9 @@ This repository covers a comprehensive range of algorithmic patterns and data st
| # | Title | Solution | Time | Space | Difficulty | Tag | Note |
|---|-------|----------|------|-------|------------|-----|------|
| 125 | [Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) | [Python](./problems/valid_palindrome/valid_palindrome.py), [C++](./problems/valid_palindrome/valid_palindrome.cc) | _O(n)_ | _O(1)_ | Easy | | |

## Trees

| # | Title | Solution | Time | Space | Difficulty | Tag | Note |
|---|-------|----------|------|-------|------------|-----|------|
| 2313 | [Minimum Flips in Binary Tree to Get Result](https://leetcode.com/problems/minimum-flips-in-binary-tree-to-get-result/description/) | [Python](./problems/minimum_flips_in_binary_tree_to_get_result/minimum_flips_in_binary_tree_to_get_result.py), [C++](./problems/minimum_flips_in_binary_tree_to_get_result/minimum_flips_in_binary_tree_to_get_result.cc) | _O(n)_ | _O(1)_ | Hard | | _n_ is the number of nodes in the binary tree. |
Empty file removed common/.gitkeep
Empty file.
3 changes: 3 additions & 0 deletions common/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .trees.treenode import TreeNode

__all__ = ["TreeNode"]
14 changes: 14 additions & 0 deletions common/trees/treenode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef COMMON_TREES_TREENODE_H
#define COMMON_TREES_TREENODE_H

// Definition for a binary tree node.
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

#endif // COMMON_TREES_TREENODE_H
8 changes: 8 additions & 0 deletions common/trees/treenode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Common tree node definition."""

# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
20 changes: 11 additions & 9 deletions config/tags.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ tags:
- "Two Pointers"
- "Sliding Window"
- "Stack"
- "Matrix"
- "Intervals"
- "Binary Search"
- "Linked List"
- "Binary Tree General"
- "Binary Tree BFS"
- "Binary Search Tree"
- "Graph General"
- "Dynamic Programming"
- "Trees"
- "Heap / Priority Queue"
- "Backtracking"
- "Heap"
- "Tries"
- "Graphs"
- "Advanced Graphs"
- "1-D Dynamic Programming"
- "2-D Dynamic Programming"
- "Greedy"
- "Trie"
- "Intervals"
- "Math & Geometry"
- "Bit Manipulation"
17 changes: 17 additions & 0 deletions problems/minimum_flips_in_binary_tree_to_get_result/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
problem:
number: 2313
title: "Minimum Flips in Binary Tree to Get Result"
leetcode_url: "https://leetcode.com/problems/minimum-flips-in-binary-tree-to-get-result/description/"
difficulty: "hard"
tags: ["Trees"]

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

complexity:
time: "O(n)"
space: "O(1)"

notes: "_n_ is the number of nodes in the binary tree."
readme_link: ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "minimum_flips_in_binary_tree_to_get_result.h"

#include <algorithm>
#include <functional>
#include <utility>
#include "../../common/trees/treenode.h"

using namespace std;

int minimumFlips(TreeNode* root, bool result) {
function<pair<int, int>(TreeNode*)> dfs = [&](TreeNode* node) -> pair<int, int> {
if (!node) {
return {1 << 30, 1 << 30}; // Impossible case
}
const int x = node->val;
if (x >= 0 && x <= 1) {
return {x == 0 ? 0 : 1, x == 1 ? 0 : 1};
}

auto [left_false, left_true] = dfs(node->left);
auto [right_false, right_true] = dfs(node->right);
if (x == 2) { // OR
return {left_false + right_false, min({left_true + right_true, left_true + right_false,
left_false + right_true})};
} else if (x == 3) { // AND
return {
min({left_false + right_false, left_true + right_false, left_false + right_true}),
left_true + right_true};
} else if (x == 4) { // XOR
return {min({left_false + right_false, left_true + right_true}),
min({left_true + right_false, left_false + right_true})};
} else if (x == 5) { // NOT
return {left_true, left_false};
} else {
return {1 << 30, 1 << 30}; // Invalid operation
}
};
return result ? dfs(root).second : dfs(root).first;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "../../common/trees/treenode.h"

int minimumFlips(TreeNode* root, bool result);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from common import TreeNode
from math import inf
from typing import Optional, Tuple


def minimumFlips(root: Optional[TreeNode], result: bool) -> int:
def dfs(node: Optional[TreeNode]) -> Tuple[int, int]:
"""Returns a tuple (min_flips_to_false, min_flips_to_true)"""
if node is None:
return inf, inf

x = node.val
if x in (0, 1):
return (1, 0) if x == 1 else (0, 1)

left_false, left_true = dfs(node.left)
right_false, right_true = dfs(node.right)
if x == 2: # OR
return (
left_false + right_false,
min(
left_true + right_false,
left_false + right_true,
left_true + right_true,
),
)
elif x == 3: # AND
return (
min(
left_false + right_false,
left_false + right_true,
left_true + right_false,
),
left_true + right_true,
)
elif x == 4: # XOR
return (
min(left_false + right_false, left_true + right_true),
min(left_false + right_true, left_true + right_false),
)
elif x == 5: # NOT
return (left_true, left_false)
else:
raise ValueError(f"Unknown operation: {x}")

return dfs(root)[int(result)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include "minimum_flips_in_binary_tree_to_get_result.h"

#include <gtest/gtest.h>
#include <string>
#include "../../common/trees/treenode.h"

struct MinimumFlipsCase {
std::string test_name;
TreeNode *root;
bool result;
int expected;
};

using MinimumFlipsTest = ::testing::TestWithParam<MinimumFlipsCase>;

TEST_P(MinimumFlipsTest, TestCases) {
const MinimumFlipsCase &testCase = GetParam();
const int result = minimumFlips(testCase.root, testCase.result);
EXPECT_EQ(result, testCase.expected);
}

INSTANTIATE_TEST_SUITE_P(
MinimumFlipsTestCases, MinimumFlipsTest,
::testing::Values(
MinimumFlipsCase{
.test_name = "Leaf0ToTrue", .root = new TreeNode(0), .result = true, .expected = 1},
MinimumFlipsCase{.test_name = "Leaf1AlreadyTrue",
.root = new TreeNode(1),
.result = true,
.expected = 0},
MinimumFlipsCase{.test_name = "OrGateFlipChild",
.root = new TreeNode(2, new TreeNode(0), new TreeNode(0)),
.result = true,
.expected = 1},
MinimumFlipsCase{.test_name = "OrGateToFalse",
.root = new TreeNode(2, new TreeNode(1), new TreeNode(1)),
.result = false,
.expected = 2},
MinimumFlipsCase{.test_name = "AndGateAlreadyTrue",
.root = new TreeNode(3, new TreeNode(1), new TreeNode(1)),
.result = true,
.expected = 0},
MinimumFlipsCase{.test_name = "AndGateFlipChild",
.root = new TreeNode(3, new TreeNode(1), new TreeNode(0)),
.result = true,
.expected = 1},
MinimumFlipsCase{.test_name = "XorGateAlreadyTrue",
.root = new TreeNode(4, new TreeNode(1), new TreeNode(0)),
.result = true,
.expected = 0},
MinimumFlipsCase{.test_name = "XorGateAlreadyFalse",
.root = new TreeNode(4, new TreeNode(1), new TreeNode(1)),
.result = false,
.expected = 0},
MinimumFlipsCase{.test_name = "NotGateAlreadyTrue",
.root = new TreeNode(5, new TreeNode(0), nullptr),
.result = true,
.expected = 0},
MinimumFlipsCase{.test_name = "NotGateAlreadyFalse",
.root = new TreeNode(5, new TreeNode(1), nullptr),
.result = false,
.expected = 0},
MinimumFlipsCase{.test_name = "ComplexTreeOrAlreadyTrue",
.root = new TreeNode(2, new TreeNode(3, new TreeNode(1), new TreeNode(0)),
new TreeNode(5, new TreeNode(0), nullptr)),
.result = true,
.expected = 0},
MinimumFlipsCase{.test_name = "ComplexTreeAndAlreadyFalse",
.root = new TreeNode(3, new TreeNode(2, new TreeNode(0), new TreeNode(0)),
new TreeNode(5, new TreeNode(1), nullptr)),
.result = false,
.expected = 0},
MinimumFlipsCase{.test_name = "ComplexTreeXorNeedFlip",
.root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(0)),
new TreeNode(3, new TreeNode(1), new TreeNode(1))),
.result = true,
.expected = 1},
MinimumFlipsCase{.test_name = "ComplexTreeXorToFalse",
.root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(1)),
new TreeNode(3, new TreeNode(1), new TreeNode(0))),
.result = false,
.expected = 1},
MinimumFlipsCase{
.test_name = "Leaf0ToFalse", .root = new TreeNode(0), .result = false, .expected = 0},
MinimumFlipsCase{
.test_name = "Leaf1ToFalse", .root = new TreeNode(1), .result = false, .expected = 1},
MinimumFlipsCase{.test_name = "AndGateToFalse",
.root = new TreeNode(3, new TreeNode(0), new TreeNode(1)),
.result = false,
.expected = 0},
MinimumFlipsCase{.test_name = "NotGateFlipToTrue",
.root = new TreeNode(5, new TreeNode(1), nullptr),
.result = true,
.expected = 1},
MinimumFlipsCase{.test_name = "NotGateFlipToFalse",
.root = new TreeNode(5, new TreeNode(0), nullptr),
.result = false,
.expected = 1}),
[](const ::testing::TestParamInfo<MinimumFlipsCase> &info) { return info.param.test_name; });
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""Test cases for the minimumFlips function."""

import sys
from pathlib import Path

import pytest

# Add the root directory to Python path
root_dir = Path(__file__).parent.parent.parent
sys.path.insert(0, str(root_dir))

from common import TreeNode # noqa: E402
from minimum_flips_in_binary_tree_to_get_result import minimumFlips # noqa: E402


@pytest.mark.parametrize(
"root, result, expected",
[
(TreeNode(0), True, 1), # Flip leaf node from 0 to 1
(TreeNode(1), True, 0), # Leaf node already 1
(TreeNode(2, TreeNode(0), TreeNode(0)), True, 1), # OR gate, flip one child
(
TreeNode(2, TreeNode(1), TreeNode(1)),
False,
2,
), # OR gate, flip both children
(TreeNode(3, TreeNode(1), TreeNode(1)), True, 0), # AND gate, already true
(TreeNode(3, TreeNode(1), TreeNode(0)), True, 1), # AND gate, flip one child
(TreeNode(4, TreeNode(1), TreeNode(0)), True, 0), # XOR gate, already true
(TreeNode(4, TreeNode(1), TreeNode(1)), False, 0), # XOR gate, already false
(TreeNode(5, TreeNode(0)), True, 0), # NOT gate, already true
(TreeNode(5, TreeNode(1)), False, 0), # NOT gate, already false
(
TreeNode(
2, TreeNode(3, TreeNode(1), TreeNode(0)), TreeNode(5, TreeNode(0))
),
True,
0,
), # Complex tree: OR(AND(1,0), NOT(0)) = 1, already true
(
TreeNode(
3, TreeNode(2, TreeNode(0), TreeNode(0)), TreeNode(5, TreeNode(1))
),
False,
0,
), # Complex tree: AND(OR(0,0), NOT(1)) = 0, already false
(
TreeNode(
4,
TreeNode(2, TreeNode(1), TreeNode(0)),
TreeNode(3, TreeNode(1), TreeNode(1)),
),
True,
1,
), # Complex tree: XOR(OR(1,0), AND(1,1)) = 1, need 1 flip to make false
(
TreeNode(
4,
TreeNode(2, TreeNode(1), TreeNode(1)),
TreeNode(3, TreeNode(1), TreeNode(0)),
),
False,
1,
), # Complex tree: XOR(OR(1,1), AND(1,0)) = 1, flip one child to make it 0
],
ids=[
"leaf_0_to_true",
"leaf_1_already_true",
"or_gate_flip_child",
"or_gate_to_false",
"and_gate_already_true",
"and_gate_flip_child",
"xor_gate_already_true",
"xor_gate_already_false",
"not_gate_already_true",
"not_gate_already_false",
"complex_tree_or_already_true",
"complex_tree_and_already_false",
"complex_tree_xor_need_flip",
"complex_tree_xor_to_false",
],
)
def test_minimum_flips(root, result, expected):
assert minimumFlips(root, result) == expected