Объединить отсортированные списки в Python - PullRequest
12 голосов
/ 21 июля 2009

У меня есть куча отсортированных списков объектов и функция сравнения

class Obj :
    def __init__(p) :
        self.points = p
def cmp(a, b) :
    return a.points < b.points

a = [Obj(1), Obj(3), Obj(8), ...]
b = [Obj(1), Obj(2), Obj(3), ...]
c = [Obj(100), Obj(300), Obj(800), ...]

result = magic(a, b, c)
assert result == [Obj(1), Obj(1), Obj(2), Obj(3), Obj(3), Obj(8), ...]

как выглядит magic? Моя текущая реализация

def magic(*args) :
    r = []
    for a in args : r += a
    return sorted(r, cmp)

но это довольно неэффективно. Лучшие ответы?

Ответы [ 9 ]

14 голосов
/ 21 июля 2009

Стандартная библиотека Python предлагает метод для этого: heapq.merge.
Как сказано в документации, это очень похоже на использование itertools (но с большими ограничениями); если вы не можете жить с этими ограничениями (или если вы не используете Python 2.6), вы можете сделать что-то вроде этого:

sorted(itertools.chain(args), cmp)

Однако я думаю, что он имеет ту же сложность, что и ваше собственное решение, хотя использование итераторов должно дать довольно неплохую оптимизацию и увеличение скорости.

2 голосов
/ 21 июля 2009

Мне нравится ответ Роберто Лиффредо. Я не знал о heapq.merge (). Hmmmph.

Вот как выглядит полное решение с использованием лидерства Роберто:

class Obj(object):
    def __init__(self, p) :
        self.points = p
    def __cmp__(self, b) :
        return cmp(self.points, b.points)
    def __str__(self):
        return "%d" % self.points

a = [Obj(1), Obj(3), Obj(8)]
b = [Obj(1), Obj(2), Obj(3)]
c = [Obj(100), Obj(300), Obj(800)]

import heapq

sorted = [item for item in heapq.merge(a,b,c)]
for item in sorted:
    print item

Или:

for item in heapq.merge(a,b,c):
    print item
2 голосов
/ 21 июля 2009

Вместо использования списка вы можете использовать [кучу] (http://en.wikipedia.org/wiki/Heap_(data_structure).

Вставка - O (log (n)), поэтому объединение a, b и c будет O (n log (n))

В Python вы можете использовать модуль heapq .

2 голосов
/ 21 июля 2009

Используйте модуль bisect. Из документации: «Этот модуль поддерживает ведение списка в отсортированном порядке без необходимости сортировки списка после каждой вставки».

import bisect

def magic(*args):
    r = []
    for a in args:
        for i in a:
            bisect.insort(r, i)
    return r
0 голосов
/ 09 апреля 2013

Ниже приведен пример функции, которая выполняется в O (n) сравнениях.

Вы могли бы сделать это быстрее, создавая итераторы a и b и увеличивая их.

Я просто дважды вызвал функцию, чтобы объединить 3 списка:

def zip_sorted(a, b):
    '''
    zips two iterables, assuming they are already sorted
    '''
    i = 0
    j = 0
    result = []
    while i < len(a) and j < len(b):
        if a[i] < b[j]:
            result.append(a[i])
            i += 1
        else:
            result.append(b[j])
            j += 1
    if i < len(a):
        result.extend(a[i:])
    else:
        result.extend(b[j:])
    return result

def genSortedList(num,seed):
    result = [] 
    for i in range(num):
        result.append(i*seed)
    return result

if __name__ == '__main__':
    a = genSortedList(10000,2.0)
    b = genSortedList(6666,3.0)
    c = genSortedList(5000,4.0)
    d = zip_sorted(zip_sorted(a,b),c)
    print d

Однако heapq.merge использует сочетание этого метода и накапливает текущие элементы всех списков, поэтому должен работать намного лучше

0 голосов
/ 21 июля 2009

Я задал похожий вопрос и получил несколько отличных ответов:

Лучшее решение этого вопроса - варианты алгоритма слияния, о котором вы можете прочитать здесь:

0 голосов
/ 21 июля 2009

Вот, пожалуйста, полнофункциональная сортировка слиянием для списков (адаптировано из моего вида здесь ):

def merge(*args):
    import copy
    def merge_lists(left, right):
        result = []
        while left and right:
            which_list = (left if left[0] <= right[0] else right)
            result.append(which_list.pop(0))
        return result + left + right
    lists = list(args)
    while len(lists) > 1:
        left, right = copy.copy(lists.pop(0)), copy.copy(lists.pop(0))
        result = merge_lists(left, right)
        lists.append(result)
    return lists.pop(0)

Назовите это так:

merged_list = merge(a, b, c)
for item in merged_list:
    print item

На всякий случай, я добавлю пару изменений в ваш класс Obj:

class Obj(object):
    def __init__(self, p) :
        self.points = p
    def __cmp__(self, b) :
        return cmp(self.points, b.points)
    def __str__(self):
        return "%d" % self.points
  • Получено от объекта
  • Пропуск self на __init__()
  • Сделать __cmp__ функцией-членом
  • Добавить str() функцию-член для представления Obj в виде строки
0 голосов
/ 21 июля 2009

Решение одной строки с использованием сортировки:

def magic(*args):
  return sorted(sum(args,[]), key: lambda x: x.points)

ИМО, это решение очень читабельно.

Используя модуль heapq, он мог бы быть более эффективным, но я его не проверял. Вы не можете указать функцию cmp / key в heapq, поэтому вы должны реализовать Obj для неявной сортировки.

import heapq
def magic(*args):
  h = []
  for a in args:
    heapq.heappush(h,a)
  return [i for i in heapq.heappop(h)
0 голосов
/ 21 июля 2009

Я не знаю, будет ли это быстрее, но вы могли бы упростить это с помощью:

def GetObjKey(a):
    return a.points

return sorted(a + b + c, key=GetObjKey)

Вы также можете, конечно, использовать cmp вместо key, если хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...