这是BST的预定遍历吗?


21

背景

一个二叉树是有根树,其每个节点最多有两个孩子。

标记的二叉树是一个二叉树,其每个节点都用一个正整数标记。此外,所有标签都是不同的

BST(二进制搜索树)是标记的二叉树,其中,每个节点的标签比其左子树的所有节点的标签比在其右子树的所有节点的标签更大和更小。例如,以下是BST:

BST

标记的二叉树的预遍历由以下伪代码定义。

function preorder(node)
    if node is null then
        return
    else
        print(node.label)
        preorder(node.left)
        preorder(node.right)

请参见下图以获得更好的直觉:

BT的预遍历

此二叉树的顶点按以下顺序打印:

F, B, A, D, C, E, G, I, H

你可以阅读更多关于BSTS 这里,和更多的预购遍历这里

挑战

给定一个整数列表一种,您的任务是确定是否有一个BST的预遍历打印的正好是一种

输入项

  • 包含正整数a的非空列表一种
  • 任选地,长度一种

输出量

  • truthy值,如果一种是一些BST的预购遍历。
  • 一个falsey值,否则。

规则

  • 标准规定有效的意见I / O漏洞适用。
  • 这是,因此最短的解决方案(以字节为单位)获胜。像往常一样,不要让可笑的简短的高尔夫语言解决方案阻止您以您选择的语言发布更长的答案。
  • 这不是规则,但是如果包含测试解决方案的链接以及其工作原理的说明,您的答案会更好。

例子

Input                   ---->   Output

[1]                     ---->   True
[1,2,3,4]               ---->   True
[5,1,4,2,3]             ---->   True
[5,4,3,2,1,6,7,8,9]     ---->   True
[4,2,1,3,6,5,7]         ---->   True
[8,3,1,6,4,7,10,14,13]  ---->   True
[2,3,1]                 ---->   False
[6,3,2,4,5,1,8,7,9]     ---->   False
[1,2,3,4,5,7,8,6]       ---->   False
[3,1,4,2]               ---->   False

查看此链接(由Kevin Cruijssen提供),以直观的方式查看示例。



我们可以假设所有整数都是正数吗?
GB

@GB是的。我现在将编辑帖子。
Delfad0r

Answers:


11

JavaScript(Node.js),49字节

a=>!a.some((p,i)=>a.some((q,j)=>q>p&a[j+=j>i]<p))

在线尝试!

一种0一种ñ-1个一种0一世<Ĵ<ñ;一种一世<一种Ĵ-1个一种一世<一种Ĵ

多亏了Arnauld,节省了1个字节。


8

果冻,7个字节

ŒPŒ¿€4ḟ

在线尝试!

返回[4]遍历,否则返回[]

本质上使用tsh的算法:预遍历的“取消资格”条件是3个元素的子序列,看起来像[mid,high,low]。(例如,[20、30、10]。)

我们等价检查的任何亚序列的任何具有索引长度4在它们的排列的列表,这是所有列表排序像并[a 1 ...一个ķ CDB]其中一个被分类和一个 <B <C <d 。(如果我们查看最后三个元素,则每个这样的列表都是不合格的,显然每个不合格列表都是这种形式的。)

ŒP          All subsequences.
  Œ¿€       Permutation index of each.
     4ḟ     Set difference of {4} and this list.

证明

预购遍历不包含失格的子序列

基本情况:遍历(•)是空列表。✓

归纳法:traversal(t)是:t.root ++ traversal(t.left) ++ traversal(t.right)

[a,b,c]为其子序列。我们将证明c <a <b是不可能的。

  • 如果t.root = a,则c <a <b要求c∈t.leftb∈t.right,因此[a,b,c]是错误的顺序。
  • 如果a,b,c∈t.lefta,b,c∈t.right,则使用归纳假设。
  • 如果a∈t.leftc∈t.right,c> a

不带不合格子序列的不重复整数列表是BST的预遍历

如果列表为空,则是平凡的BST•的遍历。

如果列表是头,然后是

  • less为小于head的元素tail的最长前缀,使more为列表的其余部分。
  • 然后more [1]> head,所有其他more [i]也都大于head(否则[head,more [1],more [i]]将成为取消资格的子序列)。
  • 递归:转成BSTS。
  • 现在我们的清单是遍历

                     head
                    /    \
             BST(less)   BST(more),
    

    这棵树是有效的BST。


1
很好的证明。实际上,我发布答案时并没有证明公式。我只是觉得经过一些尝试从输入构造BST是正确的。
tsh

5

Java 10,94个字节

a->{var r=0>1;for(int j=a.length-1,i;j-->0;)for(i=0;i<j;)r|=a[j]>a[i]&a[j+1]<a[i++];return!r;}

@tsh的JavaScript回答的端口。

在线尝试。

说明:

a->{                      // Method with integer-array parameter and boolean return-type
  var r=0>1;              //  Result-boolean, starting at false
  for(int j=a.length-1,i;j-->0;)
                          //  Loop `j` in the range (length-1, 0]:
    for(i=0;i<j;)         //   Inner loop `i` in the range [0, j):
      r|=                 //    If any are true, change the result to true as well:
         a[j]>a[i]        //     The `j`'th item is larger than the `i`'th item
         &a[j+1]<a[i++];  //     And the `j+1`'th item is smaller than the `i`'th item
  return!r;}              //  After the nested loop, check if the boolean is still false

1
可以使用来重新分配Java布尔值|=。我认为&=也可以吗?
J.Sallé18年

@J.Sallé是的,无论是|=&=工作的快捷方式b = b | conditionb = b & condition(其中&|是快捷方式&&,并||在过程中的大多数情况下)。
凯文·克鲁伊森

5

红宝石46 40 38字节

f=->r{a,*b=r;!a||b==b&[*0..a]|b&&f[b]}

在线尝试!

通过递归地将第一个元素a作为枢轴,并检查是否可以将数组的其余部分拆分为两个元素(使用交集和并集:首先删除所有元素> a,然后再次在右侧添加它们并检查是否包含更改)。


3

视网膜0.8.2,31字节

\d+
$*
M`\b((1+)1+,).*\1\2\b
^0

在线尝试!链接包括测试用例。使用@tsh的算法。说明:

\d+
$*

转换为一元。

M`\b((1+)1+,).*\1\2\b

查找位于两个后续连续降序数字之间的数字。

^0

检查匹配数是否为零。


3

Perl 6、38个字节

!*.combinations(3).grep:{[>] .[1,0,2]}

在线尝试!

说明

 *.combinations(3)  # All combinations of 3 elements a,b,c
!                 .grep:{            }  # Return false if check succeeds for any combination
                         [>] .[1,0,2]   # Check whether b>a>c, that is b>a and c<a


3

Scala(68 67字节)

def%(i:Seq[Int])= !i.combinations(3).exists(c=>c(0)<c(1)&c(0)>c(2))

在线尝试

@nwellnhof的答案的端口。

Scala(122103字节)

def f(i:Seq[Int]):Boolean=if(i.size<1)1>0 else{val(s,t)=i.tail.span(_<i(0));t.forall(_>i(0))&f(s)&f(t)}

感谢@Laikoni提出的缩短两个解决方案的建议。

在线尝试

说明:

  1. 切片(使用Scala的 span使用数组的头部作为切片条件对数组进行)。
  2. 确认数组的第一个切片小于头,第二个切片大于头。
  3. 递归检查每个切片是否也满足(2)

1
我认为您不需要空间val (s,t)true可以1>0并且您可以放下,s.forall(_<i(0))&因为这应该已经被保险了span
Laikoni '18 -10-20

1
您可以调用该函数%并放空格:def%(i:Seq[Int])=
Laikoni '18

您的解决方案包含与其他函数不同的函数声明。纯表达式相当短。;)
Y Wit博士

我试图移植tsh的答案,但没有设法使其简短。版本1 l.zipWithIndex.foldLeft(1>0){case(r,v,i)=>r&l.zip(l.tail).slice(i+1,l.length).forall(x=>l(i)>x._1|l(i)<x._2)}。版本2 (for(i<-l.indices)yield l.zip(l.tail).slice(i+1,l.length).forall(x =>l(i)>x._1|l(i)<x._2)).forall(x=>x)。有什么想法可以缩短这些时间吗?
Y Wit博士

用简单的英语算法:对每个元素进行检查,使所有成对的元素彼此相邻。
Y Wit博士,

2

05AB1E15 10 字节

ŒεD{3.IÊ}P

@Lynn的Jelly答案端口。
-5个字节,感谢@Emigna

在线尝试验证所有测试用例

说明:

