【问题标题】:Find monotonic sequences in a list?在列表中查找单调序列?
【发布时间】:2025-11-28 00:30:02
【问题描述】:

我是 Python 新手,但基本上我想用双循环从列表中创建元素的子组,因此我会将第一个元素与下一个元素进行比较,以确定是否可以创建这些子列表,否则我将打破里面的循环,我想继续最后一个元素,但在主循环中:

示例:5,7,8,4,11

比较 5 和 7,是次要的吗?是的,所以包括在新列表中,并在里面继续下一个 8,小于 5?是的,所以包括在 newlist 中,但是当与 4 比较时,我会中断循环,所以我想继续在 m 中使用这 4 个从下一个开始,在本例中为 11...

for m in xrange(len(path)):
    for i in xrange(m+1,len(path)):
              if (path[i] > path[m]):
                  newlist.append(path[i])

             else:
                  break

             m=m+i

感谢您提出建议或其他想法来实现它!

附: 一些输入将是: 输入:[45,78,120,47,58,50,32,34] 输出:[45,78,120],[47,58],50,[32,34]

由于比较完整列表的子组,我为什么要进行双循环的想法是,当 45 小于下一个时,只需添加到新列表中,如果不采用下一个进行比较本例将是 47 并开始与 58 进行比较。

【问题讨论】:

  • “我想从列表中创建元素的子组带有双循环双循环是必需的吗?!?为了让事情更清楚,您应该提供输入示例和相应的预期输出。
  • 不,这不是一个要求,只是我不知道如何以其他方式做到这一点。 Okei 我将添加一个输入和输出的示例

标签: python for-loop iteration


【解决方案1】:

我也使用了双循环,但将内循环放在一个函数中:

#!/usr/bin/env python

def process(lst):

    def prefix(lst):
        pre = []
        while lst and (not pre or pre[-1] <= lst[0]):
            pre.append(lst[0])
            lst = lst[1:]
        return pre, lst

    res=[]
    while lst:
        subres, lst = prefix(lst)
        res.append(subres) 
    return res

print process([45,78,120,47,58,50,32,34])
=> [[45, 78, 120], [47, 58], [50], [32, 34]]

前缀函数基本上是把一个列表分成2个;第一部分由第一个升序数字组成,第二部分是仍需要处理的其余部分(或空列表,如果我们完成了)。

然后,主函数简单地将第一部分组装到结果列表中,并将其余部分交还给内部函数。

我不确定单个值 50;在您的示例中,它不在子列表中,但在我的示例中。如果是需求,那就改变

        res.append(subres) 

        res.append(subres[0] if len(subres)==1 else subres)

print process([45,78,120,47,58,50,32,34])
=> [[45, 78, 120], [47, 58], 50, [32, 34]]

