它是线性树吗?(第一版)


11

背景

未标记的树可能如下所示:

   o
 / | \
o  o  o
|    / \
o   o   o

为了线性化这棵树,我们首先用每个o子节点的数量标记每个节点:

   3
 / | \
1  0  2
|    / \
0   0   0

然后将数字以呼吸优先的方式写在列表中,即逐行从左到右:

[3, 1, 0, 2, 0, 0, 0]

这是上面树的唯一且明确的表示,这意味着没有两个不同的纯树将具有相同的线性化,并且我们可以从列表中重建原始树。

尽管每棵树都对应于一个特定的整数列表,但并非每个整数列表都代表一个有效的线性化树:例如,[2, 0, 0, 0]它并不代表一个有效的树,如果我们尝试对其进行去线性化,则最终得到该树

[2,0,0,0] -> 2 [0,0,0] -> 2 [0,0] -> 2 [0]
            / \          / \        / \
                        0          0   0

但仍然0在列表中左侧,无处可放。类似地,[2, 0]这也不是有效的树线性化,因为去线性化的树有一个空的子点:

  2
 / \
0

任务

给定一个整数列表,请确定它是否是使用尽可能少的字节进行的树的有效线性化。您可以编写完整的程序或函数。

输入: 非负整数的非空列表。

输出: 如果列表是树的线性化,则为真实值,否则为伪造值。

测试用例

特鲁西
[0]
[2, 0, 0]
[1, 1, 1, 1, 1, 0]
[3, 1, 0, 2, 0, 0, 0]
[2, 0, 2, 2, 0, 0, 2, 0, 0]
[3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0]
[1, 5, 3, 0, 2, 1, 4, 0, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0]
虚假
[0, 1]
[2, 0]
[2, 0, 0, 0]
[1, 0, 1]
[3, 2, 1, 0]
[2, 0, 0, 2, 0, 0]
[4, 1, 0, 3, 0, 0, 0, 0]
[4, 2, 0, 3, 1, 0, 0, 0, 0, 0]

Answers:


4

Haskell,44个字节

f[n:k]=iterate f[k]!!n
f _=[]
g x=f[x]==[[]]

定义一个g接受列表并返回布尔值的函数。 看到它通过所有测试用例

说明

这依赖于这样的事实,即深度优先和宽度优先的线性化产生相同的阵列。有关详细信息,请参见Martin的答案。基本上,它们在数组上都给出相同的算术条件。

f给该函数的输入列表包含在单例列表中。它从n列表中弹出一个数字,然后n在其余列表中调用其自身时间以处理弹出节点的子级(深度优先)。弹出空白列表会导致[],我将其用作错误状态。该函数g检查最终结果是[[]],没有未处理节点的唯一非错误状态。如果Haskell的类型很弱,我可以使用0或某种东西作为错误状态,而不必将输入包装到另一个列表中。


3

Mathematica,38个字节

Last@#<0<=Min@Most@#&@Accumulate[#-1]&

基本思想是跟踪多个要填充的节点。列表中的每个元素都用完一个节点,并添加与其具有的子节点一样多的节点。因此,每个元素i将总数更改为i-1。此计数加一,因为应该从1(根)开始而不是0

为了使树有效,我们a)永远都不能0在列表的下方,因为我们将无处放置当前节点,并且b)需要在最后-1结束,否则我们剩下未使用的节点。

