[英]How to get rid of sub-tuples in this list?
list_of_tuple = [(0,2), (0,6), (4,6), (6,7), (8,9)]
由于(0,2)
& (4,6)
都在(0,6)
的索引内,所以我想删除它们。 结果列表将是:
list_of_tuple = [(0,6), (6,7), (8,9)]
看来我需要以某种方式对这个列表元组进行排序以使其更容易删除。 但是如何对元组列表进行排序?
给定两个数组索引元组[m,n]
和[a,b]
,如果:
m >=a & n<=b
然后[m,n]
包含在[a,b]
中,然后从列表中删除[m,n]
。
要从list_of_tuples
中删除范围超出指定元组的所有元组:
list_of_tuple = [(0,2), (0,6), (4,6), (6,7), (8,9)]
def rm(lst,tup):
return [tup]+[t for t in lst if t[0] < tup[0] or t[1] > tup[1]]
print(rm(list_of_tuple,(0,6)))
Output:
[(0, 6), (6, 7), (8, 9)]
这是一个非常简单的解决方案,但它是 O(n 2 ):
intervals = [(0, 2), (0, 6), (4, 6), (6, 7), (8, 9)] # list_of_tuple
result = [
t for t in intervals
if not any(t != u and t[0] >= u[0] and t[1] <= u[1] for u in intervals)
]
它过滤掉不等于但包含在任何其他间隔中的间隔。
似乎有机会同时滥用reduce()
和 Python 的逻辑运算符,解决方案假设列表按照 OP 的示例进行排序,主要在每个元组的第二个元素上进行排序:其次是在第一个元素上:
from functools import reduce
list_of_sorted_tuples = [(0, 2), (0, 6), (4, 6), (6, 7), (8, 9)]
def contains(a, b):
return a[0] >= b[0] and a[1] <= b[1] and [b] or b[0] >= a[0] and b[1] <= a[1] and [a] or [a, b]
reduced_list = reduce(lambda x, y: x[:-1] + contains(x[-1], y) if x else [y], list_of_sorted_tuples, [])
print(reduced_list)
OUTPUT
> python3 test.py
[(0, 6), (6, 7), (8, 9)]
>
您可以尝试这样的事情来检查(半开)区间的两端是否包含在另一个区间内:
list_of_tuple = [(0,2), (0,6), (4,6), (6,7), (8,9)]
reduced_list = []
for t in list_of_tuple:
add = True
for o in list_of_tuple:
if t is not o:
r = range(*o)
if t[0] in r and (t[1] - 1) in r:
add = False
if add:
reduced_list.append(t)
print(reduced_list) # [(0, 6), (6, 7), (8, 9)]
注意:这假设您的元组是半开区间,即 [0, 6),其中 0 包含但 6 不包含,类似于range
处理start
和stop
参数的方式。 对于完全闭区间的情况,必须进行一些小的更改:
range(*o)
-> range(o[0], o[1] + 1)
和
if t[0] in r and (t[1] - 1) in r:
-> if t[0] in r and t[1] in r:
这是迈向可以在 O(n log(n)) 中完成的解决方案的第一步:
def non_cont(lot):
s = sorted(lot, key = lambda t: (t[0], -t[1]))
i = 1
while i < len(s):
if s[i][0] >= s[i - 1][0] and s[i][1] <= s[i - 1][1]:
del s[i]
else:
i += 1
return s
这个想法是,在使用特殊键 function 进行排序后,包含在某个其他元素中的每个元素将直接位于包含它的元素之后。 然后,我们扫描列表,删除它们之前的元素所包含的元素。 现在,扫描和删除循环本身的复杂度为 O(n^2)。 上述解决方案是为了清楚起见,而不是其他任何事情。 我们可以转到下一个实现:
def non_cont_on(lot):
s = sorted(lot, key = lambda t: (t[0], -t[1]))
i = 1
result = s[:1]
for i in s:
if not (i[0] >= result[-1][0] and i[1] <= result[-1][1]):
result.append(i)
return result
这里没有二次扫描和删除循环,只有构造结果的一个很好的线性过程。 空间复杂度为 O(n)。 可以在没有额外的非常量空间的情况下执行此算法,但我将忽略这一点。
这两种算法的副作用是对区间进行排序。
如果您想保留有关包含结构的信息(通过哪个封闭间隔消耗原始集合的间隔),您可以构建一个“一级树”:
def contained(tpl1, tpl2):
return tpl1[0] >= tpl2[0] and tpl1[1] <= tpl2[1]
def interval_hierarchy(lst):
if not lst:
return
root = lst.pop()
children_dict = {root: []}
while lst:
t = lst.pop()
curr_children = list(children_dict.keys())
for k in curr_children:
if contained(k, t):
children_dict[t] = (children_dict[t] if t in children_dict else []) +\
[k, *children_dict[k]]
children_dict.pop(k)
elif contained(t, k):
children_dict[k].append(t)
if t in children_dict:
children_dict[k] += children_dict[t]
children_dict.pop(t)
else:
if not t in children_dict:
children_dict[t] = []
# return whatever information you might want to use
return children_dict, list(children_dict.keys())
您似乎正在尝试合并重叠的间隔。 例如,(9,11)、(10,12) 在下面的第二个示例中合并生成 (9,12)。
在这种情况下,使用sorted
的简单排序将自动处理元组。
方法:存储下一个要添加的区间。 继续延长间隔的结束,直到遇到一个值,其“开始”位于 (>=) 下一个要添加的值的“结束”之后。 此时,可以将存储的下一个间隔附加到结果中。 最后 Append 用于处理所有值。
def merge_intervals(val_input):
if not val_input:
return []
vals_sorted = sorted(val_input) # sorts by tuple values "natural ordering"
result = []
x0, x1 = vals_sorted[0] # store next interval to be added as (x0, x1)
for start, end in vals_sorted[1:]:
if start >= x1: # reached next separate interval
result.append((x0, x1))
x0, x1 = (start, end)
elif end > x1:
x1 = end # extend length of next interval to be added
result.append((x0, x1))
return result
print(merge_intervals([(0,2), (0,6), (4,6), (6,7), (8,9)]))
print(merge_intervals([(1,2), (9,11), (10,12), (1,7)]))
Output:
[(0, 6), (6, 7), (8, 9)]
[(1, 7), (9, 12)]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.