使用递归在Python 3中查找列表中第二个最小的int

时间:2016-02-24 06:46:52

标签: python recursion

我想在可能存在空列表的列表列表中找到第二个最小的int。我陷入了压扁的一步。

我的想法

  1. 未排序的列表,例如[1,[3,2],[[]],[4]],[[]],[12,12],[[12],[]],[ []]]

  2. stucked !!!!!!使用纯递归展平列表。我尝试在递归步骤的第一部分执行此操作。以上示例变为[1,3,2,4],[12,12,12]

  3. 找到第二个最小的int(已完成)

  4. 这是代码

    def find(abc):
    #base case
    if len(abc) == 2:
        if isinstance(abc[0], list) and isinstance(abc[1], list):
            re = find(abc[0] + abc[1])
        elif isinstance(abc[1], list):
            re = find(abc[:1] + abc[1])
        elif isinstance(abc[0], list):
            first = find(abc[0] + abc[1:])
        else:
            if abc[0] > abc[1]:
                re = (abc[0], abc[1])
            else:
                re = (abc[1], abc[0])
    # recursive step
    else:
        #i think this part has problem 
        # flatten the list
        if isinstance(abc[0], list):
            re = find(abc[0] + abc[1:])
        # if this element is interger
        else:
            current = abc[0]
            second, first = find(abc[1:])
            if (second < current):
                re = (second, first)
            elif (current > first) and (second >= current):
                re = (current, first)
            else:
                re = (first, current)            
    return re
    e.g   find([[[]], [12, 12], [[12], []], [[]]]) -> (12, 12)
          find([1, [2, 3], [[]], [4]]) -> (2, 1)
    

4 个答案:

答案 0 :(得分:1)

刚刚解决问题: 您必须在递归中处理空列表的情况。对代码的最小(并且肯定有点hacky)更改看起来像这样:

import sys 

def find(abc):
    #base case
    if len(abc) == 2:
        if isinstance(abc[0], list) and isinstance(abc[1], list):
            re = find(abc[0] + abc[1:])
        elif isinstance(abc[1], list):
            re = find(abc[:1] + abc[1])
        elif isinstance(abc[0], list):
            re = find(abc[0] + abc[1:])
            # ^^^ fixed typo (ifs could be simplified by dropping first if)
        else:
            if abc[0] > abc[1]:
                re = (abc[0], abc[1])
            else:
                re = (abc[1], abc[0])
    # recursive step
    else:
        # CHANGE HERE
        if len(abc) == 0:   # @HACK: handle empty list
            return (sys.maxsize, sys.maxsize)
        # CHANGE ENDS
        if isinstance(abc[0], list):
            re = find(abc[0] + abc[1:])
        # if this element is integer
        else:
            current = abc[0]
            second, first = find(abc[1:])
            if (second < current):
                re = (second, first)
            elif (current > first) and (second >= current):
                re = (current, first)
            else:
                re = (first, current)            
    return re # @TODO: possibly filter out maxsize in final result

这远非完美(例如,如果没有足够的值并且可能有额外的错误,则会产生maxsize)。

重构您的代码: 因此,我会以两种方式重构您的代码。首先,我将分离展平和搜索(即先展平然后搜索展平列表):

def flatten(li):
    for el in li:
        try:    # if strings can be in list, would have to check here
            for sub in flatten(el):
                yield sub
        except TypeError:
            yield el

def find(abc):
    abc = list(flatten(abc))

    def _smallest2(abc):
        # basically your implementation for finding the smallest two values
        if len(abc) <= 2:
            return tuple(sorted(abc, reverse=True))
        current = abc[0]
        second, first = _smallest2(abc[1:])
        if (second < current):
            re = (second, first)
        elif (current > first) and (second >= current):
            re = (current, first)
        else:
            re = (first, current)       
        return re

    return _smallest2(abc)

其次,我会使用here代替您的搜索实现:

import heapq

def flatten(li):
    for el in li:
        try:    # if strings can be in list, would have to check here
            for sub in flatten(el):
                yield sub
        except TypeError:
            yield el

def find(li):
    return tuple(heapq.nsmallest(2, flatten(li))[::-1])

如果您的回复值略有不同,请随时放弃tuple[::-1]

替代实施: 虽然我因为各种原因(例如健壮性,表现力)而更喜欢上面重构的代码,但这里有一个替代实现,可以说更符合您的初始问题。这个实现背后的主要思想是只检查第一个元素是否是一个列表?如果是,则展平;如果不是,递归地下去列表的尾部:

def find(abc):
    try:                                                          # first element is list
        return find(abc[0] + abc[1:])                             #  -> flatten
    except:                                                       # first element is value
        if len(abc) <= 1: return abc                              # -> either end recursion
        return sorted(abc[:1] + find(abc[1:]), reverse=True)[-2:] # -> or recurse over tail

请注意,返回类型略有不同(列表而不是元组)。您可以将sorted替换为heapq.nsmallest(对于小n来说,这可能更有效。)

答案 1 :(得分:1)

如果你的用例很大并且你想避免递归,你可以执行一些迭代魔术并避免递归:

def nested_walker(data):
    working_iterators = [iter(data)]
    while working_iterators:
        current_iterator = working_iterators.pop()
        for elem in current_iterator:
            if isinstance(elem, list):
                working_iterators.append(iter(elem))
            else:
                yield elem

然后,你可以做以下事情:

data = [[1, [3, 2], [[]], [4]],[[]], [12, 12], [[12], []], [[]]]
sorted_list = sorted(nested_walker(data))
print sorted_list[1]

有更智能的方法来检索第二个最小的整数。为了避免排序潜在的巨大尺寸,您可以使用nested_walker,因为它是一个生成器函数。

正如@stephan指出的那样,有一种方法可以使用heapq来避免排序:

data = [[1, [3, 2], [[]], [4]],[[]], [12, 12], [[12], []], [[]]]
two_mins = heapq.nsmallest(2, nested_walker(data))
print two_mins[1]  # the [0] is the minimum

值得检查the documentation about heapq,因为它在性能方面可能有点棘手。

答案 2 :(得分:0)

遍历列表,然后使用isinstance检查是否需要递归:

def MIN(lst):
   mn = None
   for item in lst:
       if isinstance(item, list) and item:
           tmp = MIN(item)
       else:
           tmp = item

       if not mn:
          mn = tmp
       elif mn > tmp:
           mn = tmp
   return mn

def find(lst):
  mins = []
  if not all(isinstance(item, list) for item in lst):
     mins.append(MIN(lst))
  for item in lst:
     if isinstance(item, list):
        mins.append(MIN(item))
  return filter(lambda x: x==0 or x, mins)

print find([[[]], [12, 12], [[12], []], [[]]])
print find([1, [2, 3], [[]], [4]])

答案 3 :(得分:0)

这是Python 3中的一个解决方案,它将列表展平,对其进行排序并最终返回结果的第二个最小项:

import collections

def flatten(l):
    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, (str, bytes)):
            for sub in flatten(el):
                yield sub
        else:
            yield el

a = flatten([[[]], [12, 12], [[12], []], [[]]])
b = flatten([1, [2, 3], [[]], [4]])

print(sorted(a)[1]) # 12
print(sorted(b)[1]) # 2

flatten函数被this answer直接窃取。对于Python 2.7,将(str, bytes)替换为basestring中的flatten