stevensonmt

stevensonmt

LeetCode Convert BST to Greater Tree: how to do without GenServer?

I simply could not figure out a way to solve this problem without using a GenServer to keep the state of the running sum. My solution with GenServer is below, but I’m wondering if anyone could suggest a way to do it without a GenServer, which felt like overkill.

  use GenServer 
  
  def init(tally), do: {:ok, tally}

  def handle_cast({:add, n}, tally), do: {:noreply, n + tally}

  def handle_call(:tally, _from, tally), do: {:reply, tally, tally}


  @spec convert_bst(root :: TreeNode.t | nil) :: TreeNode.t | nil
  def convert_bst(root) do
    {:ok, pid} = GenServer.start(__MODULE__, 0)
    do_convert(root, pid)
  end
 
  def do_convert(nil, tally), do: nil
  def do_convert(node, tally) do
    right = do_convert(node.right, tally)
    GenServer.cast(tally, {:add, node.val})
    new_val = GenServer.call(tally, :tally)
    left = do_convert(node.left, tally)
    %TreeNode{ val: new_val, left: left, right: right}
  end

Marked As Solved

al2o3cr

al2o3cr

The GenServer lets do_convert do two things:

  • return a new tree node
  • as a side-effect, update the running total

To remove it, you’ll need to transform that side-effect into a part of the return value.

Consider Map.pop for inspiration; it returns a {popped_value, updated_map} tuple. You can combine it with rebinding to get code that looks almost like mutability:

things = %{a: 1, b: 2, c: 3}

{c_value, things} = Map.pop(things, :c)

A similar transformation on do_convert would make it return a tuple of {tree_node, updated_tally}.

Also Liked

stevensonmt

stevensonmt

Many thanks. I knew there must be some way to keep that “state” without using a GenServer. My first issue had actually been figuring out how to use the left node value as the tally for the grandparent node calculation. I only figured that out after changing to GenServer which made the steps in the process clearer to me. Changing to returning tuples was trivial. Thanks again.

  def convert_bst(root) do
    do_convert(root, 0) |> elem(0)
  end
 
  def do_convert(nil, tally), do: {nil, tally}
  def do_convert(node, tally) do
    {right, interim_tally} = do_convert(node.right, tally)
    new_val = interim_tally + node.val
    {left, final_tally} = do_convert(node.left, new_val)
    {%TreeNode{ val: new_val, left: left, right: right}, final_tally}
  end
Aetherus

Aetherus

I came up with a similar solution, and it passed the tests:

defmodule Solution do
  @spec convert_bst(root :: TreeNode.t | nil) :: TreeNode.t | nil  
  def convert_bst(root) do
    {converted, _} = do_convert_bst(root, 0)
    converted
  end
    
  defp do_convert_bst(nil, _carry), do: {nil, 0}
  defp do_convert_bst(node, carry) do
    {right, sum_r} = do_convert_bst(node.right, carry)
    carry = carry + node.val + sum_r
    {left, sum_l} = do_convert_bst(node.left, carry)
    {%TreeNode{left: left, right: right, val: carry}, node.val + sum_l + sum_r}
  end
end

carry is the sum of the values of all the nodes that have already been visited that have values greater than the next node to visit.

joaoevangelista

joaoevangelista

I have no ideia how to implement such algorithm, but!
Genservers are an abstraction over a looping receiver, so you can try to implement it with recursion, keeping the state as a parameter and keep passing it. Start with the base cases that returns a value instead of recusing. I bet it will look closely to your current implementation,but without the genserver specifics

Where Next?

Popular in Questions Top

tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New

We're in Beta

About us Mission Statement