Œ             # Take all sublists of the (implicit) input-list
              #  i.e. [2,3,1] → [[2],[2,3],[2,3,1],[3],[3,1],[1]]
              #  i.e. [1,2,3,4]
              #   → [[1],[1,2],[1,2,3],[1,2,3,4],[2],[2,3],[2,3,4],[3],[3,4],[4]]
 ε      }     # Map each to:
  D           #  Duplicate the current sublist on the stack
   {          #  Sort the copy
              #   i.e. [2,3,1] → [1,2,3]
              #   i.e. [2,3,4] → [2,3,4]
    3.I       #  Get the 4th (3rd 0-indexed) permutation of this list
              #   i.e. [1,2,3] → [2,3,1]
              #   i.e. [2,3,4] → [3,4,2]
       Ê      #  Check that the lists are NOT equal
              #   i.e. [2,3,1] and [2,3,1] → 0 (falsey)
              #   i.e. [2,3,4] and [3,4,2] → 1 (truthy)
         P    # Check whether all are truthy (and output implicitly)
              #  i.e. [1,1,0,1,1,1] → 0 (falsey)
              #  i.e. [1,1,1,1,1,1,1,1,1,1] → 1 (truthy)

1
怎么ŒεD{3.IÊ}P
Emigna '18 -10-19

1
@Emigna是的,那确实要容易得多...>。>谢谢!:)(周末愉快。)
凯文·克鲁伊森

2

Haskell,41个字节

f(h:t)=t==[]||all(>h)(snd$span(<h)t)&&f t

在线尝试!

使用Lynn的观察结果,足以检查出mid..high..low没有子序列。这意味着对于每个元素h,其后的元素列表t是一个元素块,<h后跟一个元素>h块(两个块都可以为空)。因此,代码会在删除元素的前缀后进行检查<h放入中之后t,其余元素均为all >h。递归检查每个初始元素,h直到列表的长度为1。

可能的简化是检查子模式就足够了 后两个连续的模式中,高,低。不幸的是,Haskell的提取最后两个元素的方法并不短,就像使用pattern match从前面完成一样a:b:c。我找到了一个较短的解决方案,用于检查mid,high..low,但这无法拒绝输入[3,1,4,2]

来自Laikoni的格式化测试用例


1

杰普特,14个字节

d@sY ð_§XÃxÈ-Y

Japt口译员

产出 falseBST的,true无BST。

说明:

d@                Run on each item X, return true if any aren't 0: 
  sY                  Ignore the numbers before this index
     ð_§XÃ            Get the indexes of numbers less than or equal to X
                          If it is a BST, this list will be e.g. [0,1,2...]
            -Y        Subtract the position within the index list from each index
                          eg. [0,1,2] -> [0,0,0] , [0,1,4] -> [0,0,2]
          xÈ          Sum the resulting array

1

斯卡拉

所有方法都是tsh所示规则的实现。

109

l.zipWithIndex.foldLeft(1>0){case(r,(v,i))=>r&l.zip(l.tail).slice(i+1,l.size).forall(x=>l(i)>x._1|l(i)<x._2)}

101

(for(i<-l.indices)yield l.zip(l.tail).slice(i+1,l.size).forall(x =>l(i)>x._1|l(i)<x._2)).forall(x=>x)

98

l.indices.foldLeft(1>0)((r,i)=>r&(l.zip(l.tail).slice(i+1,l.size).forall(x=>l(i)>x._1|l(i)<x._2)))

78

(for(i<-l.indices;j<-i+1 to l.size-2)yield l(i)>l(j)|l(i)<l(j+1)).forall(x=>x)

如果它必须是一个函数,而不仅仅是一个表达式,那么每行必须以(17个字节)开头

def%(l:Seq[Int])=

0

Oracle SQL,177字节

with r(i,v)as(select rownum,value(t)from table(a)t)
select nvl(min(case when r.v<p.l and r.v>p.v then 0end),1)from r,(select i,lag(v)over(order by i)l,v from r)p where r.i+1<p.i

由于Oracle SQL中没有布尔类型,因此查询返回1或0。

Oracle SQL 12c,210字节

with function f(n ku$_objnumset,i int)return int as begin return n(i);end;
select min(case when f(c,1)>f(c,2)or f(c,1)<f(c,3)then 1else 0end)from(select value(t)c from table(powermultiset_by_cardinality(a,3))t)

在SQL中无法像在PL / SQL中那样访问数组中的元素-即a(i),因此fwith clause为此目的。否则,解决方案将短得多。

其他限制

  • 少于3个元素的数组引发异常(而不是返回1)
  • 假设powermultiset_by_cardinality保留顺序,即使文档中未明确说明也是如此

sqlplus清单

SQL> set heading off
SQL> with r(i,v)as(select rownum,value(t)from table(ku$_objnumset(6,3,2,4,5,1,8,7,9))t)
  2  select nvl(min(case when r.v<p.l and r.v>p.v then 0end),1)from r,
  3  (select i,lag(v)over(order by i)l,v from r)p where r.i+1<p.i
  4  /

                                            0

