How can I create a ternary tree in SMLNJ? - sml

Right now I have a binary tree modeled in SMLNJ however I want to change this model to where there is a tree inside of tree inside of tree. (Ternary Tree)
If you begin with the root of a binary tree.
Binode with an (id1*(binode)) ß- a binary tree
Then if that “binode” is located as part of a tuple: 2Dbinode
That tuple has an identifier and that binode with is the root of the binode tree.
2Dbinode (id2* (binode) *2Dbinode)
Each of those in turn are part of a 3Dbinode which consists of:
3Dbinode(id3 * (2Dbinode) * 3Dbinode)
For ex. 3Dbinode(id3 * (2Dbinode) * 3Dbinode) the root 3Dbinode might contain the following data:
(25, (7, (11)))
And by adding the nodes (25, (7, (22)))
(25, (10, (4))), (30, (7, (22)))
3DBinary Tree Model
Here is SMLNJ code for the 2D binary tree I am modifying.
datatype btree = Empty | Node of int * btree * btree;
fun AddNode (i:int, Empty) = Node(i, Empty, Empty) |
AddNode(i:int, Node(j, left, right)) =
if i = j then Node(i, left, right)
else if i < j then Node(j, AddNode(i, left), right)
else Node(j, left, AddNode(i, right));
fun printInorder Empty = () |
printInorder (Node(i,left,right)) =
(printInorder left; print(Int.toString i ^ " "); printInorder right);
val x : btree = AddNode(50, Empty);
val x : btree = AddNode(75, x);
val x : btree = AddNode(25, x);
val x : btree = AddNode(72, x);
val x : btree = AddNode(20, x);
val x : btree = AddNode(100, x);
val x : btree = AddNode(3, x);
val x : btree = AddNode(36, x);
val x : btree = AddNode(17, x);
val x : btree = AddNode(87, x);
printInorder(x);
I need to populate the data structure with N randomized (3Dnodes):
How can I implement these functions?
Search for a specific node displaying the path to the node ex: (25, 10, 4) the display would be
(25)
(25, 7)
(25, 10)
(25, 10,4)
If the user searches for a node that does not exist:
(30, (7, (30))) then the path displayed would be
(25)
(30)
(30,7)
(30, 7, 30) NOT FOUND
If the user wishes to ADD a node at any level they should be prompted to enter the 3 digit code for that node; Again the path should be displayed.
EX: ADD (30, 11, 5) then the display would be
(25)
(30)
(30, 7)
(30, 11) created
(30, 11, 5) created
Print out the contents of the 3dbinode tree as a sequence of (A,B,C)
DELETE a node
Delete a node: EX: DEL (30, 7, _)
Then the result would be
- (30, 7, 22) deleted
- (30, 7, 0) created