【讨论】:

    【解决方案2】:

    @uselpa 的版本很好。这是我的(与 [50] 相同的问题,而不仅仅是 50),它使用 collections.deque 来提高效率,还有一些长 cmets...

    #! /usr/bin/env python
    
    from collections import deque
    
    def process(lst):
        """
        Given a list of values that is not sorted (such that for some
        valid indices i,j, i<j, sometimes lst[i] > lst[j]), produce a
        new list-of-lists, such that in the new list, each sublist *is*
        sorted:
            for all sublist \elem returnval:
                assert_is_sorted(sublist)
        and furthermore this is the minimal set of sublists required
        to achieve the condition.
    
        Thus, if the input list lst is actually sorted, this returns
        [list(lst)].
        """
        def sublist(deq):
            """
            Pop items off the front of deque deq until the next one
            goes backwards.  Return the constructed sub-list.
            """
            sub = [deq.popleft()]
            while deq and deq[0] >= sub[-1]:
                sub.append(deq.popleft())
            return sub
    
        # Make a copy of lst before modifying it; use a deque so that
        # we can pull entries off it cheaply.
        deq = deque(lst)
        output = []
        ret = []
        while deq:
            ret.append(sublist(deq))
        return ret
    
    print process([45,78,120,47,58,50,32,34])
    

    (顺便说一句,在collections.deque 之前的日子里,我可能只是使用lst 的反向副本并在sublist 中使用lst.pop()。不过,这并不那么明显。)

    【讨论】:

    • 实际上,我的问题并不容易,因为为了简化我写了数字,但我的问题中的每个数字都像:point = float(row[0]), float (row[1]), float (row[2]), row[3], row[4], row[5] ,要创建这些子列表,我必须比较不同的点值以确定它是否属于子列表
    【解决方案3】:

    没有循环! 至少,没有显式循环......

    import itertools
    
    def process(lst):
        # Guard clause against empty lists
        if len(lst) < 1:
            return lst
    
        # use a dictionary here to work around closure limitations
        state = { 'prev': lst[0], 'n': 0 }
    
        def grouper(x):
            if x < state['prev']:
                state['n'] += 1
    
            state['prev'] = x
            return state['n']
    
        return [ list(g) for k, g in itertools.groupby(lst, grouper) ]
    

    用法(同时使用 Python 2 和 Python 3):

    >>> data = [45,78,120,47,58,50,32,34]
    >>> print (list(process(data)))
    [[45, 78, 120], [47, 58], [50], [32, 34]]
    

    别开玩笑了,如果您需要分组列表中的项目itertools.groupby 值得一点关注。并不总是最简单/最好的答案——但值得一试......


    编辑:如果您不喜欢 closures 并且更喜欢使用 object 来保存状态,这里有一个替代方案:

    class process:
        def __call__(self, lst):
            if len(lst) < 1:
                return lst
    
            self.prev = lst[0]
            self.n = 0
    
            return [ list(g) for k, g in itertools.groupby(lst, self._grouper) ]
    
        def _grouper(self, x):
            if x < self.prev:
                self.n += 1
    
            self.prev = x
            return self.n
    
    
    
    data = [45,78,120,47,58,50,32,34]
    print (list(process()(data)))
    

    EDIT2: 因为我更喜欢闭包......但@torek 不喜欢 dictionary 语法,这里是围绕同一解决方案的第三种变体:

    import itertools
    
    def process(lst):
        # Guard clause against empty lists
        if len(lst) < 1:
            return lst
    
        # use an object here to work around closure limitations
        state = type('State', (object,), dict(prev=lst[0], n=0))
    
        def grouper(x):
            if x < state.prev:
                state.n += 1
    
            state.prev = x
            return state.n
    
        return [ list(g) for k, g in itertools.groupby(lst, grouper) ]
    
    data = [45,78,120,47,58,50,32,34]
    print (list(process(data)))
    

    【讨论】:

    • 不错。我试图想出一个itertools 方法,但不喜欢我想出的任何选项。这可以通过使用持有状态的类实例变得更漂亮......
    • @torek 我在这里没有使用类 (1) 是为了使用闭包的乐趣 (2) 因为不必在代码中实例化一个对象(我不是什么大人物process()(data) 之类的东西的粉丝)。但我认为使用其中一种是个人喜好问题......
    • @torek 我已经编辑了答案以添加基于类的等价物。
    【解决方案4】:

    为什么不简单地使用单个 for 循环而不是使用 itertool 和所有花哨的东西。

    def list_test(inlist, outlist):
        if not inlist:
            return []
        prev=inlist[0]
        x=[]
        for item in inlist:
            if item >= prev:
                x.append(item)
            else:
                outlist.append(x)
                x=[]
                x.append(item)
            prev=item
        if x:
            outlist.append(x)
    
    
    in_list=[1,0,1,2,3]
    out_list = []
    list_test(in_list, out_list)    
    print(out_list)
    
    

    O/p:[[1], [0, 1, 2, 3]]

    【讨论】: