-
Notifications
You must be signed in to change notification settings - Fork 135
/
99problems-61-to-70.t
398 lines (349 loc) · 15.1 KB
/
99problems-61-to-70.t
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
use v6;
use Test;
plan 15;
{
# P61 (*) Count the leaves of a binary tree
# A leaf is a node with no successors.
# Write a predicate count_leaves/2 to count them.
#
# % count_leaves(T,N) :- the binary tree T has N leaves
# only 'C' and 'D' are leaves
my $tree = ['A', ['B', ['C', Any, Any], ['D', Any, Any]], Any];
sub count_leaves($tree){
return 0 unless defined($tree);
return 1 if (not defined($tree[1])) and (not defined($tree[2]));
return count_leaves($tree[1]) + count_leaves($tree[2]);
}
is(count_leaves($tree), 2, "count_leaves works");
}
{
# P61A (*) Collect the leaves of a binary tree in a list
#
# A leaf is a node with no successors. Write a predicate leaves/2 to collect them
# in a list.
#
# % leaves(T,S) :- S is the list of all leaves of the binary tree T
# the spec does not specify if the tree should be flattened in pre/infix or
# postfix order, let's just assue prefix or infix
my $tree = ['A', ['B', ['C', Any, Any], ['D', Any, Any]], Any];
my @expected = 'C', 'D';
sub leaves($tree){
return @() unless defined($tree);
return @($tree[0],)
if (not defined($tree[1])) and (not defined($tree[2]));
return leaves($tree[1]), leaves($tree[2]);
}
is_deeply([leaves($tree)], @expected, "leaves() works");
}
{
#P62 (*) 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.
#
# % internals(T,S) :- S is the list of internal nodes of the binary tree T.
my $tree = ['A', ['B', ['C', Any, Any], ['D', Any, Any]], ['E', Any, Any]];
my @expected = 'A', 'B';
# assume preorder traversal
sub internals($tree){
return @() unless defined($tree);
gather {
take $tree[0]
if defined($tree[1]) and defined($tree[2]);
take internals($tree[1]);
take internals($tree[2]);
};
}
is_deeply([internals($tree)], @expected, "internals() collects internal nodes");
# P62B (*) 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.
#
# % atlevel(T,L,S) :- S is the list of nodes of the binary tree T at level L
#
# Using atlevel/3 it is easy to construct a predicate levelorder/2 which creates
# the level-order sequence of the nodes. However, there are more efficient ways
# to do that.
sub atlevel($tree, $level) {
return @() unless defined($tree);
return @($tree[0]) if $level == 1;
gather {
take atlevel($tree[1], $level - 1);
take atlevel($tree[2], $level - 1);
}
}
my @e1 = 'A', ;
my @e2 = 'B', 'E';
my @e3 = 'C', 'D';
is_deeply([atlevel($tree, 1)], @e1, "atlevel() works at level 1");
is_deeply([atlevel($tree, 2)], @e2, "atlevel() works at level 2");
is_deeply([atlevel($tree, 3)], @e3, "atlevel() works at level 3");
}
{
# P63 (**) Construct a complete binary tree
#
# 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, note that we start counting the levels from 1 at the root).
# 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 levelorder, starting at the root with
# number 1. In doing so, we realize that 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, supposed the
# successors do exist. This fact can be used to elegantly construct a
# complete binary tree structure. Write a predicate
# complete-binary-tree/2 with the following specification:
#
# % complete-binary-tree(N,T) :- T is a complete binary tree with N nodes. (+,?)
#
# Test your predicate in an appropriate way.
skip "Test(s) not yet written: (**) Construct a complete binary tree", 1;
}
sub count($tree) {
return 0 unless $tree.defined;
return 1 + count($tree[1]) + count($tree[2]);
}
{
# P64 (**) Layout a binary tree (1)
#
# 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.
#
# 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
#
#
#
# In order to store the position of the nodes, we extend the Prolog term
# representing a node (and its successors) as follows:
#
# % nil represents the empty tree (as usual)
# % t(W,X,Y,L,R) represents a (non-empty) binary tree with root W "positioned" at (X,Y), and subtrees L and R
#
# Write a predicate layout-binary-tree/2 with the following specification:
#
# % layout-binary-tree(T,PT) :- PT is the "positioned" binary tree obtained from
# the binary tree T. (+,?)
#
# Test your predicate in an appropriate way.
my $tree = ['n', ['k', ['c', ['a', Any, Any], ['h', ['g', ['e', Any, Any], Any], Any]], ['m', Any, Any]], ['u', ['p', Any, ['s', ['q', Any, Any]], Any], Any]];
my $expected = ['n', 8, 1,
['k', 6, 2,
['c', 2, 3,
['a', 1, 4, Any, Any],
['h', 5, 4,
['g', 4, 5,
['e', 3, 6, Any, Any], Any], Any]],
['m', 7, 3, Any, Any]],
['u', 12, 2,
['p', 9, 3, Any,
['s', 11, 4,
['q', 10, 5, Any, Any], Any]], Any]];
sub align($tree, $prev_x, $prev_y, $lr){
return Any unless defined($tree);
my $y = $prev_y + 1;
my $x = 0;
if $lr eq "l" {
$x = $prev_x - 1 - count($tree[2]);
} else {
$x = $prev_x + 1 + count($tree[1]);
}
return [$tree[0],
$x,
$y,
align($tree[1], $x, $y, "l"),
align($tree[2], $x, $y, "r")];
}
my $result = align($tree, 0, 0, "r");
is_deeply($result, $expected, "tree alignment works");
}
{
# P64 (**) Layout a binary tree (1)
#
# 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.
#
# 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
#
#
#
# In order to store the position of the nodes, we extend the Prolog term
# representing a node (and its successors) as follows:
#
# % nil represents the empty tree (as usual)
# % t(W,X,Y,L,R) represents a (non-empty) binary tree with root W "positioned" at (X,Y), and subtrees L and R
#
# Write a predicate layout-binary-tree/2 with the following specification:
#
# % layout-binary-tree(T,PT) :- PT is the "positioned" binary tree obtained from
# the binary tree T. (+,?)
#
# Test your predicate in an appropriate way.
my $tree = ['n', ['k', ['c', ['a', Any, Any], ['h', ['g', ['e', Any, Any], Any], Any]], ['m', Any, Any]], ['u', ['p', Any, ['s', ['q', Any, Any]], Any], Any]];
my $expected = ['n', 8, 1,
['k', 6, 2,
['c', 2, 3,
['a', 1, 4, Any, Any],
['h', 5, 4,
['g', 4, 5,
['e', 3, 6, Any, Any], Any], Any]],
['m', 7, 3, Any, Any]],
['u', 12, 2,
['p', 9, 3, Any,
['s', 11, 4,
['q', 10, 5, Any, Any], Any]], Any]];
sub align2($tree, $prev_x, $prev_y, $lr){
return Any unless defined($tree);
my $y = $prev_y + 1;
my $x = 0;
if $lr eq "l" {
$x = $prev_x - 1 - count($tree[2]);
} else {
$x = $prev_x + 1 + count($tree[1]);
}
return [$tree[0],
$x,
$y,
align2($tree[1], $x, $y, "l"),
align2($tree[2], $x, $y, "r")];
}
my $result = align2($tree, 0, 0, "r");
is_deeply($result, $expected, "tree alignment works");
}
{
# P66 (***) Layout a binary tree (3)
#
# Yet another layout strategy is shown in the illustration opposite. The
# method yields a very compact layout while maintaining a certaing
# 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?
skip "Test(s) not yet written: (***) Layout a binary tree (3)", 1;
}
{
# P67 (**) A string representation of binary trees
#
#
# Somebody represents binary trees as strings of the following type
# (see example opposite):
#
# 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.
my $tree = ['a', ['b', ['d'], ['e']], ['c', Any, ['f', ['g']]]];
my $expected = "a(b(d,e),c(,f(g,)))";
sub stringify($tree) {
return '' unless defined($tree);
return $tree[0] if not defined($tree[1]) and (not defined($tree[2]));
return $tree[0] ~ '(' ~ stringify($tree[1]) ~ ',' ~ stringify($tree[2]) ~ ')';
}
is(stringify($tree), $expected, "string representation of binary tree");
# b) Write the same predicate tree-string/2 using difference lists and a single
# predicate tree-dlist/2 which does the conversion between a tree and a
# difference list in both directions.
#
# For simplicity, suppose the information in the nodes is a single letter
# and there are no spaces in the string.
skip "Test(s) not yet written: (**) A string representation of binary trees", 1;
}
{
# P68 (**) Preorder and inorder sequences of binary trees
#
# We consider binary trees with nodes that are identified by single lower-case
# letters, as in the example of problem P67.
#
# 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.
#
# d) Solve problems a) to c) using difference lists. Cool! Use the predefined
# predicate time/1 to compare the solutions.
#
# What happens if the same character appears in more than one node. Try for
# instance pre-in-tree(aba,baa,T).
skip "Test(s) not yet written: (**) Preorder and inorder sequences of binary trees", 1;
}
{
# P69 (**) Dotstring representation of binary trees
#
# 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.
#
# Multiway Trees
#
# A multiway tree is composed of a root element and a (possibly empty) set of
# successors which are multiway trees themselves. A multiway tree is never
# empty. The set of successor trees is sometimes called a forest.
#
#
# In Prolog we represent a multiway tree by a term t(X,F), where X denotes the
# root node and F denotes the forest of successor trees (a Prolog list). The
# example tree depicted opposite is therefore represented by the following
# Prolog term:
#
# T = t(a,[t(f,[t(g,[])]),t(c,[]),t(b,[t(d,[]),t(e,[])])])
skip "Test(s) not yet written: (**) Dotstring representation of binary trees", 1;
}
{
# P70 (**) Tree construction from a node string
#
# We suppose that the nodes of a multiway tree contain single characters. In the
# depth-first order sequence of its nodes, a special character ^ has been
# inserted whenever, during the tree traversal, the move is a backtrack to the
# previous level.
#
# By this rule, the tree in the figure opposite is represented as: afg^^c^bd^e^^^
#
# Define the syntax of the string and write a predicate tree(String,Tree) to
# construct the Tree when the String is given. Work with atoms (instead of
# strings). Make your predicate work in both directions.
skip "Test(s) not yet written: (**) Tree construction from a node string", 1;
}
# vim: ft=perl6