Ternary tree?
Edit 2: After you reviewed your question, it still seems unclear how you wish to navigate this ternary tree when searching it. A binary search tree divides its elements left and right depending on which one is greater. You have described no similar criterion: When should your functions use the first, second and third branch?
Edit 3: I've provided the function pathExists that works on ternary trees, but AddNode is still missing as you have not provided any insight into the questions that I've highlighted with bold. If your tree really does serve the purpose of containing points in a 3-dimensional space, it does sound like you want a k-d tree, as I suggested once. I've also partially provided the function make3DTree under the assumption that a k-d tree is what you're looking for.
Until I saw the drawing you made of a ternary tree (or generally an n-ary tree), I could not understand your question. A ternary tree simply has (up to) three child nodes at each level instead of the binary tree's two.
The wording "tree inside of a tree inside of a tree" means something different than the branching factor (2, 3, n). Exactly how you turn the triples (25, 7, 11), (25, 7, 22), (25, 10, 4), and (30, 7, 22) into your ternary tree is still a little puzzling to me. It seems that the bottom nodes only have two empty leaves. I will interpret this as if there were a third empty arrow in the middle.
Your AddNode function constructs binary search trees where the smaller elements go to the left, and the larger elements to the right. But how, then, do you wish to use a third branch?
Compare the following generic / integer-specific binary tree datatype definitions,
datatype 'a btree = BTreeEmpty | BTreeNode of 'a * 'a btree * 'a btree
datatype int_btree = BTreeEmpty | BtreeNode of 'a * int_btree * int_btree
with the ones for ternary trees,
datatype 'a ttree = TTreeEmpty | TTreeNode of 'a * 'a ttree * 'a ttree * 'a ttree
datatype int_ttree = TTreeEmpty | TtreeNode of 'a * int_ttree * int_ttree * int_ttree
or even ones that support variable branching in each node,
datatype 'a tree = TreeEmpty | TreeNode of 'a * 'a tree list
datatype int_tree = TreeEmpty | TreeNode of int * int_tree list
Creating the ternary tree you depicted,
val treeModel =
let val t = TTreeNode
val e = TTreeEmpty
in
t (25,
e,
t (7,
e,
t (11, e, e, e),
t (10,
e,
t (4, e, e, e),
e
)
),
t (30,
e,
t (7,
e,
t (22, e, e, e),
e
),
e)
)
end
although it would probably be more convenient with a function like AddNode, only you need to specify a consistent way for elements to be added to this kind of tree. How does the binary search tree logic translate to ternary trees?
Determining if a path exists in a ternary tree
You can determine if a path to a node exists. Since a tree can have any depth, a path can have any length and should be represented with a list. The path [25, 7, 10, 4] exists in your ternary tree, for example.
fun pathExists [] _ = true (* the empty path is trivially found *)
| pathExists _ TTreeEmpty = false (* no non-empty path goes through an empty tree *)
| pathExists (x::xs) (TTreeNode (y, subtree1, subtree2, subtree3)) =
x = y andalso
(pathExists xs subtree1 orelse
pathExists xs subtree2 orelse
pathExists xs subtree3)
Testing this function with treeModel above:
- pathExists [25, 7, 10, 4] treeModel;
> val it = true : bool
- pathExists [25, 30, 7, 22] treeModel;
> val it = true : bool
- pathExists [25, 7, 11, 9] treeModel;
> val it = false : bool
- pathExists [25, 7, 9] treeModel;
> val it = false : bool
Inserting a point in a ternary tree [???]
A template for this function could be
fun AddNode (x, TTreeEmpty) = TTreeNode (x, TTreeEmpty, TTreeEmpty, TTreeEmpty)
| AddNode (x, TTreeNode (y, subtree1, subtree2, subtree3))) = ???
but if x <> y, which of the three subtrees should it try to add x to?
K-d tree?
Edit 1: After having answered, I realize that perhaps you are looking for a k-d tree? A k-d tree can store k-dimensional vectors, using only binary trees, in a way that allows efficient, locality-specific lookups.
The great trick here is to say that the 1st level of the tree divides the space into two halves on the X-axis, the 2nd level of the tree divides the left/right halves into halves on the Y-axis, the 3rd level of the tree divides the left/right halves into halves on the Z-axis, the 4th level on the X-axis again, the 5th level on the Y-axis again, and so on.
Here is an initial translation of that pseudocode for k = 3 into Standard ML:
(* 'byDimension dim (p1, p2)' determines if p1 is greater than p2 in dimension dim. *)
fun byDimension 0 ((x1,_,_), (x2,_,_)) = x1 > x2
| byDimension 1 ((_,y1,_), (_,y2,_)) = y1 > y2
| byDimension 2 ((_,_,z1), (_,_,z2)) = z1 > z2
| byDimension d _ _ = raise Fail ("Invalid dimension " ^ Int.toString d)
(* split points into two halves and isolate the middle element *)
fun splitAt dim points = ...
(* The number of dimensions, matching the arity of the point tuples below *)
val k = 3
fun make3DTree ([], _) = BTreeEmpty
| make3DTree (points, depth) =
let val axis = depth mod k
val len = List.length points
val points_sorted = ListMergeSort.sort (byDimension axis) points
val (points_left, median, points_right) = splitAt len points_sorted
in BTreeNode (median,
make3DTree (points_left, depth+1),
make3DTree (points_right, depth+1))
end
One optimization could be to reduce the constant re-sorting.
Adding a single 3D point to this tree is well-defined, but does not guarantee that the tree is particularly balanced:
(* get the (n mod 3)-th value of a 3-tuple. *)
fun getDimValue (n, (x,y,z)) =
let val m = n mod k
in if m = 0 then x else
if m = 1 then y else
if m = 2 then z else
raise Fail ("Invalid dimension " ^ Int.toString n)
end
(* the smallest tree that contains a k-dimensional point
* has depth k-1 (because of 0-indexing). *)
fun deepEnough depth = depth >= k-1
fun insertNode (point, BTreeEmpty, depth) =
let val v1 = getDimValue (depth, point)
val v2 = getDimValue (depth+1, point)
val (left, right) =
if deepEnough depth
then (BTreeEmpty, BTreeEmpty)
else if v1 > v2
then (insertNode (point, BTreeEmpty, depth+1), BTreeEmpty)
else (BTreeEmpty, insertNode (point, BTreeEmpty, depth+1))
in BTreeNode (v1, left, right)
end
| insertNode (point, BTreeNode (v1, left, right), depth) =
let val v2 = getDimValue (depth, point)
in if v1 > v2
then BTreeNode (v1, insertNode (point, left, depth+1), right)
else BTreeNode (v1, left, insertNode (point, right, depth+1))
end
Determining if a point exists in this tree can be achieved by performing a nearest-neighbor search, which is slightly less trivial to implement.

