Difference between revisions of "99 questions/61 to 69"

From HaskellWiki
Jump to navigation Jump to search
(cleaner soln to 62B)
m
 
(21 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 
__NOTOC__
 
__NOTOC__
   
This is part of [[H-99:_Ninety-Nine_Haskell_Problems|Ninety-Nine Haskell Problems]], based on [http://www.hta-bi.bfh.ch/~hew/informatik3/prolog/p-99/ Ninety-Nine Prolog Problems].
+
This is part of [[H-99:_Ninety-Nine_Haskell_Problems|Ninety-Nine Haskell Problems]], based on [https://prof.ti.bfh.ch/hew1/informatik3/prolog/p-99/ Ninety-Nine Prolog Problems].
   
  +
<h2 style="border:none">Binary trees</h2>
If you want to work on one of these, put your name in the block so we know someone's working on it. Then, change n in your block to the appropriate problem number, and fill in the <Problem description>,<example in lisp>,<example in Haskell>,<solution in haskell> and <description of implementation> fields.
 
   
  +
As defined [[99 questions/54A to 60|here]].
== Binary trees ==
 
 
As defined in problem 54A.
 
   
 
An example tree:
 
An example tree:
Line 15: Line 13:
 
</haskell>
 
</haskell>
   
== Problem 61 ==
 
   
  +
== Problem 61 ==
Count the leaves of a binary tree
 
  +
<div style="border-bottom:1px solid #eee">Count the leaves of a binary tree. <span style="float:right"><small>[[99 questions/Solutions/61|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
 
A leaf is a node with no successors. Write a predicate count_leaves/2 to count them.
 
A leaf is a node with no successors. Write a predicate count_leaves/2 to count them.
   
<pre>
 
 
Example:
 
Example:
  +
  +
<pre>
 
% count_leaves(T,N) :- the binary tree T has N leaves
 
% count_leaves(T,N) :- the binary tree T has N leaves
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
> count_leaves tree4
 
2
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> countLeaves tree4
count_leaves Empty = 0
 
  +
2
count_leaves (Branch a Empty Empty) = 1
 
count_leaves (Branch a left right) = count_leaves left + count_leaves right
 
 
</haskell>
 
</haskell>
 
== Problem 61A ==
 
   
  +
Collect the leaves of a binary tree in a list
 
  +
== Problem 61A ==
  +
<div style="border-bottom:1px solid #eee">Collect the leaves of a binary tree in a list. <span style="float:right"><small>[[99 questions/Solutions/61A|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
 
A leaf is a node with no successors. Write a predicate leaves/2 to collect them in a list.
 
A leaf is a node with no successors. Write a predicate leaves/2 to collect them in a list.
  +
  +
Example:
   
 
<pre>
 
<pre>
Example:
 
 
% leaves(T,S) :- S is the list of all leaves of the binary tree T
 
% leaves(T,S) :- S is the list of all leaves of the binary tree T
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
> leaves tree4
 
[4, 2]
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
leaves :: Tree a -> [a]
+
λ> leaves tree4
  +
[4,2]
leaves Empty = []
 
leaves (Branch a Empty Empty) = [a]
 
leaves (Branch a left right) = leaves left ++ leaves right
 
 
</haskell>
 
</haskell>
  +
   
 
== Problem 62 ==
 
== Problem 62 ==
  +
<div style="border-bottom:1px solid #eee">Collect the internal nodes of a binary tree in a list. <span style="float:right"><small>[[99 questions/Solutions/62|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
Collect the internal nodes of a binary tree in a list
 
 
An internal node of a binary tree has either one or two non-empty successors. Write a predicate internals/2 to collect them in a list.
 
An internal node of a binary tree has either one or two non-empty successors. Write a predicate internals/2 to collect them in a list.
   
<pre>
 
 
Example:
 
Example:
  +
  +
<pre>
 
% internals(T,S) :- S is the list of internal nodes of the binary tree T.
 
% internals(T,S) :- S is the list of internal nodes of the binary tree T.
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
Prelude>internals tree4
 
Prelude>[1,2]
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
internals :: Tree a -> [a]
+
λ> internals tree4
  +
[1,2]
internals Empty = []
 
internals (Branch a Empty Empty) = []
 
internals (Branch a left right) = [a] ++ (internals left) ++ (internals right)
 
 
</haskell>
 
</haskell>
  +
 
  +
 
== Problem 62B ==
 
== Problem 62B ==
  +
<div style="border-bottom:1px solid #eee">Collect the nodes at a given level in a list. <span style="float:right"><small>[[99 questions/Solutions/62B|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
Collect the nodes at a given level in a list
 
 
A node of a binary tree is at level N if the path from the root to the node has length N-1. The root node is at level 1. Write a predicate atlevel/3 to collect all nodes at a given level in a list.
 
A node of a binary tree is at level N if the path from the root to the node has length N-1. The root node is at level 1. Write a predicate atlevel/3 to collect all nodes at a given level in a list.
   
<pre>
 
 
Example:
 
Example:
  +
  +
<pre>
 
% atlevel(T,L,S) :- S is the list of nodes of the binary tree T at level L
 
% atlevel(T,L,S) :- S is the list of nodes of the binary tree T at level L
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
Prelude>atlevel tree4 2
 
Prelude>[2,2]
 
</pre>
 
 
Solution:
 
   
 
<haskell>
 
<haskell>
  +
λ> atLevel tree4 2
atlevel :: Tree a -> Int -> [a]
 
  +
[2,2]
atlevel t level = loop t 1
 
where
 
loop Empty _ = []
 
loop (Branch a l r) n
 
| n == level = [a]
 
| otherwise = loop l (n+1) ++ loop r (n+1)
 
 
</haskell>
 
</haskell>
   
Another possibility is to decompose the problem:
 
 
<haskell>
 
levels :: Tree a -> [[a]]
 
levels Empty = repeat []
 
levels (Branch a l r) = [a] : zipWith (++) (levels l) (levels r)
 
 
atlevel :: Tree a -> Int -> [a]
 
atlevel t n = levels t !! (n-1)
 
</haskell>
 
   
 
== Problem 63 ==
 
== Problem 63 ==
  +
<div style="border-bottom:1px solid #eee">Construct a complete binary tree. <span style="float:right"><small>[[99 questions/Solutions/63|Solutions]]</small></span>
 
  +
</div>
Construct a complete binary tree
 
  +
&nbsp;<br>
  +
 
A complete binary tree with height H is defined as follows:
 
A complete binary tree with height H is defined as follows:
 
* The levels 1,2,3,...,H-1 contain the maximum number of nodes (i.e 2**(i-1) at the level i)
 
* The levels 1,2,3,...,H-1 contain the maximum number of nodes (i.e 2**(i-1) at the level i)
Line 131: Line 112:
   
 
Write a predicate complete_binary_tree/2.
 
Write a predicate complete_binary_tree/2.
  +
  +
Example:
   
 
<pre>
 
<pre>
Example:
 
 
% complete_binary_tree(N,T) :- T is a complete binary tree with N nodes.
 
% complete_binary_tree(N,T) :- T is a complete binary tree with N nodes.
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
  +
Main> complete_binary_tree 4
 
  +
<haskell>
  +
λ> completeBinaryTree 4
 
Branch 'x' (Branch 'x' (Branch 'x' Empty Empty) Empty) (Branch 'x' Empty Empty)
 
Branch 'x' (Branch 'x' (Branch 'x' Empty Empty) Empty) (Branch 'x' Empty Empty)
   
Main> is_complete_binary_tree $ Branch 'x' (Branch 'x' Empty Empty) (Branch 'x' Empty Empty)
+
λ> isCompleteBinaryTree $ Branch 'x' (Branch 'x' Empty Empty) (Branch 'x' Empty Empty)
 
True
 
True
</pre>
+
</haskell>
  +
   
Solution:
 
<haskell>
 
import Data.List
 
 
data Tree a = Empty | Branch a (Tree a) (Tree a)
 
deriving (Show, Eq)
 
 
filled :: Tree a -> [[Bool]]
 
filled Empty = repeat [False]
 
filled (Branch _ l r) = [True] : zipWith (++) (filled l) (filled r)
 
 
complete_binary_tree :: Int -> Tree Char
 
complete_binary_tree n = generate_tree 1
 
where generate_tree x
 
| x > n = Empty
 
| otherwise = Branch 'x' (generate_tree (2*x) )
 
(generate_tree (2*x+1))
 
 
is_complete_binary_tree :: Tree a -> Bool
 
is_complete_binary_tree Empty = True
 
is_complete_binary_tree t = and $ last_proper : zipWith (==) lengths powers
 
where levels = takeWhile or $ filled t
 
-- The upper levels of the tree should be filled.
 
-- Every level has twice the number of nodes as the one above it,
 
-- so [1,2,4,8,16,...]
 
lengths = map (length . filter id) $ init levels
 
powers = iterate (2*) 1
 
-- The last level should contain a number of filled spots,
 
-- and (maybe) some empty spots, but no filled spots after that!
 
last_filled = map head $ group $ last levels
 
last_proper = head last_filled && (length last_filled) < 3
 
</haskell>
 
 
 
== Problem 64 ==
 
== Problem 64 ==
  +
<div style="border-bottom:1px solid #eee">Layout algorithm for displaying trees. <span style="float:right"><small>[[99 questions/Solutions/64|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
 
Given a binary tree as the usual Prolog term t(X,L,R) (or nil). As a preparation for drawing the tree, a layout algorithm is required to determine the position of each node in a rectangular grid. Several layout methods are conceivable, one of them is shown in the illustration below:
 
Given a binary tree as the usual Prolog term t(X,L,R) (or nil). As a preparation for drawing the tree, a layout algorithm is required to determine the position of each node in a rectangular grid. Several layout methods are conceivable, one of them is shown in the illustration below:
   
http://www.hta-bi.bfh.ch/~hew/informatik3/prolog/p-99/p64.gif
+
https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/p64.gif
   
 
In this layout strategy, the position of a node v is obtained by the following two rules:
 
In this layout strategy, the position of a node v is obtained by the following two rules:
Line 189: Line 145:
   
 
Here is the example tree from the above illustration:
 
Here is the example tree from the above illustration:
  +
 
<haskell>
 
<haskell>
 
tree64 = Branch 'n'
 
tree64 = Branch 'n'
Line 215: Line 172:
 
)
 
)
 
</haskell>
 
</haskell>
  +
 
Example in Haskell:
 
Example in Haskell:
<pre>
 
> layout tree64
 
Branch ('n',(8,1)) (Branch ('k',(6,2)) (Branch ('c',(2,3)) ...
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> layout tree64
type Pos = (Int, Int)
 
  +
Branch ('n',(8,1)) (Branch ('k',(6,2)) (Branch ('c',(2,3)) ...
  +
</haskell>
  +
   
layout :: Tree a -> Tree (a, Pos)
 
layout t = fst (layoutAux 1 1 t)
 
where layoutAux x y Empty = (Empty, x)
 
layoutAux x y (Branch a l r) = (Branch (a, (x',y)) l' r', x'')
 
where (l', x') = layoutAux x (y+1) l
 
(r', x'') = layoutAux (x'+1) (y+1) r
 
</haskell>
 
The auxiliary function is passed the x-coordinate for the left-most node of the subtree, the y-coordinate for the root of the subtree, and the subtree itself.
 
It returns the subtree annotated with positions, plus the count of <tt>Branch</tt> nodes in the subtree.
 
 
 
== Problem 65 ==
 
== Problem 65 ==
  +
<div style="border-bottom:1px solid #eee">Layout algorithm for displaying trees (part 2). <span style="float:right"><small>[[99 questions/Solutions/65|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
 
An alternative layout method is depicted in the illustration below:
 
An alternative layout method is depicted in the illustration below:
   
http://www.hta-bi.bfh.ch/~hew/informatik3/prolog/p-99/p65.gif
+
https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/p65.gif
   
 
Find out the rules and write the corresponding function.
 
Find out the rules and write the corresponding function.
Line 247: Line 196:
   
 
Here is the example tree from the above illustration:
 
Here is the example tree from the above illustration:
  +
 
<haskell>
 
<haskell>
 
tree65 = Branch 'n'
 
tree65 = Branch 'n'
Line 267: Line 217:
 
)
 
)
 
</haskell>
 
</haskell>
  +
 
Example in Haskell:
 
Example in Haskell:
<pre>
 
> layout tree65
 
Branch ('n',(15,1)) (Branch ('k',(7,2)) (Branch ('c',(3,3)) ...
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> layout tree65
layout :: Tree a -> Tree (a, Pos)
 
  +
Branch ('n',(15,1)) (Branch ('k',(7,2)) (Branch ('c',(3,3)) ...
layout t = layoutAux x1 1 sep1 t
 
  +
</haskell>
where d = depth t
 
ld = leftdepth t
 
x1 = 2^(d-1) - 2^(d-ld) + 1
 
sep1 = 2^(d-2)
 
layoutAux x y sep Empty = Empty
 
layoutAux x y sep (Branch a l r) =
 
Branch (a, (x,y))
 
(layoutAux (x-sep) (y+1) (sep `div` 2) l)
 
(layoutAux (x+sep) (y+1) (sep `div` 2) r)
 
   
depth :: Tree a -> Int
 
depth Empty = 0
 
depth (Branch a l r) = max (depth l) (depth r) + 1
 
 
leftdepth :: Tree a -> Int
 
leftdepth Empty = 0
 
leftdepth (Branch a l r) = leftdepth l + 1
 
</haskell>
 
The auxiliary function is passed the x- and y-coordinates for the root of the subtree, the horizontal separation between the root and its child nodes, and the subtree itself.
 
It returns the subtree annotated with positions.
 
   
 
== Problem 66 ==
 
== Problem 66 ==
  +
<div style="border-bottom:1px solid #eee">Layout algorithm for displaying trees (part 3). <span style="float:right"><small>[[99 questions/Solutions/66|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
 
Yet another layout strategy is shown in the illustration below:
 
Yet another layout strategy is shown in the illustration below:
   
http://www.hta-bi.bfh.ch/~hew/informatik3/prolog/p-99/p66.gif
+
https://www.ic.unicamp.br/~meidanis/courses/mc336/2009s2/prolog/problemas/p66.gif
   
 
The method yields a very compact layout while maintaining a certain symmetry in every node. Find out the rules and write the corresponding Prolog predicate. Hint: Consider the horizontal distance between a node and its successor nodes. How tight can you pack together two subtrees to construct the combined binary tree?
 
The method yields a very compact layout while maintaining a certain symmetry in every node. Find out the rules and write the corresponding Prolog predicate. Hint: Consider the horizontal distance between a node and its successor nodes. How tight can you pack together two subtrees to construct the combined binary tree?
Line 311: Line 242:
   
 
Example in Haskell:
 
Example in Haskell:
  +
<pre>
 
  +
<haskell>
> layout tree65
 
  +
λ> layout tree65
 
Branch ('n',(5,1)) (Branch ('k',(3,2)) (Branch ('c',(2,3)) ...
 
Branch ('n',(5,1)) (Branch ('k',(3,2)) (Branch ('c',(2,3)) ...
</pre>
+
</haskell>
Solution:
 
<haskell>
 
layout :: Tree a -> Tree (a, Pos)
 
layout t = t'
 
where (l, t', r) = layoutAux x1 1 t
 
x1 = maximum l + 1
 
   
layoutAux :: Int -> Int -> Tree a -> ([Int], Tree (a, Pos), [Int])
 
layoutAux x y Empty = ([], Empty, [])
 
layoutAux x y (Branch a l r) = (ll', Branch (a, (x,y)) l' r', rr')
 
where (ll, l', lr) = layoutAux (x-sep) (y+1) l
 
(rl, r', rr) = layoutAux (x+sep) (y+1) r
 
sep = maximum (0:zipWith (+) lr rl) `div` 2 + 1
 
ll' = 0 : overlay (map (+sep) ll) (map (subtract sep) rl)
 
rr' = 0 : overlay (map (+sep) rr) (map (subtract sep) lr)
 
   
  +
== Problem 67A ==
-- overlay xs ys = xs padded out to at least the length of ys
 
  +
<div style="border-bottom:1px solid #eee">A string representation of binary trees. <span style="float:right"><small>[[99 questions/Solutions/67A|Solutions]]</small></span>
-- using any extra elements of ys
 
  +
</div>
overlay :: [a] -> [a] -> [a]
 
  +
&nbsp;<br>
overlay [] ys = ys
 
  +
overlay xs [] = xs
 
  +
Somebody represents binary trees as strings of the following type:
overlay (x:xs) (y:ys) = x : overlay xs ys
 
  +
</haskell>
 
  +
:a(b(d,e),c(,f(g,)))
The auxiliary function is passed the x- and y-coordinates for the root of the subtree and the subtree itself.
 
It returns
 
* a list of distances the laid-out tree extends to the left at each level,
 
* the subtree annotated with positions, and
 
* a list of distances the laid-out tree extends to the right at each level.
 
These distances are usually positive, but may be 0 or negative in the case of a skewed tree.
 
To put two subtrees side by side, we must determine the least even separation so that they do not overlap on any level.
 
Having determined the separation, we can compute the extents of the composite tree.
 
   
  +
a) Write a Prolog predicate which generates this string representation, if the tree is given as usual (as nil or t(X,L,R) term). Then write a predicate which does this inverse; i.e. given the string representation, construct the tree in the usual form. Finally, combine the two predicates in a single predicate tree_string/2 which can be used in both directions.
The definitions of <tt>layout</tt> and its auxiliary function use local recursion to compute the x-coordinates.
 
This works because nothing else depends on these coordinates.
 
   
  +
Example in Prolog
== Problem 67 ==
 
<Problem description>
 
   
 
<pre>
 
<pre>
  +
?- tree_to_string(t(x,t(y,nil,nil),t(a,nil,t(b,nil,nil))),S).
Example:
 
  +
S = 'x(y,a(,b))'
<example in lisp>
 
  +
?- string_to_tree('x(y,a(,b))',T).
  +
T = t(x, t(y, nil, nil), t(a, nil, t(b, nil, nil)))
  +
</pre>
   
 
Example in Haskell:
 
Example in Haskell:
<example in Haskell>
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> stringToTree "x(y,a(,b))" >>= print
<solution in haskell>
 
  +
Branch 'x' (Branch 'y' Empty Empty) (Branch 'a' Empty (Branch 'b' Empty Empty))
  +
λ> let t = cbtFromList ['a'..'z'] in stringToTree (treeToString t) >>= print . (== t)
  +
True
 
</haskell>
 
</haskell>
   
<description of implementation>
 
 
 
 
== Problem 68 ==
 
== Problem 68 ==
  +
<div style="border-bottom:1px solid #eee">Preorder and inorder sequences of binary trees. <span style="float:right"><small>[[99 questions/Solutions/68|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
  +
We consider binary trees with nodes that are identified by single lower-case letters, as in the example of Problem 67.
<Problem description>
 
   
  +
a) Write predicates preorder/2 and inorder/2 that construct the preorder and inorder sequence of a given binary tree, respectively. The results should be atoms, e.g. 'abdecfg' for the preorder sequence of the example in problem P67.
<pre>
 
  +
Example:
 
  +
b) Can you use preorder/2 from problem part a) in the reverse direction; i.e. given a preorder sequence, construct a corresponding tree? If not, make the necessary arrangements.
<example in lisp>
 
  +
  +
c) If both the preorder sequence and the inorder sequence of the nodes of a binary tree are given, then the tree is determined unambiguously. Write a predicate pre_in_tree/3 that does the job.
   
 
Example in Haskell:
 
Example in Haskell:
<example in Haskell>
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> let { Just t = stringToTree "a(b(d,e),c(,f(g,)))" ;
<solution in haskell>
 
  +
po = treeToPreorder t ;
  +
io = treeToInorder t } in preInTree po io >>= print
  +
Branch 'a' (Branch 'b' (Branch 'd' Empty Empty) (Branch 'e' Empty Empty)) (Branch 'c' Empty (Branch 'f' (Branch 'g' Empty Empty) Empty))
 
</haskell>
 
</haskell>
   
<description of implementation>
 
 
 
 
== Problem 69 ==
 
== Problem 69 ==
  +
<div style="border-bottom:1px solid #eee">Dotstring representation of binary trees. <span style="float:right"><small>[[99 questions/Solutions/69|Solutions]]</small></span>
  +
</div>
  +
&nbsp;<br>
   
  +
We consider again binary trees with nodes that are identified by single lower-case letters, as in the example of problem P67. Such a tree can be represented by the preorder sequence of its nodes in which dots (.) are inserted where an empty subtree (nil) is encountered during the tree traversal. For example, the tree shown in problem P67 is represented as 'abd..e..c.fg...'. First, try to establish a syntax (BNF or syntax diagrams) and then write a predicate tree_dotstring/2 which does the conversion in both directions. Use
<Problem description>
 
  +
difference lists.
 
<pre>
 
Example:
 
<example in lisp>
 
   
 
Example in Haskell:
 
Example in Haskell:
<example in Haskell>
 
</pre>
 
   
Solution:
 
 
<haskell>
 
<haskell>
  +
λ> fst (ds2tree example)
<solution in haskell>
 
  +
Branch 'a' (Branch 'b' (Branch 'd' Empty Empty) (Branch 'e' Empty Empty)) (Branch 'c' Empty (Branch 'f' (Branch 'g' Empty Empty) Empty))
</haskell>
 
   
  +
λ> tree2ds (Branch 'x' (Branch 'y' Empty Empty) (Branch 'z' (Branch '0' Empty Empty) Empty))
<description of implementation>
 
  +
"xy..z0..."
  +
</haskell>
   
   

Latest revision as of 05:35, 11 June 2023


This is part of Ninety-Nine Haskell Problems, based on Ninety-Nine Prolog Problems.

Binary trees

As defined here.

An example tree:

tree4 = Branch 1 (Branch 2 Empty (Branch 4 Empty Empty))
                 (Branch 2 Empty Empty)


Problem 61

Count the leaves of a binary tree. Solutions

 

A leaf is a node with no successors. Write a predicate count_leaves/2 to count them.

Example:

% count_leaves(T,N) :- the binary tree T has N leaves

Example in Haskell:

λ> countLeaves tree4
2


Problem 61A

Collect the leaves of a binary tree in a list. Solutions

 

A leaf is a node with no successors. Write a predicate leaves/2 to collect them in a list.

Example:

% leaves(T,S) :- S is the list of all leaves of the binary tree T

Example in Haskell:

λ> leaves tree4
[4,2]


Problem 62

Collect the internal nodes of a binary tree in a list. Solutions

 

An internal node of a binary tree has either one or two non-empty successors. Write a predicate internals/2 to collect them in a list.

Example:

% internals(T,S) :- S is the list of internal nodes of the binary tree T.

Example in Haskell:

λ> internals tree4
[1,2]


Problem 62B

Collect the nodes at a given level in a list. Solutions

 

A node of a binary tree is at level N if the path from the root to the node has length N-1. The root node is at level 1. Write a predicate atlevel/3 to collect all nodes at a given level in a list.

Example:

% atlevel(T,L,S) :- S is the list of nodes of the binary tree T at level L

Example in Haskell:

λ> atLevel tree4 2
[2,2]


Problem 63

Construct a complete binary tree. Solutions

 

A complete binary tree with height H is defined as follows:

  • The levels 1,2,3,...,H-1 contain the maximum number of nodes (i.e 2**(i-1) at the level i)
  • In level H, which may contain less than the maximum possible number of nodes, all the nodes are "left-adjusted". This means that in a levelorder tree traversal all internal nodes come first, the leaves come second, and empty successors (the nil's which are not really nodes!) come last.

Particularly, complete binary trees are used as data structures (or addressing schemes) for heaps.

We can assign an address number to each node in a complete binary tree by enumerating the nodes in level-order, starting at the root with number 1. For every node X with address A the following property holds: The address of X's left and right successors are 2*A and 2*A+1, respectively, if they exist. This fact can be used to elegantly construct a complete binary tree structure.

Write a predicate complete_binary_tree/2.

Example:

% complete_binary_tree(N,T) :- T is a complete binary tree with N nodes.

Example in Haskell:

λ> completeBinaryTree 4
Branch 'x' (Branch 'x' (Branch 'x' Empty Empty) Empty) (Branch 'x' Empty Empty)

λ> isCompleteBinaryTree $ Branch 'x' (Branch 'x' Empty Empty) (Branch 'x' Empty Empty)
True


Problem 64

Layout algorithm for displaying trees. Solutions

 

Given a binary tree as the usual Prolog term t(X,L,R) (or nil). As a preparation for drawing the tree, a layout algorithm is required to determine the position of each node in a rectangular grid. Several layout methods are conceivable, one of them is shown in the illustration below:

p64.gif

In this layout strategy, the position of a node v is obtained by the following two rules:

  • x(v) is equal to the position of the node v in the inorder sequence
  • y(v) is equal to the depth of the node v in the tree

Write a function to annotate each node of the tree with a position, where (1,1) in the top left corner or the rectangle bounding the drawn tree.

Here is the example tree from the above illustration:

tree64 = Branch 'n'
                (Branch 'k'
                        (Branch 'c'
                                (Branch 'a' Empty Empty)
                                (Branch 'h'
                                        (Branch 'g'
                                                (Branch 'e' Empty Empty)
                                                Empty
                                        )
                                        Empty
                                )
                        )
                        (Branch 'm' Empty Empty)
                )
                (Branch 'u'
                        (Branch 'p'
                                Empty
                                (Branch 's'
                                        (Branch 'q' Empty Empty)
                                        Empty
                                )
                        )
                        Empty
                )

Example in Haskell:

λ> layout tree64
Branch ('n',(8,1)) (Branch ('k',(6,2)) (Branch ('c',(2,3)) ...


Problem 65

Layout algorithm for displaying trees (part 2). Solutions

 

An alternative layout method is depicted in the illustration below:

p65.gif

Find out the rules and write the corresponding function. Hint: On a given level, the horizontal distance between neighboring nodes is constant.

Use the same conventions as in problem P64 and test your function in an appropriate way.

Here is the example tree from the above illustration:

tree65 = Branch 'n'
                (Branch 'k'
                        (Branch 'c'
                                (Branch 'a' Empty Empty)
                                (Branch 'e'
                                        (Branch 'd' Empty Empty)
                                        (Branch 'g' Empty Empty)
                                )
                        )
                        (Branch 'm' Empty Empty)
                )
                (Branch 'u'
                        (Branch 'p'
                                Empty
                                (Branch 'q' Empty Empty)
                        )
                        Empty
                )

Example in Haskell:

λ> layout tree65
Branch ('n',(15,1)) (Branch ('k',(7,2)) (Branch ('c',(3,3)) ...


Problem 66

Layout algorithm for displaying trees (part 3). Solutions

 

Yet another layout strategy is shown in the illustration below:

p66.gif

The method yields a very compact layout while maintaining a certain symmetry in every node. Find out the rules and write the corresponding Prolog predicate. Hint: Consider the horizontal distance between a node and its successor nodes. How tight can you pack together two subtrees to construct the combined binary tree?

Use the same conventions as in problem P64 and P65 and test your predicate in an appropriate way. Note: This is a difficult problem. Don't give up too early!

Which layout do you like most?

Example in Haskell:

λ> layout tree65
Branch ('n',(5,1)) (Branch ('k',(3,2)) (Branch ('c',(2,3)) ...


Problem 67A

A string representation of binary trees. Solutions

 

Somebody represents binary trees as strings of the following type:

a(b(d,e),c(,f(g,)))

a) Write a Prolog predicate which generates this string representation, if the tree is given as usual (as nil or t(X,L,R) term). Then write a predicate which does this inverse; i.e. given the string representation, construct the tree in the usual form. Finally, combine the two predicates in a single predicate tree_string/2 which can be used in both directions.

Example in Prolog

?- tree_to_string(t(x,t(y,nil,nil),t(a,nil,t(b,nil,nil))),S).
S = 'x(y,a(,b))'
?- string_to_tree('x(y,a(,b))',T).
T = t(x, t(y, nil, nil), t(a, nil, t(b, nil, nil)))

Example in Haskell:

λ> stringToTree "x(y,a(,b))" >>= print
Branch 'x' (Branch 'y' Empty Empty) (Branch 'a' Empty (Branch 'b' Empty Empty))
λ> let t = cbtFromList ['a'..'z'] in stringToTree (treeToString t) >>= print . (== t)
True


Problem 68

Preorder and inorder sequences of binary trees. Solutions

 

We consider binary trees with nodes that are identified by single lower-case letters, as in the example of Problem 67.

a) Write predicates preorder/2 and inorder/2 that construct the preorder and inorder sequence of a given binary tree, respectively. The results should be atoms, e.g. 'abdecfg' for the preorder sequence of the example in problem P67.

b) Can you use preorder/2 from problem part a) in the reverse direction; i.e. given a preorder sequence, construct a corresponding tree? If not, make the necessary arrangements.

c) If both the preorder sequence and the inorder sequence of the nodes of a binary tree are given, then the tree is determined unambiguously. Write a predicate pre_in_tree/3 that does the job.

Example in Haskell:

λ> let { Just t = stringToTree "a(b(d,e),c(,f(g,)))" ;
         po = treeToPreorder t ;
         io = treeToInorder t } in preInTree po io >>= print
Branch 'a' (Branch 'b' (Branch 'd' Empty Empty) (Branch 'e' Empty Empty)) (Branch 'c' Empty (Branch 'f' (Branch 'g' Empty Empty) Empty))


Problem 69

Dotstring representation of binary trees. Solutions

 

We consider again binary trees with nodes that are identified by single lower-case letters, as in the example of problem P67. Such a tree can be represented by the preorder sequence of its nodes in which dots (.) are inserted where an empty subtree (nil) is encountered during the tree traversal. For example, the tree shown in problem P67 is represented as 'abd..e..c.fg...'. First, try to establish a syntax (BNF or syntax diagrams) and then write a predicate tree_dotstring/2 which does the conversion in both directions. Use difference lists.

Example in Haskell:

λ> fst (ds2tree example)
Branch 'a' (Branch 'b' (Branch 'd' Empty Empty) (Branch 'e' Empty Empty)) (Branch 'c' Empty (Branch 'f' (Branch 'g' Empty Empty) Empty))

λ> tree2ds (Branch 'x' (Branch 'y' Empty Empty) (Branch 'z' (Branch '0' Empty Empty) Empty))
"xy..z0..."