繁体   English   中英

如何摆脱此列表中的子元组?

[英]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处理startstop参数的方式。 对于完全闭区间的情况,必须进行一些小的更改:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM