我一直在努力解决增加子序列问题。我想出的算法目前仅解决了排序数组。我在python 3.5中编写代码。此问题托管在Leetcode上。
在增加子序列问题时,我得到一个整数数组,任务是找到给定数组的所有不同可能的增加子序列,并且增加的子序列的长度应该至少为2.
示例:
输入 - [4,6,7,7]
输出 - [[4,6],[4,7],[4,6,7],[4,6,7,7],[6,7],[6,7,7], [7,7],[4,7,7-]]
以下是我解决此问题的工作代码:
array = [4,6,7,7]
def incrSubseq(array): #Only works for sorted arrays.
res = [[]]
ans = []
for num in array:
res += [item + [num] for item in res if (item+[num] not in res)]
for values in res:
if len(values)>1:
ans = ans + [values]
print(ans)
incrSubseq(array)
此代码如何运作?
res
(列表列表)
由空列表初始化。array
,它按排序顺序排列,将每个元素添加到列表中,从而找到可以形成的所有子集。列表推导中的if
语句会过滤掉重复的项目,从而只保留列表的一个副本。因此,解决问题。
现在,我在这里缺少的是一种解决未排序数组的方法。据我所知,我需要检查一下,当我尝试向res
添加元素时,它应该大于或等于它之前的项目。
res += [item + [num] for item in res if (item+[num] not in res) and (item <= num)]
在空列表中给出。
有关改进代码的任何建议吗?
答案 0 :(得分:1)
你的想法是完全正确的!只需检查item
中的最后一个元素是否小于之前的元素(可能允许相等,具体取决于定义的增加方式)。
因此,您添加了检查item[-1] <= num
(使用-1
,您将获得Python中数组的最后一个元素。)
现在还有一个问题。 item
可能为空,您会收到错误消息。因此,如果<=
中至少有一个元素,您只需要item
检查。
下面是一个使用布尔运算短路的奇特解决方案,其中(len(item) == 0 or item[-1] <= num)
在没有元素的情况下为真(然后第二次检查不执行),或者至少有一个元素在item
并检查它是否更小或相等。
array = [4,6,3,7]
def incrSubseq(array): # Works for sorted arrays too :)
res = [[]]
for num in array:
res += [item + [num] for item in res if (item + [num] not in res
and (len(item) == 0 or item[-1] <= num))]
return [values for values in res if len(values) > 1]
print(incrSubseq(array))
Short circuiting 表示只评估布尔表达式,直到可以确定其最终值。例如,False and 1/0
不会引发异常,因为and
为False
,如果其两个参数的任何为False
。因此,当评估从左到右时,它不会计算1/0
。
上述算法的内部部分可以写成:
for num in array:
for item in res:
if item + [num] not in res:
if len(item) == 0:
# item is empty.
res += [num]
else:
# item is not empty so we check its last element.
if item[-1] <= num:
res += item + [num]
else:
# We got something increasing here. Do not add.
pass
此算法的复杂度可按如下方式计算。假设[1, 2, ..., n]
的最坏情况输入。然后在每个步骤中,结果子序列的数量加倍,得到O(2^n)
个子序列,输出大小为O(n * 2^n)
。每个算法至少需要那么长的时间(如果你真的对输出每个序列感兴趣 - 如果你想生成动态的所有东西,那么函数式语言的迭代器和懒惰的评估风格并不重要。)
这种算法需要更长的时间。我们必须为输出中的每个子序列做的主要工作是检查它是否与天真的item + [num] not in res
重复。两个长度为m的列表的比较需要O(m)
最坏的情况。并且考虑到最后num
占用总运行时间的一半以上,我们在此处仅将其用作良好的近似值。这意味着检查最后2^(n-1)
个创建的子序列需要O(2^(n-1) * 2^(n-1) * n) = O(n * 4^n)
,因为您检查每个旧序列的每个新序列。使用prefix tree作为结果数据结构时,可以将其简化为O(2^n)
,因为检查新子序列是否有效只需要O(1)
。遍历树以实际记下所有解决方案需要再次O(2^n * n)
。
旁注:如果您喜欢python中的函数式编程,请查看syntax_sugar_python,