【问题标题】:How to improve Increasing Subsequences algorithm to incorporate un-sorted array?如何改进增加子序列算法以合并未排序的数组?
【发布时间】:2017-02-04 13:27:48
【问题描述】:

我一直致力于解决递增子序列问题。我提出的算法目前仅解决排序数组。我正在用 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)

这段代码是如何工作的?

  1. 首先初始化一个结果变量res(列表列表) 它由一个空列表初始化。
  2. 然后我遍历给定的整数数组array,它按排序顺序,将每个元素添加到列表中,从而找到可以形成的所有子集。列表推导式中的 if 语句过滤掉重复项,因此只保留列表的一份副本。
  3. 然后过滤所有长度大于1的数组。

这样,问题就解决了。

现在,我在这里缺少的是一种解决未排序数组的方法。据我了解,我需要以一种方式进行检查,即当我尝试将元素添加到 res 时,它应该大于或等于它之前的项目。

res += [item + [num] for item in res if (item+[num] not in res) and (item <= num)] 给出空列表。

对改进代码有什么建议吗?

【问题讨论】:

    标签: arrays algorithm python-3.x sorting


    【解决方案1】:

    你的想法完全正确!只需检查item 中的最后一个元素是否小于前一个元素(可能允许相等,具体取决于如何定义增加)。

    因此,您添加检查 item[-1] <= num(使用 -1,您将获得 Python 中数组的最后一个元素)。

    现在还有一个问题。 item 可能为空,您会收到错误消息。所以你只想要<= 检查,如果item 中至少有一个元素。 下面是一个使用布尔运算短路的奇特解决方案,其中(len(item) == 0 or item[-1] <= num) 在没有元素时为真(然后第二次检查执行),或者@987654331 中至少有一个元素@ 并检查它是否更小或相等。

    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 不会引发异常,因为andFalse,如果其两个参数中的任何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

    【讨论】:

    • 你能解释一下len(item) == 0是如何改变一切的吗?我没有完全明白短路。
    • 我的代码也没有优化。 array = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] 会花费很多时间。由于我正在生成所有子集,因此它将是指数复杂度代码。 O(n^n) 我想。
    • 感谢您的帮助! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 2014-03-13
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多