【问题标题】:Check for presence of a sliced list in Python检查 Python 中是否存在切片列表
【发布时间】:2011-03-19 19:41:57
【问题描述】:

我想编写一个函数来确定一个子列表是否存在于一个更大的列表中。

list1 = [1,0,1,1,1,0,0]
list2 = [1,0,1,0,1,0,1]

#Should return true
sublistExists(list1, [1,1,1])

#Should return false
sublistExists(list2, [1,1,1])

有没有 Python 函数可以做到这一点?

【问题讨论】:

标签: python list


【解决方案1】:

如果您确定您的输入将只包含单个数字 0 和 1,那么您可以转换为字符串:

def sublistExists(list1, list2):
    return ''.join(map(str, list2)) in ''.join(map(str, list1))

这会创建两个字符串,因此它不是最有效的解决方案,但由于它利用了 Python 中优化的字符串搜索算法,因此对于大多数用途来说可能已经足够了。

如果效率非常重要,您可以查看Boyer-Moore 字符串搜索算法,该算法适用于列表。

幼稚的搜索有 O(n*m) 最坏的情况,但如果您不能使用转换为字符串的技巧并且您不需要担心性能,那么它可能是合适的。

【讨论】:

  • -- : 代码严重损坏,试试sublistExists([10], [1,0]) == True?!
  • @Nas Banov:这就是为什么 Mark 在他的第一句话中写道“如果您确定您的输入将只包含单个字符 '0' 和 '1'...”
  • @Tim:但输入不包含“单个字符 '0' 和 '1'”,请注意!所示示例仅包含数字 0 和 1(或“数字”,如果您愿意)。 :) 此外,他的代码稍微宽泛一些——它将处理正确的任何 1 字符列表或任何 1 位数字列表(但不能同时处理两者)。通过将分隔符引入str.join 很容易解决
  • 我同意你关于 Boyer-Moore 的看法。我已经发布了一个实现的答案。
【解决方案2】:

没有我知道的函数

def sublistExists(list, sublist):
    for i in range(len(list)-len(sublist)+1):
        if sublist == list[i:i+len(sublist)]:
            return True #return position (i) if you wish
    return False #or -1

正如 Mark 所说,这不是最有效的搜索(它是 O(n*m))。这个问题的处理方式与字符串搜索大致相同。

【讨论】:

  • 你应该避免使用关键字list作为变量名。
【解决方案3】:

让我们来点实用的,好吗? :)

def contains_sublist(lst, sublst):
    n = len(sublst)
    return any((sublst == lst[i:i+n]) for i in xrange(len(lst)-n+1))

请注意,any() 将在 lst 中 sublst 的第一次匹配时停止 - 如果在 O(m*n) 操作之后没有匹配则失败

