2015-11-18 15:06:25 +00:00
|
|
|
/**
|
|
|
|
* @file trees.yap
|
|
|
|
* @author R.A.O'Keefe
|
|
|
|
This file has been included as an YAP library by Vitor Santos Costa, 1999
|
|
|
|
|
|
|
|
* @date Wed Nov 18 01:30:42 2015
|
|
|
|
*
|
|
|
|
* @brief
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
:- module(trees, [
|
|
|
|
get_label/3,
|
|
|
|
list_to_tree/2,
|
|
|
|
map_tree/3,
|
|
|
|
put_label/4,
|
|
|
|
tree_size/2,
|
|
|
|
tree_to_list/2
|
|
|
|
]).
|
2001-04-09 20:54:03 +01:00
|
|
|
|
2015-11-18 15:06:25 +00:00
|
|
|
:- meta_predicate
|
|
|
|
map_tree(2, ?, ?).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%
|
2001-04-09 20:54:03 +01:00
|
|
|
% File : TREES.PL
|
2015-11-18 15:06:25 +00:00
|
|
|
% Author :
|
2001-04-09 20:54:03 +01:00
|
|
|
% Updated: 8 November 1983
|
|
|
|
% Purpose: Updatable binary trees.
|
|
|
|
|
2015-11-18 15:06:25 +00:00
|
|
|
|
|
|
|
/** @defgroup trees Updatable Binary Trees
|
|
|
|
@ingroup library
|
|
|
|
@{
|
|
|
|
|
|
|
|
The following queue manipulation routines are available once
|
|
|
|
included with the `use_module(library(trees))` command.
|
|
|
|
|
|
|
|
These are the routines I meant to describe in DAI-WP-150, but the
|
2001-04-09 20:54:03 +01:00
|
|
|
wrong version went in. We have
|
2015-11-18 15:06:25 +00:00
|
|
|
+ list_to_tree : O(N)
|
|
|
|
+ tree_to_list : O(N)
|
|
|
|
+ tree_size : O(N)
|
|
|
|
+ map_tree : O(N)
|
|
|
|
+ get_label : O(lg N)
|
|
|
|
+ put_label : O(lg N)
|
2001-04-09 20:54:03 +01:00
|
|
|
where N is the number of elements in the tree. The way get_label
|
|
|
|
and put_label work is worth noting: they build up a pattern which
|
|
|
|
is matched against the whole tree when the position number finally
|
|
|
|
reaches 1. In effect they start out from the desired node and
|
|
|
|
build up a path to the root. They still cost O(lg N) time rather
|
|
|
|
than O(N) because the patterns contain O(lg N) distinct variables,
|
|
|
|
with no duplications. put_label simultaneously builds up a pattern
|
|
|
|
to match the old tree and a pattern to match the new tree.
|
|
|
|
*/
|
|
|
|
|
2015-11-18 15:06:25 +00:00
|
|
|
/** @pred get_label(+ _Index_, + _Tree_, ? _Label_)
|
2014-09-11 20:06:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
Treats the tree as an array of _N_ elements and returns the
|
|
|
|
_Index_-th.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** @pred list_to_tree(+ _List_, - _Tree_)
|
|
|
|
|
|
|
|
|
|
|
|
Takes a given _List_ of _N_ elements and constructs a binary
|
|
|
|
_Tree_.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
/** @pred map_tree(+ _Pred_, + _OldTree_, - _NewTree_)
|
|
|
|
|
|
|
|
|
|
|
|
Holds when _OldTree_ and _NewTree_ are binary trees of the same shape
|
|
|
|
and `Pred(Old,New)` is true for corresponding elements of the two trees.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
/** @pred put_label(+ _Index_, + _OldTree_, + _Label_, - _NewTree_)
|
|
|
|
|
|
|
|
|
|
|
|
constructs a new tree the same shape as the old which moreover has the
|
|
|
|
same elements except that the _Index_-th one is _Label_.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
/** @pred tree_size(+ _Tree_, - _Size_)
|
|
|
|
|
|
|
|
|
|
|
|
Calculates the number of elements in the _Tree_.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
/** @pred tree_to_list(+ _Tree_, - _List_)
|
|
|
|
|
|
|
|
|
|
|
|
Is the converse operation to list_to_tree.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2001-04-09 20:54:03 +01:00
|
|
|
/*
|
|
|
|
:- mode
|
|
|
|
get_label(+, +, ?),
|
|
|
|
find_node(+, +, +),
|
|
|
|
list_to_tree(+, -),
|
|
|
|
list_to_tree(+, +, -),
|
|
|
|
list_to_tree(+),
|
|
|
|
map_tree(+, +, -),
|
|
|
|
put_label(+, +, +, -),
|
|
|
|
find_node(+, +, +, -, +),
|
|
|
|
tree_size(+, ?),
|
|
|
|
tree_size(+, +, -),
|
|
|
|
tree_to_list(+, -),
|
|
|
|
tree_to_list(+, -, -).
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
% get_label(Index, Tree, Label)
|
|
|
|
% treats the tree as an array of N elements and returns the Index-th.
|
|
|
|
% If Index < 1 or > N it simply fails, there is no such element.
|
|
|
|
|
|
|
|
get_label(N, Tree, Label) :-
|
|
|
|
find_node(N, Tree, t(Label,_,_)).
|
|
|
|
|
|
|
|
|
|
|
|
find_node(1, Tree, Tree) :- !.
|
|
|
|
find_node(N, Tree, Node) :-
|
|
|
|
N > 1,
|
|
|
|
0 is N mod 2,
|
|
|
|
M is N / 2, !,
|
|
|
|
find_node(M, Tree, t(_,Node,_)).
|
|
|
|
find_node(N, Tree, Node) :-
|
|
|
|
N > 2,
|
|
|
|
1 is N mod 2,
|
|
|
|
M is N / 2, !,
|
|
|
|
find_node(M, Tree, t(_,_,Node)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% list_to_tree(List, Tree)
|
|
|
|
% takes a given List of N elements and constructs a binary Tree
|
|
|
|
% where get_label(K, Tree, Lab) <=> Lab is the Kth element of List.
|
|
|
|
|
|
|
|
list_to_tree(List, Tree) :-
|
|
|
|
list_to_tree(List, [Tree|Tail], Tail).
|
|
|
|
|
|
|
|
|
|
|
|
list_to_tree([Head|Tail], [t(Head,Left,Right)|Qhead], [Left,Right|Qtail]) :-
|
|
|
|
list_to_tree(Tail, Qhead, Qtail).
|
|
|
|
list_to_tree([], Qhead, []) :-
|
|
|
|
list_to_tree(Qhead).
|
|
|
|
|
|
|
|
|
|
|
|
list_to_tree([t|Qhead]) :-
|
|
|
|
list_to_tree(Qhead).
|
|
|
|
list_to_tree([]).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% map_tree(Pred, OldTree, NewTree)
|
|
|
|
% is true when OldTree and NewTree are binary trees of the same shape
|
|
|
|
% and Pred(Old,New) is true for corresponding elements of the two trees.
|
|
|
|
% In fact this routine is perfectly happy constructing either tree given
|
|
|
|
% the other, I have given it the mode I have for that bogus reason
|
|
|
|
% "efficiency" and because it is normally used this way round. This is
|
|
|
|
% really meant more as an illustration of how to map over trees than as
|
|
|
|
% a tool for everyday use.
|
|
|
|
|
|
|
|
map_tree(Pred, t(Old,OLeft,ORight), t(New,NLeft,NRight)) :-
|
2008-07-11 18:02:10 +01:00
|
|
|
once(call(Pred, Old, New)),
|
2001-04-09 20:54:03 +01:00
|
|
|
map_tree(Pred, OLeft, NLeft),
|
|
|
|
map_tree(Pred, ORight, NRight).
|
|
|
|
map_tree(_, t, t).
|
|
|
|
|
|
|
|
% put_label(Index, OldTree, Label, NewTree)
|
|
|
|
% constructs a new tree the same shape as the old which moreover has the
|
|
|
|
% same elements except that the Index-th one is Label. Unlike the
|
|
|
|
% "arrays" of Arrays.Pl, OldTree is not modified and you can hang on to
|
|
|
|
% it as long as you please. Note that O(lg N) new space is needed.
|
|
|
|
|
|
|
|
put_label(N, Old, Label, New) :-
|
|
|
|
find_node(N, Old, t(_,Left,Right), New, t(Label,Left,Right)).
|
|
|
|
|
|
|
|
|
|
|
|
find_node(1, Old, Old, New, New) :- !.
|
|
|
|
find_node(N, Old, OldSub, New, NewSub) :-
|
|
|
|
N > 1,
|
|
|
|
0 is N mod 2,
|
|
|
|
M is N / 2, !,
|
|
|
|
find_node(M, Old, t(Label,OldSub,Right), New, t(Label,NewSub,Right)).
|
|
|
|
find_node(N, Old, OldSub, New, NewSub) :-
|
|
|
|
N > 2,
|
|
|
|
1 is N mod 2,
|
|
|
|
M is N / 2, !,
|
|
|
|
find_node(M, Old, t(Label,Left,OldSub), New, t(Label,Left,NewSub)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% tree_size(Tree, Size)
|
|
|
|
% calculates the number of elements in the Tree. All trees made by
|
|
|
|
% list_to_tree that are the same size have the same shape.
|
|
|
|
|
|
|
|
tree_size(Tree, Size) :-
|
|
|
|
tree_size(Tree, 0, Total), !,
|
|
|
|
Size = Total.
|
|
|
|
|
|
|
|
|
|
|
|
tree_size(t(_,Left,Right), SoFar, Total) :-
|
|
|
|
tree_size(Right, SoFar, M),
|
|
|
|
N is M+1, !,
|
|
|
|
tree_size(Left, N, Total).
|
|
|
|
tree_size(t, Accum, Accum).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% tree_to_list(Tree, List)
|
|
|
|
% is the converse operation to list_to_tree. Any mapping or checking
|
|
|
|
% operation can be done by converting the tree to a list, mapping or
|
|
|
|
% checking the list, and converting the result, if any, back to a tree.
|
|
|
|
% It is also easier for a human to read a list than a tree, as the
|
|
|
|
% order in the tree goes all over the place.
|
|
|
|
|
|
|
|
tree_to_list(Tree, List) :-
|
|
|
|
tree_to_list([Tree|Tail], Tail, List).
|
|
|
|
|
|
|
|
|
|
|
|
tree_to_list([], [], []) :- !.
|
|
|
|
tree_to_list([t|_], _, []) :- !.
|
|
|
|
tree_to_list([t(Head,Left,Right)|Qhead], [Left,Right|Qtail], [Head|Tail]) :-
|
|
|
|
tree_to_list(Qhead, Qtail, Tail).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
list(0, []).
|
|
|
|
list(N, [N|L]) :- M is N-1, list(M, L).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|