SQL> with function f(n ku$_objnumset,i int)return int as begin return n(i);end;
  2  select min(case when f(c,1)>f(c,2)or f(c,1)<f(c,3)then 1else 0end)
  3  from(select value(t)c from table(powermultiset_by_cardinality(ku$_objnumset(6,3,2,4,5,1,8,7,9),3))t)
  4  /

                                                     0

SQL> with r(i,v)as(select rownum,value(t)from table(ku$_objnumset(8,3,1,6,4,7,10,14,13))t)
  2  select nvl(min(case when r.v<p.l and r.v>p.v then 0end),1)from r,
  3  (select i,lag(v)over(order by i)l,v from r)p where r.i+1<p.i
  4  /

                                            1

SQL> with function f(n ku$_objnumset,i int)return int as begin return n(i);end;
  2  select min(case when f(c,1)>f(c,2)or f(c,1)<f(c,3)then 1else 0end)
  3  from(select value(t)c from table(powermultiset_by_cardinality(ku$_objnumset(8,3,1,6,4,7,10,14,13),3))t)
  4  /

                                                     1

在线验证apex.oracle.com

更新资料

Oracle SQL,155个字节

with r(i,v)as(select rownum,value(t)from table(a)t)select nvl(min(case when a.v<b.v and a.v>c.v then 0end),1)r from r a,r b,r c where a.i<b.i and b.i+1=c.i

0

C,823字节(不计算空格字符);923字节(包括空格)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct tree
{struct tree * left;struct tree * right;int val;}tree;static int * test_p = 0;
void insert_root(tree ** root, int in)
{if (*root == NULL){*root = (tree *)calloc(1,sizeof(tree));(*root)->val = in;return;}else if (in < (*root)->val){insert_root(&((*root)->left),in);}else{insert_root(&((*root)->right),in);}}
void preorder(tree * root)
{if ( root == 0x0 ) { return; }*test_p++ = root->val;preorder(root->left);preorder(root->right);}
int main(int argc, char ** argv)
{int test_list[argc-1];memset(test_list,0,argc*sizeof(int));test_p = test_list;tree * root = (tree *)calloc(1,sizeof(tree));root->val = strtol(argv[1],0x0,10);int i = 1;while ( argv[++i] != 0x0 ){insert_root(&root,strtol(argv[i],0x0,10));}preorder(root);test_p = test_list;i = 1;while ( ( i < argc ) ){if ( *test_p != strtol(argv[i],0x0,10) ){return 0;}test_p++;i++;}return 1;}

该程序的可读版本如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct tree
{
    struct tree * left;

    struct tree * right;

    int val;

} tree;


static int * test_p = 0;

void insert_root(tree ** root, int in)
{
  if (*root == NULL)
  {
    *root = (tree *)calloc(1,sizeof(tree));

    (*root)->val = in;

    return;
  }

  else if (in < (*root)->val)
  {
    insert_root(&((*root)->left),in);
  }

  else
  {
    insert_root(&((*root)->right),in);
  }
}

void preorder(tree * root)
{
    if ( root == 0x0 ) {  return; }

        *test_p++ = root->val;

        preorder(root->left);

        preorder(root->right);

}

int main(int argc, char ** argv)
{
    int test_list[argc-1];

    memset(test_list,0,argc*sizeof(int));

    test_p = test_list;

    tree * root = (tree *)calloc(1,sizeof(tree));

    root->val = strtol(argv[1],0x0,10);

    int i = 1;

    while ( argv[++i] != 0x0 )
    {
        insert_root(&root,strtol(argv[i],0x0,10));
    }

    preorder(root);

    test_p = test_list;

    i = 1;

    while ( ( i < argc ) )
    {
        if ( *test_p != strtol(argv[i],0x0,10) )
        {
            return 0;
        }

        test_p++;

        i++;
    }

    return 1;   
}

该程序中的主要方法读取(据说)合法的预遍历数字列表。

调用的函数insert_root将整数插入二进制搜索树,其中前一个节点包含较小的值,而下一个节点包含较大的int值。

函数preorder(root)以预遍历遍历树,并同时将算法遇到的每个整数连接到int数组test_list

最后的while循环测试stdin列表中的每个int值和test_list中的每个int值在每个索引是否相等。如果有来自stdin的列表元素中未能与test_list中相应索引处的相应元素匹配,则main函数将返回0。否则main方法将返回1

要确定返回的主输入,请键入echo $ status在bash终端中。BASH将打印出1或0。


2
您的分数是计数空格。
小麦巫师
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.