【讨论】:

    【解决方案4】:

    这是一种适用于简单列表的方法,它比 Mark 的脆弱性略小

    def sublistExists(haystack, needle):
        def munge(s):
            return ", "+format(str(s)[1:-1])+","
        return munge(needle) in munge(haystack)
    

    【讨论】:

    • @e1i45,你试过了吗?当 s 中的项目不是字符串时会发生什么?
    • DELIMITER.join(str(x) for x in xs) 可能有效。但也许它比格式慢?
    【解决方案5】:

    如果我理解正确,你有一个更大的列表,比如:

    list_A= ['john', 'jeff', 'dave', 'shane', 'tim']
    

    然后还有其他列表

    list_B= ['sean', 'bill', 'james']
    
    list_C= ['cole', 'wayne', 'jake', 'moose']
    

    然后我将列表 B 和 C 附加到列表 A

    list_A.append(list_B)
    
    list_A.append(list_C)
    

    所以当我打印 list_A 时

    print (list_A)
    

    我得到以下输出

    ['john', 'jeff', 'dave', 'shane', 'tim', ['sean', 'bill', 'james'], ['cole', 'wayne', 'jake', 'moose']]
    

    现在我想检查子列表是否存在:

    for value in list_A:
        value= type(value)
        value= str(value).strip('<>').split()[1]
        if (value == "'list'"):
            print "True"
        else:
            print "False"
    

    如果您在较大列表中有任何子列表,这将为您提供“真”。

    【讨论】:

      【解决方案6】:

      只需从两个列表创建集合并使用 issubset 函数:

      def sublistExists(big_list, small_list):
          return set(small_list).issubset(set(big_list))
      

      【讨论】:

      • 不,触发误报In [65]: sublistExists([1,2,2,3,2,5,6], [3,3,2]) Out[65]: True
      【解决方案7】:
      def sublistExists(x, y):
        occ = [i for i, a in enumerate(x) if a == y[0]]
        for b in occ:
            if x[b:b+len(y)] == y:
                 print 'YES-- SUBLIST at : ', b
                 return True
            if len(occ)-1 ==  occ.index(b):
                 print 'NO SUBLIST'
                 return False
      
      list1 = [1,0,1,1,1,0,0]
      list2 = [1,0,1,0,1,0,1]
      
      #should return True
      sublistExists(list1, [1,1,1])
      
      #Should return False
      sublistExists(list2, [1,1,1])
      

      【讨论】:

        【解决方案8】:

        不妨加入@NasBanov 解决方案的递归版本

        def foo(sub, lst):
            '''Checks if sub is in lst.
        
            Expects both arguments to be lists
            '''
            if len(lst) < len(sub):
                return False
            return sub == lst[:len(sub)] or foo(sub, lst[1:])
        

        【讨论】:

        • 递归...可能导致长列表的堆栈溢出
        • @TigranSaluev - 堆栈溢出或最大递归深度或 RecursionError?
        • RuntimeError: cmp 中超出最大递归深度
        • “也可以”——嗯,为什么呢?与迭代版本相比,这种递归方法似乎没有可取之处。它似乎更长,效率更低,更容易出错,更难理解。 (我一般不反对递归。)
        • @wwii:好吧 :) 我想知道您是否有特定的理由递归地执行此操作,但似乎只是因为它可以完成。特别是考虑到递归深度问题,这似乎是一个糟糕的解决方案。
        【解决方案9】:

        按照 Mark Byers 的建议,执行此操作的有效方法是使用 Boyer-Moore algorithm。我已经在这里完成了:Boyer-Moore search of a list for a sub-list in Python,但将在此处粘贴代码。它基于维基百科的文章。

        search() 函数返回正在搜索的子列表的索引,如果失败则返回 -1。

        def search(haystack, needle):
            """
            Search list `haystack` for sublist `needle`.
            """
            if len(needle) == 0:
                return 0
            char_table = make_char_table(needle)
            offset_table = make_offset_table(needle)
            i = len(needle) - 1
            while i < len(haystack):
                j = len(needle) - 1
                while needle[j] == haystack[i]:
                    if j == 0:
                        return i
                    i -= 1
                    j -= 1
                i += max(offset_table[len(needle) - 1 - j], char_table.get(haystack[i]));
            return -1
        
        
        def make_char_table(needle):
            """
            Makes the jump table based on the mismatched character information.
            """
            table = {}
            for i in range(len(needle) - 1):
                table[needle[i]] = len(needle) - 1 - i
            return table
        
        def make_offset_table(needle):
            """
            Makes the jump table based on the scan offset in which mismatch occurs.
            """
            table = []
            last_prefix_position = len(needle)
            for i in reversed(range(len(needle))):
                if is_prefix(needle, i + 1):
                    last_prefix_position = i + 1
                table.append(last_prefix_position - i + len(needle) - 1)
            for i in range(len(needle) - 1):
                slen = suffix_length(needle, i)
                table[slen] = len(needle) - 1 - i + slen
            return table
        
        def is_prefix(needle, p):
            """
            Is needle[p:end] a prefix of needle?
            """
            j = 0
            for i in range(p, len(needle)):
                if needle[i] != needle[j]:
                    return 0
                j += 1    
            return 1
        
        def suffix_length(needle, p):
            """
            Returns the maximum length of the substring ending at p that is a suffix.
            """
            length = 0;
            j = len(needle) - 1
            for i in reversed(range(p + 1)):
                if needle[i] == needle[j]:
                    length += 1
                else:
                    break
                j -= 1
            return length
        

        这是问题中的示例:

        def main():
            list1 = [1,0,1,1,1,0,0]
            list2 = [1,0,1,0,1,0,1]
            index = search(list1, [1, 1, 1])
            print(index)
            index = search(list2, [1, 1, 1])
            print(index)
        
        if __name__ == '__main__':
            main()
        

        输出:

        2 -1

        【讨论】:

          【解决方案10】:
          def sublist(l1,l2):
            if len(l1) < len(l2):
              for i in range(0, len(l1)):
                for j in range(0, len(l2)):
                  if l1[i]==l2[j] and j==i+1:
                  pass
                return True
            else:
              return False
          

          【讨论】:

            【解决方案11】:

            我最喜欢的简单解决方案如下(但是,它的野蛮力量,所以我不建议在大数据上使用它):

            >>> l1 = ['z','a','b','c']
            >>> l2 = ['a','b']
            >>>any(l1[i:i+len(l2)] == l2 for i in range(len(l1)))
            True
            

            上面的代码实际上创建了 l1 的所有可能的长度为 l2 的切片,并依次将它们与 l2 进行比较。

            详解

            只有当你不明白它是如何工作的(并且你想知道它)时才阅读这个解释,否则没有必要阅读它

            首先,您可以这样迭代 l1 项的索引:

            >>> [i for i in range(len(l1))]
            [0, 1, 2, 3]
            

            所以,因为 i 代表 l1 中项目的索引,您可以使用它来显示实际项目,而不是索引号:

            >>> [l1[i] for i in range(len(l1))]
            ['z', 'a', 'b', 'c']
            

            然后从长度为 2 的 l1 创建切片(类似于从列表中选择项目):

            >>> [l1[i:i+len(l2)] for i in range(len(l1))]
            [['z', 'a'], ['a', 'b'], ['b', 'c'], ['c']] #last one is shorter, because there is no next item.
            

            现在您可以将每个切片与 l2 进行比较,您会看到第二个匹配:

            >>> [l1[i:i+len(l2)] == l2 for i in range(len(l1))]
            [False, True, False, False] #notice that the second one is that matching one
            

            最后,使用名为 any 的函数,您可以检查至少一个布尔值是否为 True:

            >>> any(l1[i:i+len(l2)] == l2 for i in range(len(l1)))
            True
            

            【讨论】:

              【解决方案12】:

              我知道这可能与原始问题不太相关,但如果两个列表中的项目顺序无关紧要,对其他人来说可能是非常优雅的 1 行解决方案。如果 List1 元素在 List2 中(无论顺序如何),下面的结果将显示 True。如果顺序很重要,请不要使用此解决方案。

              List1 = [10, 20, 30]
              List2 = [10, 20, 30, 40]
              result = set(List1).intersection(set(List2)) == set(List1)
              print(result)
              

              输出

              True
              

              【讨论】:

                猜你喜欢
                • 2013-08-07
                • 1970-01-01
                • 1970-01-01
                • 2015-03-06
                • 2018-04-10
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多