Python 按连续顺序对元组列表排序

Python 按连续顺序对元组列表排序,python,list,python-3.x,Python,List,Python 3.x,我想对元组列表进行连续排序,使每个元组的第一个元素等于前一个元组的最后一个元素 例如: input = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)] output = [(10, 7), (7, 13), (13, 4), (4, 9), (9, 10)] 我开发了如下搜索: output=[] given = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)] t = given[0][0] for i in

我想对元组列表进行连续排序,使每个元组的第一个元素等于前一个元组的最后一个元素

例如:

input = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
output = [(10, 7), (7, 13), (13, 4), (4, 9), (9, 10)]
我开发了如下搜索:

output=[]
given = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
t = given[0][0]
for i in range(len(given)):
      # search tuples starting with element t
      output += [e for e in given if e[0] == t]
      t = output[-1][-1] # Get the next element to search

print(output)    
tpl = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
start_dict = {item[0]: item for item in tpl}

start = tpl[0][0]
res = []
while start_dict:
    item = start_dict[start]
    del start_dict[start]
    res.append(item)
    start = item[-1]

print(res)
10 - 7 - 13 - 4 - 9 - 10 (same 10 as at the beginning)
有没有一种类似蟒蛇的方法来达到这样的目的? 还有一种“到位”的方法(只有一份清单)


在我的问题中,输入可以使用所有元组以循环方式重新排序,因此选择的第一个元素并不重要。

如果您不怕浪费一些内存,您可以创建一个字典
start\u dict
,包含起始整数作为键,元组作为值,并执行以下操作:

output=[]
given = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
t = given[0][0]
for i in range(len(given)):
      # search tuples starting with element t
      output += [e for e in given if e[0] == t]
      t = output[-1][-1] # Get the next element to search

print(output)    
tpl = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
start_dict = {item[0]: item for item in tpl}

start = tpl[0][0]
res = []
while start_dict:
    item = start_dict[start]
    del start_dict[start]
    res.append(item)
    start = item[-1]

print(res)
10 - 7 - 13 - 4 - 9 - 10 (same 10 as at the beginning)
如果两个元组以相同的数字开头,您将丢失其中一个。。。如果没有使用所有的起始编号,循环将不会终止


但也许这是一个可以建立的基础。

事实上,关于您打算将什么作为输出,以及如果输入列表的结构无效,您需要做什么,还有很多问题

假设您有一对输入,其中每个数字只包含两次。因此,我们可以把这样的输入看作一个图,其中数字是节点,每对是边。就我理解你的问题而言,你假设这个图是循环的,看起来像这样:

output=[]
given = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
t = given[0][0]
for i in range(len(given)):
      # search tuples starting with element t
      output += [e for e in given if e[0] == t]
      t = output[-1][-1] # Get the next element to search

print(output)    
tpl = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
start_dict = {item[0]: item for item in tpl}

start = tpl[0][0]
res = []
while start_dict:
    item = start_dict[start]
    del start_dict[start]
    res.append(item)
    start = item[-1]

print(res)
10 - 7 - 13 - 4 - 9 - 10 (same 10 as at the beginning)
这表明您可以将存储图形的列表缩减为
[10,7,13,4,9]
。下面是对输入列表进行排序的脚本:

# input
input = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]

# sorting and archiving
first = input[0][0]
last = input[0][1]
output_in_place = [first, last]

while last != first:
    for item in input:
        if item[0] == last:
            last = item[1]
            if last != first:
                output_in_place.append(last)

print(output_in_place)

# output
output = []
for i in range(len(output_in_place) - 1):
    output.append((output_in_place[i], output_in_place[i+1]))
output.append((output_in_place[-1], output_in_place[0]))

print(output)

我会首先创建一个字典的形式

{first_value: [list of tuples with that first value], ...}
然后从那里开始工作:

from collections import defaultdict

chosen_tuples = input[:1]  # Start from the first

first_values = defaultdict()
for tup in input[1:]:
    first_values[tup[0]].append(tup)

while first_values:  # Loop will end when all lists are removed
    value = chosen_tuples[-1][1]  # Second item of last tuple
    tuples_with_that_value = first_values[value]
    chosen_tuples.append(tuples_with_that_value.pop())
    if not chosen_with_that_value:
        del first_values[value]  # List empty, remove it
您可以尝试以下方法:

input = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]

output = [input[0]]  # output contains the first element of input
temp = input[1:]  # temp contains the rest of elements in input

while temp:
    item = [i for i in temp if i[0] == output[-1][1]].pop()  # We compare each element with output[-1]
    output.append(item)  # We add the right item to output
    temp.remove(item)  # We remove each handled element from temp
输出:

>>> output
[(10, 7), (7, 13), (13, 4), (4, 9), (9, 10)]

假设
列表中的元组是循环的,您可以使用
dict
在O(n)的复杂度内实现它,如下所示:

新列表持有的最终值将为:

>>> new_list
[(10, 7), (7, 13), (13, 4), (4, 9), (9, 10)]
这是一种(效率低于字典版本)变体,其中列表发生了更改:

tpl = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]

