我想在可能存在空列表的列表列表中找到第二个最小的int。我陷入了压扁的一步。
我的想法
未排序的列表,例如[1,[3,2],[[]],[4]],[[]],[12,12],[[12],[]],[ []]]
stucked !!!!!!使用纯递归展平列表。我尝试在递归步骤的第一部分执行此操作。以上示例变为[1,3,2,4],[12,12,12]
找到第二个最小的int(已完成)
这是代码
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)
答案 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
。