Prolog二进制搜索树测试 - 不需要的父母的父节点比较

时间:2016-01-07 17:55:21

标签: prolog binary-tree binary-search-tree

我是Prolog的新秀,请记住这一点。 我尝试编写一个谓词来确定某个给定的术语是否是二叉搜索树。我想出了这段代码:

is_btree(nil).
is_btree(node(N,L,R)) :-
   number(N),
   is_btree(L), 
   is_btree(R), 
   small(N, R),
   big(N, L).

small(N, nil).
small(N, node(M,L,R)) :-
   N < M,
   small(N, L),
   small(N, R).

big(N, nil).
big(N, node(M,L,R)) :-
   N > M,
   big(N, L),
   big(N, R).

在我测试右侧有一个节点“高于父节点”的图表之前,它工作得很好,但它高于或等于父节点的父节点。在这种情况下,Prolog报告失败。

以下是意外失败的示例查询:

?- is_btree(node(9,node( 3,node( 2,nil,nil),
                           node(10,nil,nil)),
                   node(12,node( 8,nil,nil),
                           node(15,nil,nil)))).
false.

当左侧的某个节点高于父节点的父节点时会出现一个非常类似的问题 - 如下图所示的情况:

enter image description here

如何仅使用其直接父节点的值检查节点值,而不检查父节点父节点的值?

5 个答案:

答案 0 :(得分:3)