Related

Convert an infinite tree into an infinite stream

Description of the problem
I have a lazy infinite binary tree:
type 'a tree_node = TN of 'a * 'a inf_btree * 'a inf_btree
and 'a inf_btree = 'a tree_node Lazy.t
let rec ibt_map (f : 'a -> 'b) (t : 'a inf_btree) : 'b inf_btree =
let TN (x, ltree, rtree) = Lazy.force t in
lazy (TN (f x, ibt_map f ltree, ibt_map f rtree))
let rec example : int inf_btree =
lazy (TN (1,
ibt_map ((+) 1) example,
ibt_map ((+) 2) example
)
)
;;
and a lazy stream:
type 'a link_node = LN of 'a * 'a stream
and 'a stream = 'a link_node Lazy.t
Now I want to transform a tree into a stream, in such a way that it preserves the order of the elements in the tree. More precisely, I want elements close to the root to come early in the stream. However, if it's done depth-first then half the tree will never occur in the stream:
let df_tree_to_stream (t : 'a inf_btree) : 'a stream =
let TN (x, ltree, rtree) = Lazy.force t in
let substream1 = df_tree_to_stream ltree in
let substream2 = df_tree_to_stream rtree in
lazy (LN (x, substream1))
(* how to work in substream2 ??? *)
An attempted solution by alternation
We could try to merge the two streams so that they alternate, but then the order of the elements will not be preserved. In the given example, the tree looks like
1
3 2
5 6 4 3
.........
The order of the stream should be
1, 3, 2, 5, 6, 4, 3, ...
But if we simply alternate the streams returned by subtrees, then the tree rooted at 3 would have streams that start with 5 and 6. So this subtree transforms into the stream 3, 5, 6 ... The other subtree becomes 2, 4, 3 ... So the overall resulting stream would be
1, 3, 2, 5, 4, ...
We could instead manage the order of visitation of nodes in the tree by maintaining a queue.
Question
My question is, is there simpler a way? Can we get the desired order not using a queue and only using recursion?
It is unusual to try to get away from the tail recursive version, but yes you can hide the queue of the breadth first traversal in a call stack. For instance, you can define an interleave function that switches the left and right source with decreasing frequency:
let rec interleave first period count l r () =
if count = 0 then
if first then
interleave false period period r l ()
else
let period = 2 * period in
interleave true period period r l ()
else
match l () with
| Seq.Nil -> r ()
| Seq.Cons(y,l) ->
Seq.Cons(y, interleave first period (count-1) l r)
let interleave = interleave true 1 1
let rec to_seq tree () =
let lazy (TN(x,l,r)) = tree in
Seq.Cons(x, interleave (to_seq l) (to_seq r))

verifying size of binary trees?

I have a datatype in this way
datatype 'a bin_tree =
Leaf of 'a
| Node of 'a bin_tree (* left tree *)
* int (* size of left tree *)
* int (* size of right tree *)
* 'a bin_tree (* right tree *)
so an example for correct tree would be:
val tree1 =
Node(Node(Node(Leaf 47, 1, 1, Leaf 38),
2,1,
Leaf 55),
3,2,
Node(Leaf 27, 1, 1, Leaf 96))
and an example for violating tree would be
val tree1false =
Node(Node(Node(Leaf 47, 1, 1, Leaf 38),
2,1,
Leaf 55),
4,2,
Node(Leaf 27, 1, 1, Leaf 96))
How can I write a predicate test such that
- test tree1;
val it = true : bool
- test tree1false;
val it = false : bool
This is a recursive problem. Before solving recursive problems on trees, it is a good idea to have a firm grasp on recursion on lists. You could say that trees are generalisations of lists, or that lists are special-cases of trees: lists have one tail, trees can have any number of tails depending on the type of tree. So here is how you could reconstruct and solve the problem using lists:
If, instead of the typical list definition, you have a list that also memoizes its own length:
(* datatype 'a list = [] | :: of 'a * 'a list *)
datatype 'a lenlist = Nil | Cons of int * 'a * 'a lenlist
Then you can test that the stored length is in accordance with the actual number of values.
I'll start by creating a function that counts to illustrate the part of the function that performs recursion:
(* For regular built-in lists *)
fun count0 [] = 0
| count0 (x::xs) = 1 + count0 xs
(* Counting the memoized list type disregarding the n *)
fun count1 Nil = 0
| count1 (Cons (n, x, xs)) = 1 + count1 xs
The next part is that I'd like, in each recursive step, to test that the stored number n is also in accordance with the actual counting. What is the return type of this function? Well, the test function that you want should be 'a lenlist -> bool and the count function that I made is 'a lenlist -> int.
I will suggest that you make a testcount that kinda does both. But you can do so in many ways, e.g. by giving it "extra arguments", or by giving it "extra return values". I will demonstrate both, just to show that sometimes one is better than the other and experience will tell you which.
Here is a val testcount1 : 'a lenlist -> bool * int function:
fun testcount1 Nil = (true, 0)
| testcount1 (Cons (n, x, xs)) =
let val (good_so_far, m) = testcount1 xs
val still_good = good_so_far andalso n = m + 1
in (still_good, m + 1)
end
val goodList = Cons (4, #"c", Cons (3, #"o", Cons (2, #"o", Cons (1, #"l", Nil))))
val badList = Cons (3, #"d", Cons (2, #"e", Cons (1, #"r", Cons (0, #"p", Nil))))
Testing this,
- testcount1 goodList;
> val it = (true, 4) : bool * int
- testcount1 badList;
> val it = (false, 4) : bool * int
This shows that testcount1 returns whether the numbers add up and the list's actual length, which was necessary during recursion to test that the numbers add up in each step, but in the end is no longer necessary. You could wrap this testcount function up in a simpler test function that only cares about the bool:
fun test xs = #1 (testcount1 xs)
Here is another way to go about: There is something not so satisfying with the way testcount1 recurses. It keeps calculating the m + 1s even though it is able to determine that a list (e.g. at Cons (0, #"p", Nil)) is broken.
Here is an alternate val testcount2 : 'a lenlist * int -> bool that stores a number in an extra argument instead:
fun testcount2 (Nil, 0) = true
| testcount2 (Nil, _) = false
| testcount2 (Cons (n, x, xs), m) =
n = m andalso testcount2 (xs, m - 1)
This seems a lot neater to me: The function is tail-recursive, and it stops immediately when it senses that something is fishy. So it doesn't need to traverse the entire list if it's broken at the head. The downside is that it needs to know the length, which we don't know yet. But we can compensate by assuming that whatever is advertised is correct until it's clearly the case, or not.
Testing this function, you need to not only give it a goodList or a badList but also an m:
- testcount2 (goodList, 4);
> val it = true : bool
- testcount2 (badList, 4);
> val it = false : bool
- testcount2 (badList, 3);
> val it = false : bool
It's important that this function doesn't just compare n = m, since in badList, that'd result in agreeing that badList is 3 elements long, since n = m is true for each iteration in all Cons cases. This is helped in the two Nil cases that require us to have reached 0 and not e.g. ~1 as is the case for badList.
This function can also be wrapped inside test to hide the fact that we feed it an extra argument derived from the 'a lenlist itself:
fun size Nil = 0
| size (Cons (n, _, _)) = n
fun test xs = testcount2 (xs, size xs)
Some morals so far:
Sometimes it is necessary to create helper functions to solve your initial problem.
Those helper functions are not restricted to have the same type signature as the function you deliver (whether this is for an exercise in school, or for an external API/library).
Sometimes it helps to extend the type that your function returns.
Sometimes it helps to extend the arguments of your functions.
Just because your task is "Write a function foo -> bar", this does not mean that you cannot create this by composing functions that return a great deal more or less than foo or bar.
Now, for some hints for solving this on binary trees:
Repeating the data type,
datatype 'a bin_tree =
Leaf of 'a
| Node of 'a bin_tree (* left tree *)
* int (* size of left tree *)
* int (* size of right tree *)
* 'a bin_tree (* right tree *)
You can start by constructing a skeleton for your function based on the ideas above:
fun testcount3 (Leaf x, ...) = ...
| testcount3 (Leaf x, ...) = ...
| testcount3 (Node (left, leftC, rightC, right), ...) = ...
I've embedded som hints here:
Your solution should probably contain pattern matches against Leaf x and Node (left, leftC, rightC, right). And given the "extra argument" type of solution (which at least proved nice for lists, but we'll see) needed two Leaf x cases. Why was that?
If, in the case of lists, the "extra argument" m represents the expected length of the list, then what would an "extra argument" represent in the case of trees? You can't say "it's the length of the list", since it's a tree. How do you capture the part where a tree branches?
If this is still too difficult, consider solving the problem for lists without copy-pasting. That is, you're allowed to look at the solutions in this answer (but try not to), but you're not allowed to copy-paste code. You have to type it if you want to copy it.
As a start, try to define the helper function size that was used to produce test from testcount2, but for trees. So maybe call it sizeTree to avoid the name overlap, but other than that, try and make it resemble. Here's a skeleton:
fun sizeTree (Leaf x) = ...
| sizeTree (Node (left, leftC, rightC, right)) = ...
Sticking testcount3 and sizeTree together, once written, should be easy as tau.

I want to make function maptree with standard ML

I want to make function maptree with standard ML.
If function f(x) = x + 1;
then
maptree(f, NODE(NODE(LEAF 1,LEAF 2),LEAF 3));
should make result
NODE(NODE(LEAF 2,LEAF 3),LEAF 4))
I write the code like below.
datatype 'a tree = LEAF of 'a | NODE of 'a tree * 'a tree;
fun f(x) = x + 1;
fun maptree(f, NODE(X, Y)) = NODE(maptree(f, X), maptree(f, Y))
| maptree(f, LEAF(X)) = LEAF(f X);
but when I execute this code like this
maptree(f, (NODE(NODE(LEAF 1,LEAF 2),LEAF 3)));
result is not I want to
(NODE(NODE(LEAF 2,LEAF 3),LEAF 4)))
but
NODE(NODE(LEAF #,LEAF #),LEAF 4)).
Why this happened(not a number but #)?
# is used by the REPL when the data structure it prints is deeper than a pre-set value. If you increase that value, you'll get the result you excepted. I assume you're using SML/NJ, which calls that setting print.depth:
sml -Cprint.depth=20
- maptree(f, (NODE(NODE(LEAF 1,LEAF 2),LEAF 3)));
val it = NODE (NODE (LEAF 2,LEAF 3),LEAF 4) : int tree
You can find more options like these by executing sml -H. Look them up under the "compiler print settings" section:
compiler print settings:
print.depth (max print depth)
print.length (max print length)
print.string-depth (max string print depth)
print.intinf-depth (max IntInf.int print depth)
print.loop (print loop)
print.signatures (max signature expansion depth)
print.opens (print `open')
print.linewidth (line-width hint for pretty printer)
Some comments:
I would probably go with the definition
datatype 'a tree = Leaf | Node of 'a tree * 'a * 'a tree
so that trees with zero or two elements can also be expressed.
I would probably curry the tree map function
fun treemap f Leaf = Leaf
| treemap f (Node (l, x, r)) = Node (treemap f l, x, treemap f r)
since you can then partially apply it, e.g. like:
(* 'abstree t' returns t where all numbers are made positive *)
val abstree = treemap Int.abs
(* 'makeFullTree n' returns a full binary tree of size n *)
fun makeFullTree 0 = Leaf
| makeFullTree n =
let val subtree = makeFullTree (n-1)
in Node (subtree, n, subtree)
end
(* 'treetree t' makes an int tree into a tree of full trees! *)
val treetree = treemap makeFullTree
You may at some point want to fold a tree, too.

Layout a binary search tree

This is the problem 65 in OCaml 99
Given a binary search and layout it like this:
The y axis of a node is easy as it is just the level number, starting from 1.
The x asix of a node is bit more complicated, but through observation, assuming the height of the whole tree is h, then the x of a node is the max size of the left child as if it is a full tree, i.e., x = 2 ^ (h-y)-1
However, there is a special case where the x of the left most node is always 1 that we need to handle.
Here is my code:
type 'a btree = Empty | Node of 'a * 'a btree * 'a btree
type 'a pos_binary_tree =
| E (* represents the empty tree *)
| N of 'a * int * int * 'a pos_binary_tree * 'a pos_binary_tree
let rec height = function
| Empty -> 0
| Node (_,l,r) -> 1 + max (height l) (height r)
let get_fullsize h level = (2. ** (Float.of_int (h+1-level)) |> Int.of_float) - 1
let layout_btree2_correct t =
let h = height t in
let rec lay off y = function
| Empty -> get_fullsize h y, E
| Node (w, Empty, r) when off = 0 ->
let wtr, newr = lay 1 (y+1) r in
1+wtr, N (w, 1, y+1, E, newr)
| Node (w, l, r) ->
let wt1, newl = lay off (y+1) l in
let wt2, newr = lay (off+wt1+1) (y+1) r in
wt1+wt2+1, N (w, off+wt1+1, y, newl, newr)
in
lay 0 1 t |> snd
What I do are:
get the height of the whole tree
always return back the full width that it might occupy
x should be the left node width + 1
For the special case of the left most node, it returns 1 + width of right as the width
In my way, I have to travel the tree one time first to get the height. Anyone can suggest better implementation, for example, just travel the tree once?
Are you asking for different algorithms to search the tree? Or different ways to implement that algorithm?
The Trees and Tree Algorithms section may be helpful to you. http://interactivepython.org/runestone/static/pythonds/index.html

Filling a normal binary tree in ML with values

Where let's say:
datatype bin_tree = Empty |
Node of value * bin_tree * bin_tree
How would I go about filling a binary tree (not a binary search tree where left is smaller than root and right bigger). Just values from a list inserted at each node in a binary tree.
You use the value constructors you've declared.
If we assume for a moment that value is int instead, then we for instance have that the tree
1
/ \
2 4
/
3
is represented by:
Node (1,
Node (2,
Node (3, Empty, Empty),
Empty
),
Node (4, Empty, Empty)
)
Or, equivalently, on one line:
Node (1, Node (2, Node (3, Empty, Empty), Empty), Node (4, Empty, Empty))
It's not really possible to help you, without knowing more about how you wan't your tree constructed from a given list. However here is an example that creates a balanced tree. It takes the first element and uses it as the node value, and then it splits the rest of the list into two sub lists of equal size (if possible), by taking all "even" element in the "left" list and all "odd" elements in the "right" list:
datatype 'a bin_tree = Empty
| Node of 'a * 'a bin_tree * 'a bin_tree
fun list_split xs =
let
fun loop [] (left, right) = (rev left, rev right)
| loop (x::y::xs) (left, right) = loop xs (x :: left, y :: right)
| loop (x :: xs) (left, right) = loop xs (x :: left, right)
in
loop xs ([], [])
end
fun built_tree [] = Empty
| built_tree (x :: xs) =
let
val (left, right) = list_split xs
val left_tree = built_tree left
val right_tree = built_tree right
in
Node (x, left_tree, right_tree)
end
The result:
- built_tree [1,2,3,4,5,6,7,8,9];
val it =
Node
(1,Node (2,Node (4,Node (8,Empty,Empty),Empty),Node (6,Empty,Empty)),
Node (3,Node (5,Node (9,Empty,Empty),Empty),Node (7,Empty,Empty)))
: int bin_tree
Here is an answer to the same question done in Java. This will probably help a good bit :).