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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,12 @@ set(APP_SOURCES
"src/core/datatypes/container/unordered/UnorderedMap.cpp"
"src/core/expression/Lambda.cpp"
"src/core/expression/FunctionPointer.cpp"
## LC
"src/leetcode/arrays/two_sum/TwoSum.cpp"
"src/leetcode/arrays/median_two_arrays/MedianTwoSortedArrays.cpp"
"src/leetcode/arrays/container_with_most_water/ContainerWithMostWater.cpp"
"src/leetcode/arrays/longest_common_prefix/Solution.cpp"
"src/leetcode/arrays/3sum/Solution.cpp"
)

# Test files
Expand All @@ -144,6 +146,7 @@ set(APP_TESTS
"tests/median_two_sorted_arrays_ut.cpp"
"tests/container_with_most_water_ut.cpp"
"tests/longest_common_prefix_ut.cpp"
"tests/three_sum_ut.cpp"
)

# ----------------------------------------------------------------------------------------
Expand Down
65 changes: 65 additions & 0 deletions src/leetcode/arrays/3sum/Solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "Solution.h"
#include <algorithm>
/**
* @brief Complexity
* - Time: O(nlog(n) + n(n-1)) ~ O(n2)
* - Space: O(1)
*/
std::vector<std::vector<int>> Solution::threeSum(std::vector<int>& nums) {
std::vector<std::vector<int>> trips{};
const size_t size = nums.size();
if (size < 3) {
return trips;
}

std::sort(nums.begin(), nums.end());

// after sorted, if the smallest number is greater than 0,
// no triplet can sum to zero
if (nums[0] > 0) {
return {};
}

for (size_t i = 0; i < size - 2; ++i) {
if (i > 0 && nums[i] == nums[i - 1]) {
// we should not use num[i] == num[i+1] expression because we
// may fall thorough out
continue; // skip duplicates for the 1st
}

int first = nums[i];

// use two pointers to find the 2 remain numbers
size_t j = i + 1;
size_t k = size - 1;
while (j < k) {
int second = nums[j];
int third = nums[k];
int sum = first + second + third;

if (sum == 0) {
trips.push_back({first, second, third});

++j;
--k;

// move next
while (j < k && nums[j] == nums[j - 1]) {
++j; // skip duplicates for the 2nd
}

while (j < k && nums[k] == nums[k + 1]) {
--k; // skip duplicates for the 3rd
}
} else if (sum < 0) {
// require a large sum by increasing the 2nd
++j;
} else {
// require a smaller sum by decreasing the 3rd
--k;
}
}
}

return trips;
}
41 changes: 41 additions & 0 deletions src/leetcode/arrays/3sum/Solution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//cppcheck-suppress-file [functionStatic,ctuOneDefinitionRuleViolation]

// 15 - M
// Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]]
// such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

// Notice that the solution set must not contain duplicate triplets.

// Example 1:
// Input: nums = [-1,0,1,2,-1,-4]
// Output: [[-1,-1,2],[-1,0,1]]
// Explanation:
// nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
// nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
// nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
// The distinct triplets are [-1,0,1] and [-1,-1,2].
// Notice that the order of the output and the order of the triplets does not matter.

// Example 2:
// Input: nums = [0,1,1]
// Output: []
// Explanation: The only possible triplet does not sum up to 0.

// Example 3:
// Input: nums = [0,0,0]
// Output: [[0,0,0]]
// Explanation: The only possible triplet sums up to 0.

// Constraints:
// 3 <= nums.length <= 3000
// -105 <= nums[i] <= 105

#pragma once

#include <string>
#include <vector>

class Solution {
public:
std::vector<std::vector<int>> threeSum(std::vector<int>& nums);
};
78 changes: 78 additions & 0 deletions tests/three_sum_ut.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "../src/leetcode/arrays/3sum/Solution.h"

using ::testing::ElementsAre;
using ::testing::UnorderedElementsAre;

/**
* Test case definition
*/
struct ThreeSumCase {
std::vector<int> input;
std::vector<std::vector<int>> expected;
};

/**
* Parameterized test fixture
*/
class ThreeSumTest : public ::testing::TestWithParam<ThreeSumCase> {};

/**
* Main test
*
* Note:
* - Order of triplets does NOT matter
* - Order inside each triplet DOES matter (sorted by implementation)
*/
TEST_P(ThreeSumTest, ReturnsCorrectTriplets) {
const auto& tc = GetParam();
Solution s{};

auto nums = tc.input;
auto result = s.threeSum(nums);

std::vector<::testing::Matcher<std::vector<int>>> matchers;
for (const auto& triplet : tc.expected) {
matchers.push_back(ElementsAre(triplet[0], triplet[1], triplet[2]));
}

EXPECT_THAT(result, UnorderedElementsAreArray(matchers));
}

/**
* Test data
*/
INSTANTIATE_TEST_SUITE_P(
BasicAndEdgeCases, ThreeSumTest,
::testing::Values(ThreeSumCase{{}, {}}, ThreeSumCase{{1}, {}},
ThreeSumCase{{1, 2}, {}}, ThreeSumCase{{1, 2, 3}, {}},
ThreeSumCase{{2, 1, 3}, {}}, ThreeSumCase{{0, 1, 1}, {}},
ThreeSumCase{{-5, -4, -3, -2}, {}},
ThreeSumCase{{1, 2, 3, 4}, {}},

ThreeSumCase{{-1, 0, 1, 2, -1, -4},
{{-1, -1, 2}, {-1, 0, 1}}},

ThreeSumCase{{0, 0, 0}, {{0, 0, 0}}},

ThreeSumCase{{0, 0, 0, 0}, {{0, 0, 0}}},

ThreeSumCase{{1, 2, 0, 1, 0, 0, 0, 0}, {{0, 0, 0}}},

ThreeSumCase{{-2, 0, 0, 2, 2}, {{-2, 0, 2}}}));

/**
* Extra safety test:
* ensure no duplicate triplets are returned
*/
TEST(ThreeSumExtra, NoDuplicateTriplets) {
std::vector<int> nums{-2, 0, 0, 2, 2};
Solution s{};

auto result = s.threeSum(nums);

ASSERT_EQ(result.size(), 1);
EXPECT_EQ(result[0], std::vector<int>({-2, 0, 2}));
}