以下是您要解决的问题略有不同。

  1. 收集元素:按顺序

    in_order(nil) --> [].
    in_order(node(X,L,R)) --> in_order(L), [X], in_order(R).
    
  2. 用于关联相邻的元素(均为有限域变量)

    chain(Zs, #<)
    
  3. 让我们把它们放在一起,并像这样定义is_bintreeFD/1

    :- use_module(library(clpfd)).
    
    is_bintreeFD(T) :-
       phrase(in_order(T), Zs),
       chain(Zs, #<).
    

    示例查询:

    ?- is_bintreeFD(node(9,node( 3,node(2,nil,nil),node(10,nil,nil)),
                           node(12,node(8,nil,nil),node(15,nil,nil)))).
    false.
    
    ?- is_bintreeFD(node(9,node( 3,node( 2,nil,nil),node( 8,nil,nil)),
                           node(12,node(10,nil,nil),node(15,nil,nil)))).
    true.
    

答案 1 :(得分:3)

这个答案直接跟随this previous answer,尤其是@WillNess的评论建议&#34; [...] 切换两个目标,所以尽快停止遍历失败时 [...] chain [...]&#34;之前有phrase

lazy_chain/2chain/2类似,但使用等待充分的实例化:

:- use_module(library(clpfd)).

lazy_chain(Zs, R_2) :-
   (  var(R_2)                  -> instantiation_error(R_2)
   ;  clpfd:chain_relation(R_2) -> freeze(Zs, lazy_chain_aux(Zs,R_2))
   ;  otherwise                 -> domain_error(chain_relation, R_2)
   ).

lazy_chain_aux([], _).
lazy_chain_aux([Z0|Zs], R_2) :-
   freeze(Zs, lazy_chain_aux_(Zs,R_2,Z0)).

lazy_chain_aux_([], _, _).
lazy_chain_aux_([Z1|Zs], R_2, Z0) :-
   call(R_2, Z0, Z1),
   freeze(Zs, lazy_chain_aux_(Zs,R_2,Z1)).

根据lazy_chain/2我们定义is_bintreeL/2,如下所示:

is_bintreeL(T) :-
   lazy_chain(Zs, #<),
   phrase(in_order(T), Zs).

那么......早期失败&#34;

?- T = node(2, nil, node(1, nil, node(3, nil, node(4, nil, node(5, nil, node(6, nil, node(7, nil, node(8, nil, node(9, nil, node(10, nil, node(11, nil, node(12, nil, node(13, nil, node(14, nil, node(15, nil, node(16, nil, node(17, nil, node(18, nil, node(19, nil, node(20, nil, node(21, nil, node(22, nil, node(23, nil, node(24, nil, node(25, nil, node(26, nil, node(27, nil, node(28, nil, node(29, nil, node(30, nil, node(31, nil, node(32, nil, node(33, nil, node(34, nil, node(35, nil, node(36, nil, node(37, nil, node(38, nil, node(39, nil, node(40, nil, node(41, nil, node(42, nil, node(43, nil, node(44, nil, node(45, nil, node(46, nil, node(47, nil, node(48, nil, node(49, nil, node(50, nil, node(51, nil, node(52, nil, node(53, nil, node(54, nil, node(55, nil, node(56, nil, node(57, nil, node(58, nil, node(59, nil, node(60, nil, node(61, nil, node(62, nil, node(63, nil, node(64, nil, node(65, nil, node(66, nil, node(67, nil, node(68, nil, node(69, nil, node(70, nil, node(71, nil, node(72, nil, node(73, nil, node(74, nil, node(75, nil, node(76, nil, node(77, nil, node(78, nil, node(79, nil, node(80, nil, node(81, nil, node(82, nil, node(83, nil, node(84, nil, node(85, nil, node(86, nil, node(87, nil, node(88, nil, node(89, nil, node(90, nil, node(91, nil, node(92, nil, node(93, nil, node(94, nil, node(95, nil, node(96, nil, node(97, nil, node(98, nil, node(99, nil, node(100, nil, nil)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))),
   time((phrase(in_order(T),Zs),eager_chain(Zs,#<))).
% 210 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 4100201 Lips)
false.

?- T = node(2, nil, node(1, nil, node(3, nil, node(4, nil, node(5, nil, node(6, nil, node(7, nil, node(8, nil, node(9, nil, node(10, nil, node(11, nil, node(12, nil, node(13, nil, node(14, nil, node(15, nil, node(16, nil, node(17, nil, node(18, nil, node(19, nil, node(20, nil, node(21, nil, node(22, nil, node(23, nil, node(24, nil, node(25, nil, node(26, nil, node(27, nil, node(28, nil, node(29, nil, node(30, nil, node(31, nil, node(32, nil, node(33, nil, node(34, nil, node(35, nil, node(36, nil, node(37, nil, node(38, nil, node(39, nil, node(40, nil, node(41, nil, node(42, nil, node(43, nil, node(44, nil, node(45, nil, node(46, nil, node(47, nil, node(48, nil, node(49, nil, node(50, nil, node(51, nil, node(52, nil, node(53, nil, node(54, nil, node(55, nil, node(56, nil, node(57, nil, node(58, nil, node(59, nil, node(60, nil, node(61, nil, node(62, nil, node(63, nil, node(64, nil, node(65, nil, node(66, nil, node(67, nil, node(68, nil, node(69, nil, node(70, nil, node(71, nil, node(72, nil, node(73, nil, node(74, nil, node(75, nil, node(76, nil, node(77, nil, node(78, nil, node(79, nil, node(80, nil, node(81, nil, node(82, nil, node(83, nil, node(84, nil, node(85, nil, node(86, nil, node(87, nil, node(88, nil, node(89, nil, node(90, nil, node(91, nil, node(92, nil, node(93, nil, node(94, nil, node(95, nil, node(96, nil, node(97, nil, node(98, nil, node(99, nil, node(100, nil, nil)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))),
   time((lazy_chain(Zs,#<),phrase(in_order(T),Zs))).
% 52 inferences, 0.000 CPU in 0.000 seconds (98% CPU, 1225664 Lips)
false.

懒惰胜利 - 至少在上述情况下:)

但请注意,将lazy_chain/2一起使用可能会导致难以找到的错误!

要获得更强大的解决方案,请参阅this alternative answer ...

为了完整起见,这里是eager_chain/2的源代码:

eager_chain(Zs, R_2) :-
   (  var(R_2)                  -> instantiation_error(R_2)
   ;  clpfd:chain_relation(R_2) -> eager_chain_aux(Zs, R_2)
   ;  otherwise                 -> domain_error(chain_relation, R_2)
   ).

eager_chain_aux([], _).
eager_chain_aux([Z0|Zs], R_2) :-
   eager_chain_aux_(Zs, R_2, Z0).

eager_chain_aux_([], _, _).
eager_chain_aux_([Z1|Zs], R_2, Z0) :-
   call(R_2, Z0, Z1),
   eager_chain_aux_(Zs, R_2, Z1).

答案 2 :(得分:2)

在这个答案中我们使用 用于声明性整数算术

:- use_module(library(clpfd)).

:- asserta(clpfd:full_answer).

我们像这样定义谓词is_bintree/1bintree_in/2

is_bintree(T) :-
   bintree_in(T, _).

bintree_in(nil, LB-UB) :-            % LB-UB denotes the open interval (LB,UB)
   LB #< UB.                         % that is all integers I suchthat LB<I<UB
bintree_in(node(Mid,L,R), LB-UB) :-
   Mid #> LB,
   Mid #< UB,
   bintree_in(L, LB-Mid),
   bintree_in(R, Mid-UB).

首先,我们测试OP给出的树 1,2

| ?- bintree_in(node(9,node( 3,node(2,nil,nil),node(10,nil,nil)),
                       node(12,node(8,nil,nil),node(15,nil,nil))), _).
no

让我们修复树并再次检查!

| ?- bintree_in(node(9,node( 3,node( 2,nil,nil),node( 8,nil,nil)),
                       node(12,node(10,nil,nil),node(15,nil,nil))), _).
_A in inf..1, _B in 16..sup ? ;      % (somewhat sloppy)
no

OK!接下来是一些极端情况:

| ?- bintree_in(T, 0-0).             % no solution (as expected)
no
| ?- bintree_in(T, 0-1).             % empty tree
T = nil ? ;
no
| ?- bintree_in(T, 0-2).             % singleton tree
T = nil ? ;
T = node(1,nil,nil) ? ;
no

请注意,虽然is_btree/1只能“测试”,但bintree_in/2可以 3 “test”和“generate”!

因此,让我们在一个小域中生成(所有可能的)特定大小的二叉树:

| ?- bintree_in(T, 0-3).             % T has less than 3 elements
T = nil ? ;
T = node(_A,nil,nil), _A in 1..2 ? ;
T = node(1,nil,node(2,nil,nil)) ? ;
T = node(2,node(1,nil,nil),nil) ? ;
no

| ?- bintree_in(T, 0-4).             % T has less than 4 elements
T = nil ? ;
T = node(_A,nil,nil), _A in 1..3 ? ;
T = node(_A,nil,node(_B,nil,nil)), _A#=<_B+ -1, _B#>=_A+1, _B in 2..3, _A in 1..2 ? ;
T = node(1,nil,node(2,nil,node(3,nil,nil))) ? ;
T = node(1,nil,node(3,node(2,nil,nil),nil)) ? ;
T = node(_A,node(_B,nil,nil),nil), _A#>=_B+1, _A in 2..3, _B in 1..2 ? ;
T = node(2,node(1,nil,nil),node(3,nil,nil)) ? ; 
T = node(3,node(1,nil,node(2,nil,nil)),nil) ? ;
T = node(3,node(2,node(1,nil,nil),nil),nil) ? ;
no

最后,我们使用bintree_in/2生成候选解决方案,并使用is_btree/1进行测试!

is_btree/1需要充分的实例化; labeling/2为我们提供了基本条款。

| ?- call_time((  UB in 2..12, 
                  indomain(UB),
                  bintree_in(T, 0-UB),
                  term_variables(T, Zs),
                  labeling([], Zs),
                  \+ is_btree(T)
               ;  true
               ),
               T_ms).
T_ms = 6270 ? ;
no

脚注1: 此答案中的代码会运行(位于
脚注2: 提供的所有输出都是 SICStus Prolog 4.3.2(64位)。
脚注3: 不只是做两件事,而是 (几乎)任意混合生成和测试,因为它可以处理部分实例化的术语。 功能

答案 3 :(得分:2)

在对this previous answer的评论中,@ WillNess建议将“早期失败”添加为一项功能。

in_order_inf_sup//3有效地结合了in_order//1chain/2

:- use_module(library(clpfd)).

in_order_inf_sup(nil, P, P) --> [].
in_order_inf_sup(node(X,L,R), P0, P) -->
   in_order_inf_sup(L, P0, P1),
   [X],
   { P1 #< X },
   in_order_inf_sup(R, X, P).

示例查询(与上一个答案相同):

?- phrase(in_order_inf_sup(node(9,node( 3,node(2,nil,nil),node(10,nil,nil)),
                                  node(12,node(8,nil,nil),node(15,nil,nil))),_,_),
          Zs).
false.

?- phrase(in_order_inf_sup(node(9,node( 3,node( 2,nil,nil),node( 8,nil,nil)),
                                  node(12,node(10,nil,nil),node(15,nil,nil))),_,_),
          Zs).
Zs = [2,3,8,9,10,12,15].

答案 4 :(得分:1)

但它应该失败。该树是无效的BST,您的谓词测试有效的BST。

但这里有一些事要做。现在,您在树上执行两次传递 - 首先在is_btree,第二次使用small / big

这两者可以融合成一个,但一个明显的解决方案将完全符合您的要求,从而在这些无效的BST上取得成功:

is_bst(nil).

is_bst(node(N,L,R)):- 
   (  L = nil 
   ;  L = node(M,LL,LR), M < N, is_bst(L), ....
   ),
   (  R = nil
   ;  ...... 
   ).

要修复它,我们必须从树遍历中返回一个结果 - 这是树的最右边元素 - 并在比较中使用 it 进行验证。

编辑:错过了最左边的元素也需要返回)