我们使用Accumulate[#-1]得出剩余节点的运行总数(计算输入列表的前缀总和减一)。然后我们检查最后一个元素,只有最后一个元素-1与:

Last@#<0<=Min@Most@#

请注意,检查最后一个元素是否为负就足够了,因为我们决不能将其减量大于1,因此,如果最后一个值等于-2或小于该值,则其他最小值不能为非负数是不可能的。


2

视网膜,29字节

\d+
$*
^(?<-1>(1)*,)*$(?(1)!)

在线尝试!(第一行启用换行分隔的测试套件。)

说明

基本思想与我的Mathematica回答相同:我们跟踪剩余节点的运行总数,确保其永远不会低于零,而以零结尾。但是,使用正则表达式实现此方法的方式非常不同。

\d+
$*

这只是将输入转换为一元,将每个整数n变为n1s。

^(?<-1>(1)*,)*$(?(1)!)

这就是真正的魔术发生的地方。这是一个很短的正则表达式,只匹配有效的树,但是机制很微妙。

我正在使用平衡组来跟踪节点数量,这是使用正则表达式内部堆栈的一种方式。

首先,当然,这样的堆栈永远不会有负的深度,因此我们不能-1像在Mathematica解决方案中那样,最终以结尾表示。但是,我们可以注意到,在输入的最后一个元素具有为零上的有效栈(否则我们无法结束-1)。事实证明,它实际上节省了字节,以检查使我们最终零和零个剩余的节点。

所以这是正则表达式的分解:

^        e# Anchor the match to the beginning of the string.
(?<-1>   e# Each repetition of this group will match one number. 
         e# We can ignore the <-1> for now.
  (1)*   e#   Match each unary digit of the current number, pushing
         e#   a capture onto stack 1. This increments our total of
         e#   remaining nodes by 1 for each child.
  ,      e#   Match a comma. Note that this requires that there is at
         e#   least one more number in the list.
)*       e# At the end of the repetition the <-1> pops one capture from
         e# the stack. This is the node that the current number itself
         e# takes up.
$        e# Match the end of the string. This requires the input to end
         e# in a zero, because the last thing we matched was a comma.
(?(1)!)  e# Make sure that stack 1 is empty, so that we don't have any
         e# unused nodes.

1

CJam(20字节)

{X0@{+\(_0>{\}*}/|!}

在线测试套件。这是一个匿名块,它在堆栈上放置一个数组,并在堆栈上保留0或1。

解剖

用伪代码是:

p = 1
q = 0
foreach (i in input):
  q += i
  if (--p <= 0):      # in practice, if (--p == 0):
      p, q = q, p
return (p | q) == 0   # i.e. p == 0 && q == 0

q在树中累积当前级别的节点标签的总和;p倒数当前级别中剩余的节点。


{X0@{+\(_{\}&}/|!}我认为?
马丁·恩德

同样,您似乎应该能够通过使用完整程序来节省一个字节,以免出现@
马丁·恩德

1

迷宫,17字节

(
+?
;-)
,_"
@@,!

在线尝试!

Truthy输出-1和falsy输出是空的。在迷宫中定义真实和虚假有点棘手,因为迷宫的分支主要是三元的。但是,构造具有可靠两个分支的条件的唯一方法是,只能执行以下操作:

>"F
 T

在这种情况下,我会考虑直线前进(因为运动方向不受影响)并转为真实。这些分别对应于零和非零。我使用空输出表示零的原因是,如果您将输出通过管道传输到另一个Labyrinth程序中,则输入运算符?实际上会在输入为空时将零推入,因此我认为空字符串是有效的零表示。

说明

该算法仍然与我在Mathematica和Retina答案中的算法相同,但是由于Labyrinth的控制流程,这次的工作方式略有不同:

  • 在这里,我们不与总计数器一一对应。相反,我们a)使用负计数器,然后b)将其初始化为-11初始值,以便我们希望计数器在整个列表中都为负,并在最后一个输入处为零。这实际上简化了这里的控制流程。
  • 有三种可能的终止条件,而不是构建完整列表并检查它是否包含错误的值:

    1. 在达到总数零之前,我们达到了EOF。在这种情况下,还剩下未使用的节点,我们什么也不打印。
    2. 我们达到零我们处于EOF。在这种情况下,我们有一个有效的树。
    3. 我们达到零,还没有达到EOF。在这种情况下,我们在覆盖所有元素之前就用光了节点,并且不打印任何内容。

至于实际的代码,我们从左上角开始。将(堆栈顶部的隐式零变成a -1,它将是运行总数。然后,我们进入程序的非常紧密的主循环+?-)"_,;+

+   Add the top two values. This does nothing on the first iteration,
    but gets rid of a helper-zero on subsequent iterations.
?   Read and push integer.
-   Subtract it from running total.
)   Increment.
"   No-op. There is a branch at this point. If the running total is zero,
    we move straight ahead onto the , (see below). Otherwise, the loop continues.
_   Push a zero. This is necessary to prevent the IP from turning south.
,   Read a character. This will either be the next separator (some positive
    number) or EOF (-1). If it's EOF, the IP turns south and the program
    terminates. Otherwise, the loop continues.
;   Discard the separator.

这只剩下在某些时候我们将运行总计降低到零的情况。IP移到右下角,,并读取另一个字符以检查是否已到达EOF。如果不是,则该值将为正,并且IP向西转向@,程序终止。如果我们这样做达到EOF时,IP转动东部和打印-1!。然后,IP将@通过一条怪异的路径向左下方蠕动,以终止程序。


0

Python,82个字节

lambda l:len(l)==sum(l)+1 and not any(list(l[x]>=len(l)-x for x in range(len(l))))

需要更多的测试用例。


list如果至少是Python 2,则不需要强制转换,通过重新排列并反转第二个条件,您可以将其转换为70个字节:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade 2016年

^与此相关,您可以将的正文更改为保存另外2个字节,all以将x<len(l)-y for y,x in enumerate(l)其保存为
68。– Kade

我现在不打算再打这个了,因为我认为这不是一个准确的解决方案。感谢您的提示。
Sparr

0

Pyth,13个字节

qxsM._tMQ_1tl

我们首先计算输入表示中所有点上树的当前填充度。这部分想法主要是从马丁·恩德(Martin Ender)借来的,所以多亏了他。sM._tMQ

有了此列表后,我们将检查包含-1x..._1)的第一个索引是否为输入的长度减去一个(q...tl(Q))。

不相信它有用吗?自己尝试!

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.