for i in range(1, len(tpl)-1):   # iterate over the indices of the list
    item = tpl[i]
    for j, next_item in enumerate(tpl[i+1:]):  # find the next item 
                                               # in the remaining list
        if next_item[0] == item[1]:
            next_index = i + j
            break
    tpl[i], tpl[next_index] = tpl[next_index], tpl[i]  # now swap the items
以下是同一想法的更有效版本:

tpl = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]
start_index = {item[0]: i for i, item in enumerate(tpl)}

item = tpl[0]
next_index = start_index[item[-1]]
for i in range(1, len(tpl)-1):
    tpl[i], tpl[next_index] = tpl[next_index], tpl[i]
    # need to update the start indices:
    start_index[tpl[next_index][0]] = next_index
    start_index[tpl[i][0]] = i
    next_index = start_index[tpl[i][-1]]
print(tpl)

清单已更改到位;字典仅包含元组的起始值及其在列表中的索引。

这是一个使用排序功能和自定义键功能的健壮解决方案:

input = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]

def consec_sort(lst):
    def key(x):
        nonlocal index
        if index <= lower_index:
            index += 1
            return -1
        return abs(x[0] - lst[index - 1][1])
    for lower_index in range(len(lst) - 2):
        index = 0
        lst = sorted(lst, key=key)
    return lst

output = consec_sort(input)
print(output)
input=[(10,7)、(4,9)、(13,4)、(7,13)、(9,10)]
def连续分拣(lst):
def键(x):
非局部索引
如果指数我的2美分:

def match_tuples(input):
    # making a copy to not mess up with the original one
    tuples = input[:]          # [(10,7), (4,9), (13, 4), (7, 13), (9, 10)]
    last_elem = tuples.pop(0)  # (10,7)

    # { "first tuple's element": "index in list"}
    indexes = {tup[0]: i for i, tup in enumerate(tuples)} # {9: 3, 4: 0, 13: 1, 7: 2}

    yield last_elem  # yields de firts element

    for i in range(len(tuples)):
        # get where in the list is the tuple which first element match the last element in the last tuple
        list_index = indexes.get(last_elem[1])
        last_elem = tuples[list_index] # just get that tuple
        yield last_elem
输出

input = [(10,7), (4,9), (13, 4), (7, 13), (9, 10)]
print(list(match_tuples(input)))
# output: [(10, 7), (7, 13), (13, 4), (4, 9), (9, 10)]
要获得
O(n)
算法,需要确保不在阵列上进行双循环。一种方法是将已经处理过的值保存在某种查找表中(一个
dict
将是一个不错的选择)

例如,类似这样的内容(我希望内联注释能够很好地解释功能)。这将修改列表,并应避免不必要的(甚至是隐含的)列表循环:

inp = [(10, 7), (4, 9), (13, 4), (7, 13), (9, 10)]

# A dictionary containing processed elements, first element is
# the key and the value represents the tuple. This is used to
# avoid the double loop
seen = {}

# The second value of the first tuple. This must match the first
# item of the next tuple
current = inp[0][1]

# Iteration to insert the next element
for insert_idx in range(1, len(inp)):
    # print('insert', insert_idx, seen)
    # If the next value was already found no need to search, just
    # pop it from the seen dictionary and continue with the next loop
    if current in seen:
        item = seen.pop(current)
        inp[insert_idx] = item
        current = item[1]
        continue

    # Search the list until the next value is found saving all
    # other items in the dictionary so we avoid to do unnecessary iterations
    # over the list.
    for search_idx in range(insert_idx, len(inp)):
        # print('search', search_idx, inp[search_idx])
        item = inp[search_idx]
        first, second = item
        if first == current:
            # Found the next tuple, break out of the inner loop!
            inp[insert_idx] = item
            current = second
            break
        else:
            seen[first] = item

如果一个元组与其他元组中的任何一个都不匹配怎么办?另外,配对是唯一的吗?或者如果第一次配对不正确,您必须处理回溯吗?我认为排序或连续这两个术语都不适用于此问题。@JonathonReinhart Ten,您将如何暴露此问题?
排序列表
在这种情况下听起来已经比
排序列表
更好了。仍然在寻找一个更好的词。但是,
sort
显然是误导性的,因为它表明,可以使用排序算法,这意味着只需查看列表中的两个条目,您就知道哪一个比另一个小。我喜欢您的“dict(input)”转换建议,它提高了长输入数组的速度。我很高兴知道否决投票的原因。可能是我可以改进:)可能是因为如果您支票上方的行中的elem不在input_dict
中,将引发
KeyError
异常。您可能应该使用
尝试:\n elem=。。。m] \n除KeyError外:\n raise KeyError(…
相反。根据代码中的逻辑,这永远不会发生。在引发
键错误
之前,将引发代码的
异常
,这是我正在使用的自定义异常,OP可以根据其要求进行修改。原因:循环从已知的
键开始,在分配下一个键之前,我检查presdict键的不一致性为什么你要费心从字典中删除元素start_dict[start]?另外,我更喜欢start=item[-1],因为解决方案仍然适用于包含2个以上元素的元组。即使是可变元组长度[(1,2),(7,3,1),(2,4,6,7)]@user3715819:循环(现在的情况)当
start\u dict
为空时终止;这就是为什么我要麻烦从中删除。但是,是的:还有很多改进的空间!