Functional Programming Binary Search Tree Homework - functional-programming

So I am having to write an insert function for a binary tree to make it a binary search tree, but I'm having some trouble. Everything is functions, so I understand that there is no concept of a state. Therefore, I need to recursively create the tree over and over when inserting. I'm having trouble wrapping my head around this idea.
treenode -> procedure(val, left, right) procedure(some) if some then -(some, 1) then right else left else val
This allows me to create nodes and access the value, the left sub-tree and right sub-tree like so (0 stands for an empty tree):
.treenode(4, 0, 0)
to create a more complex tree, I can do:
.treenode(4, .treenode(3, 0, 0), .treenode(6, .treenode(2, 0, 0), 0))
I have gotten as far as inserting into an empty tree:
insert -> procedure(root, value) if empty(root) then .treenode(value, 0, 0) else insert_recursive(root, .treenode(value, 0, 0)
This is where I cannot figure out how to insert into a tree like this. There is not concept of a state in this language so I need to somehow recursively append the new node with the value onto the current tree. If someone could give me a hint,, I would really appreciate it. The way I'm supposed to call this is:
tree = empty
tree = insert(tree, 4)
tree = insert(tree, 6)
....
and so on

I've never seen this grammar before, so bear with me if I got some of the syntax wrong. Hopefully the code demonstrates what needs to be done -
if the tree is empty, there is no value to compare against, create a singleton node with the value. This is the part you already finished.
otherwise, the tree is not empty, therefore we have a value to compare against. If the value to insert is less than the root's value, create a new node consisting of the root's value, insert the value into the root's left branch, and leave the root's right branch untouched
if the value is greater than the root's value, do the same as above, but insert the new value in the root's right branch, instead of the left
the value is neither less-than nor greater-than the root's value, therefore it is equal to the root's value and there is nothing left to insert. In this case, return the unmodified root
The bullet points correspond to the numbered comments below -
insert -> procedure(root, value)
if empty(root) then #1
.treenode(value, 0, 0)
else if value < root.value #2
.treenode (root.value, insert(root.left, value), root.right)
else if value > root.value #3
.treenode (root.value, root.left, insert(root.right, value))
else #4
root
StackOverflow allows us to run JavaScript snippets directly in answer posts. Here's a functioning snippet that allows you to see this concept in action -
const empty =
{}
const treenode = (value, left = empty, right = empty) =>
({ value, left, right })
const insert = (t, value) =>
t === empty
? treenode (value, empty, empty)
: value < t.value
? treenode (t.value, insert (t.left, value), t.right)
: value > t.value
? treenode (t.value, t.left, insert (t.right, value))
: t
const print = (t, pre = '', child = '') =>
t === empty
? pre + '∅\n'
: print
( t.right
, child + '┌── '
, child + '. '
)
+ pre + String (t.value) + '\n'
+ print
( t.left
, child + '└── '
, child + '. '
)
let tree = empty
tree = insert (tree, 4)
tree = insert (tree, 6)
tree = insert (tree, 9)
tree = insert (tree, 3)
tree = insert (tree, 5)
tree = insert (tree, 1)
console.log (print (tree))
The program prints the constructed tree. The ∅ symbol represents empty -
. . . ┌── ∅
. . ┌── 11
. . . └── ∅
. ┌── 9
. . └── ∅
┌── 6
. . ┌── ∅
. └── 5
. . └── ∅
4
. ┌── ∅
└── 3
. . ┌── ∅
. └── 1
. . └── ∅

Related

How do I initialize a type alias like this in Elm?

type alias Bag a = List (a, Int)
I am trying to create functions that work with this type alias but I can't figure out how to return a Bag from a function.. Like in the following function I am hoping to insert a new item into an existing bag (could be empty, which is the only case I am trying to get working at the moment).
insert : a -> Bag a -> Bag a
insert b c = case c of
[] -> Bag a : [(b, 1)]
--this case below is what I'm hoping could work out..cant really check without getting the first case to work---
x::xs -> if Tuple.first x == b
then Bag a : [(b, (Tuple.second x) + 1)] ++ xs
else [x] ++ insert b xs
How can I initialize a new Bag without using this multi-line method below?
testBag : Bag Int
testBag = [(5,1),(2,1)]
PS very new to Elm and struggling to find resources that can help me shift from an imperative mindset..
You don't need to write Bag a: in front of bags. The compiler can deduce what type you need.
You do need to match up the indentation for the different branches of your case:
insert : a -> Bag a -> Bag a
insert b c = case c of
[] -> [(b, 1)]
x::xs -> if Tuple.first x == b
then [(b, (Tuple.second x) + 1)] ++ xs
else [x] ++ insert b xs
This works, but I wouldn't have written it that way. I'd have used pattern matching to split b into the item and count rather than Tuple.first and Tuple.second, and I'd use more descriptive names for the variables, giving:
add : a -> Bag a -> Bag a
add item bag =
case bag of
[] -> [ ( item, 1 ) ]
( firstItem, count ) :: otherItems ->
if item == firstItem then
( firstItem, count + 1 ) :: otherItems
else
( firstItem, count ) :: add item otherItems
I'd also define
emptyBag : Bag a
emptyBag = []
so that you can make
exampleBag : Bag Int
exampleBag = emptyBag |> add 5 |> add 3 |> add 5 |> add 3 |> add 3
By the way, most IDEs can run elm-format automatically for you when you save your work. That would lay out your code to be better maintainable and more easily readable. Other elm programmers would find it easier to read your code because it would be in the frequently-used elm layout.

How to “tripartite” a tree using 0, 1 and 2 and maximize the numbers used?

An interesting problem is to assign labels (0, 1 or 2) for every node in a tree (not necessarily binary), where no parent-child pair can have the same label. In addition, we need to maximize the sum of all labels, i.e., use as little 0 as possible. Return the minimum number of 0 labels used. Some leaf nodes have been pre-labeled. We cannot change those labels. If there are no way to assign labels, for example, one parent node has children pre-labeled with all three labels, return negative infinity.
I am trying dynamic programming on this. One possible recurrence is that OPT(v) returns the number of 0 used to label v’s subtree, and we start at the overall root. When recurse down the tree, try to label each v’s children 0, 1 and 2 (by manipulating v.label field in each node) and see which option returns the minimum number of 0. If we reach bottom and a leaf node has been pre-labeled, we can’t explore all labels but to use the given label. If the leaf node is not pre-labeled, try each label, same as above. The tree itself can be used as my memorization structure where the label is stored in each node’s .label field. But I am not sure how to write the recurrence explicitly, especially for the recursive case when I explore all possible labels for every child of current node. I have no idea to express this combination and get the minimum of it. The base case is fairly simple, perhaps return 1 if the leaf is labeled 0 and return 0 otherwise.
Your idea looks fine. Just one thing to improve: the memoization should concern not just one label value, but possibly all 3 label values (0, 1 and 2). For each label you would (per node) memoize what the minimum number of zeroes is in that node's tree (where it is the root) when that label is assigned to it.
Then, depending on which choice you made for the parent node, you would look at the two possible labels that remain and choose the label which has the least number of zeroes linked to it.
For the below implementation I have used this tree as example:
*
/ \
* * ___
/|\ / \ \
1 * * 2 * *
/ \ \
* 2 2
/|\
2 * 0
The asterisks are nodes that have no label.
So the algorithm would start at the root and temporarily assign it a 0, then see what effect and possibilities that leaves for the children. Then for each child go through the possible values it can have (not zero), ...and recurse deeper into the tree, each time backtracking -- registering the count of 0 labels -- and continuing with the next possible label for the node (and going down the tree again unless memoization is available).
For the above example we can see that an optimal labeling would be:
0
/ \
2 1 ___
/|\ / \ \
1 1 1 2 0 0
/ \ \
1 2 2
/|\
2 2 0
The root and its left child could swap values -- it doesn't matter. The result is 4 zeroes.
Here is the implementation:
// Main algorithm:
function triple(node, parentLabel=-1) {
let choices = node.label !== undefined ? [node.label] : [0,1,2];
let minCount = Infinity;
for (let label of choices) {
if (label === parentLabel) continue; // We cannot use same label as parent has
let count = node.memo[label]; // Already memoized?
if (count === undefined) { // No...
count = 0;
for (let child of node.children) {
count += triple(child, label); // recur
if (count >= minCount) break; // not better...
}
node.memo[label] = count;
}
if (label === 0) count++; // Count the zero
if (count < minCount) minCount = count; // better!
}
// Minimum number of 0-labels if parent has the given parentLabel
return minCount;
}
class Node {
constructor(label, ...children) {
this.label = label;
this.children = children;
this.memo = [undefined, undefined, undefined];
}
}
// Short-cut function for creating a Node instance with or without label
function N(...children) {
let label = undefined;
if (typeof children[0] === "number") { // first argument is a label
label = children.shift(); // extract first argument
}
return new Node(label, ...children);
}
// Demo
let tree = N(
N(
N(1), N(), N()
),
N(
N(2),
N(
N(
N(2), N(), N(0)
),
N(2)
),
N(
N(2)
)
)
)
console.log("input tree:");
console.log(tree);
let count = triple(tree);
console.log("Number of zeroes:", count);
This implementation would return Infinity when there is no valid labelling possible.

How to find the node with the smallest value in a given level?

So I want to write a recursive function that returns the smallest value at a given level in a binary search tree.
I've written this code but it only works if the bst is a full binary search tree. (every node has 2 children).
Example :
if my tree was :
root is equal to 3
right child is equal to 4
left child is equal to 1
And the call was the root with level 1, it returns 1. which is exactly the value I want.
but with this tree: (Same call)
root is equal 2
left child is equal to null
right child is equal to 3
it returns 0 (NULL). because 0 < 3. when I want it to actually return 3.
The code I used:
int minAtLevel(AVLNodePtr root, int level)
{
// If the tree is empty
if (root == NULL)
return 0;
// if level becomes 0, it means we are on
// any node at the given level
if (level == 0)
return root->key;
int x = minAtLevel(root->left, level - 1);
int y = minAtLevel(root->right, level - 1);
// return maximum of two
return min(x, y);
}
Thanks.

Standard ML: Size of binary tree computed incorrectly?

My book has the following function which calculates the number of non-leaf nodes in a binary tree:
fun size Empty = 0
| size(Node(t_1, _, t_2)) = size t_1 + size t_2 + 1;
Suppose I want to calculate all nodes in a binary tree. How would I modify this function to do so?
Here's what I was thinking:
fun size Empty = 0
| size(Node(Empty, _, Empty)) = 1
| size(Node(t_1, _, t_2)) = size t_1 + size t_2 + 1;
Does this look right?
Thanks,
bclayman
Both of the implementations that you provided are actually the same. The second case of your second implementation is a special case of you your third pattern. For your first implementation, size(Node(Empty,1,Empty)) will recurse one the left subtree, returning 0, recurse on the right subtree, which returns 0, and then adds 1, yielding the result 1. In fact, if you switch the order of the second and third case, the compiler will tell you that it is redundant:
test.sml:3.5-5.38 Error: match redundant
Empty => ...
Node (t_1,_,t_2) => ...
--> Node (Empty,_,Empty) => ...
Matt is correct that your two functions are functionally the same -- both of which return a count of all nodes in the tree. I didn't notice this at first since I took it at face value that your first function counted nonleaf nodes and then noticed that your Node(Empty,_,Empty) pattern is the correct pattern of a leaf (if a leaf is defined as a node with no non-empty children). But -- this means that the function in the book doesn't just count nonleaf (parents) nodes. If you do want a function which just counts parent nodes, there is a use for your pattern after all:
fun parents Empty = 0
| parents(Node(Empty, _, Empty)) = 0
| parents(Node(t_1, _, t_2)) = parents t_1 + parents t_2 + 1;
If your application of trees is one in which heavy use is made of the parent node vs. leaf node distinction, you could (at the cost of making some of your function definitions more involved) ditch the Node constructor in favor of separate Parent and Leaf constructors. Something like:
datatype 'a tree = Empty | Leaf of 'a | Parent of 'a tree * 'a * 'a tree;
Then you can write functions like
fun countLeaves Empty = 0
| countLeaves (Leaf _) = 1
| countLeaves (Parent(t1,_,t2)) = countLeaves t1 + countLeaves t2;
So e.g.
- val t = Parent(Parent(Leaf "2", "*", Leaf "3"), "+", Leaf "4");
- countLeaves t;
val it = 3 : int

Tail recursion in SML does not present any output

Following my previous post here , I tried to do what was suggested and convert the code
into a Tail-recursion method with let .
The original code - which does not work (due to using val inside if condition) :
fun func() =
val decimal = 0 (* the final result *)
val multiple = 0 (* keeps track of multiples, eg. In XXV, X would be a multiple *)
val current = 0 (* the digit currently being processed *)
val top = 0 (* value of the last element in the list *)
val last_add = 0 (* the last digit that wasn't a multiple, or subtraction operation *)
val last_sub = 0
val problem = 0 (* if value is 1 then there is a problem with the input *)
val myList = [1,2,3,4,5] (* the list has more values *)
while (myList <> []) (* run while the list is not empty *)
val current = tl(myList) (* grab the last element from the list *)
val myList = tl(myList) (* remove the last element from the list *)
val top = tl(myList) (* grab the value at the end of the list *)
if ( myList <> []) andalso (current > top))
then
val decimal = decimal + current - top
val last_sub = top;
val myList = tl(myList)
else
if ( (myList = []) andalso (current = top))
then val decimal = decimal + current
val multiple = multiple + 1
else
if (last_sub = current)
then val problem = 1
else
val decimal = decimal + current
val multiple = 0
val last_add = current
And the code as a tail-recursion method :
fun calc [] = 0
|calc [x] = x
|calc (head::tail) =
let
val decimal = 0
val multiple = 0
val current = 0
val top = 0
val last_add = 0
val last_sub = 0
val problem = 0
val doNothing = 0
in
let
val current = hd(rev(head::tail)) (* grab the last element *)
val head::tail = rev(tl(rev(head::tail))) (* POP action - remove the last element from the list *)
val top = hd(rev(head::tail)) (* grab the new last element after removing *)
in
if (current > top) then
let
val decimal = decimal + current - top
val last_sub = top
val head::tail = rev(tl(rev(head::tail))) (* POP action - remove the last element from the list *)
in
calc(head::tail)
end
else
if ( (head::tail = []) andalso (current = top))
then let
val decimal = decimal + current
val multiple = multiple + 1
in
calc(head::tail)
end
else
if (last_sub <> current)
then let
val decimal = decimal + current
val multiple = 0
val last_add = current
in
calc(head::tail)
end
else
(* do nothing *)
val doNothing = 0
end
end;
However , when I try to enter :
calc([0,100,20,30,4,50]);
I get :
uncaught exception Bind [nonexhaustive binding failure]
raised at: stdIn:216.13-216.50
I know the code is very hard to read and pretty long , but it would be greatly appreciated
if someone could explain to me how to fix it , or help me find the reason for this output .
Thanks
You have a few issues with your code.
First of all, you can use last to grab the last element of a list. See the List documentation for more info. But unless you have a really good reason to do so, it's easier and much more efficient to simply start from the beginning of the list and pop elements off the beginning as you recurse. You already have the first element bound to head in your code using pattern matching.
Secondly, unless you use refs (which you probably don't want to do) there are no variables in Standard ML, only values. What this means is that if you want to carry state between invocations, any accumulators need to be parameters of your function. Using a helper function to initialize accumulators is a common pattern.
Third, instead of comparing a list to [] to test if it's empty, use the null function. Trust me on this. You'll get warnings using = because of subtle type inference issues. Better yet, use a pattern match on your function's parameters or use a case statement. Pattern matching allows the compiler to tell you whether you've handled all possible cases.
Fourth, SML typically uses camelCase, not snake_case, for variable names. This is more stylistic, but as you write more code and collaborate, you're going to want to fit with the conventions.
Fifth, when you do recursion on a list, don't try to look at multiple values in the list. This complicates things. Treat it as a head element and tail list, and everything will become much simpler. In my code, instead of keeping current in the list, I did this by splitting it out into a separate parameter. Have a base case where you simply return the answer from one of your accumulators, and a recursive case where you recurse with updated accumulator values and a single value popped from your list. This eliminates the problem scenario.
I'm not sure if this logic is correct since I don't know what you're trying to calculate, but check out this code which illustrates some of the things I talked about.
(* This is the helper function which takes accumulators as
parameters. You shouldn't call this directly. *)
fun calc' decimal _ _ _ _ [] =
(* We processed everything in the list. Just return the accumulator. *)
decimal
| calc' decimal multiple lastAdd lastSub current (top::tail) =
(* This case is for when there are 1 or more elements in the list. *)
if current > top then
calc' (decimal + current - top) multiple lastAdd top top tail
else if current = top then
calc' (decimal + current) (multiple + 1) lastAdd lastSub top tail
else
calc' (decimal + current) 0 current lastSub top tail
(* This is the function you should call. *)
fun calc [] = 0
| calc [_] = 0 (* Given a single-element list. *)
| calc (x::xs) =
(* Apply the helper with correct initial values. *)
calc' 0 0 0 0 x xs
In a functional language, instead of assigning to a variable when you want to change it, simply recurse and specify the new value for the correct parameter. This is how you write a "loop" in a functional language using recursion. As long as you only use tail-recursion, it will be just as efficient as a while loop in your favorite imperative language.

Resources