LeetCode 1 Two Sum Elixir solution
LeetCode problem link: 1. Two Sum
You can find the problem description and solutions in all programming languages supported by LeetCode here
The general approach for different programming languages is:
1. For the current nums[i], calculate target - nums[i]
2. Check whether we have seen this number before.
3. If yes, return the two indices.
4. If not, store the current number and its index.
What is more interesting is the difference in implementation in C++ or Java and Elixir.
The biggest conceptual difference here is between the imperative style of C++/Java and the functional style of Elixir with recursion and immutable data.
1. C++ and Java use a loop for. Elixir, on the other hand, uses recursion:
find(rest, target, ..., i + 1)Each recursive call processes one element.
[num | rest]This means: num is the first element of the list, rest is all remaining elements.
2. Another important difference is immutability.
In C++
map[nums[i]] = i;we change the existing unordered_map.
In Java
map.put(nums[i], i);we also change existing Map.
In Elixir map doesn't change and Map.putreturns new map, we pass it to the next recursive call:
Map.put(map, num, i)
find(rest, target, Map.put(map, num, i), i + 1)3. The difference in search.
C++
auto it = map.find(val);
if (it != map.end())Java
Integer index = map.get(val);
if (index != null)In Elixir:
case Map.fetch(map, val) do
{:ok, index} ->
:error ->
endMap.fetch/2 explicitly returns one of two possible results:
{:ok, index} - found
:error - not foundThat is why case is very useful in this situation.
Time complexity: O(n)
Space complexity: O(n)
LeetCode 1 Two Sum problem: Elixir solution
defmodule Solution do
@spec two_sum(nums :: [integer], target :: integer) :: [integer]
def two_sum(nums, target) do
find(nums, target, %{}, 0)
end
defp find([num | rest], target, map, i) do
val = target - num
case Map.fetch(map, val) do
{:ok, index} ->
[index, i]
:error ->
find(rest, target, Map.put(map, num, i), i + 1)
end
end
defp find([], _target, _map, _i), do: []
endBut we also have a solution without recursion using built-in Enum functions:
defmodule Solution do
@spec two_sum(nums :: [integer], target :: integer) :: [integer]
def two_sum(nums, target) do
nums
|> Enum.with_index()
|> Enum.reduce_while(%{}, fn {num, i}, map ->
val = target - num
case Map.fetch(map, val) do
{:ok, index} ->
{:halt, [index, i]}
:error ->
{:cont, Map.put(map, num, i)}
end
end)
end
endThe difference is in the way we iterate:
Manual tail recursion - we control the transition to the next element yourself through a recursive call.
Enum abstraction - iteration is handled by built-in Enum functions such as Enum.with_index() and Enum.reduce_while().
