У розчині госебу є невелика вада , що робить його O (n ** 2), а не O (n).
Проблема в тому, що це виконує:
item = l1.pop(0)
Зі зв’язаними списками або діками це буде операцією O (1), тому це не вплине на складність, але оскільки списки python реалізовані як вектори, це копіює решту елементів l1 на один пробіл, операція O (n) . Оскільки це робиться при кожному проходженні списку, він перетворює алгоритм O (n) в O (n ** 2). Це можна виправити, використовуючи метод, який не змінює списки джерел, а просто відстежує поточну позицію.
Я спробував порівняльний аналіз виправленого алгоритму проти простого сортування (l1 + l2), як запропонував dbr
def merge(l1,l2):
if not l1: return list(l2)
if not l2: return list(l1)
if l1[-1] > l2[-1]:
l1,l2 = l2,l1
it = iter(l2)
y = it.next()
result = []
for x in l1:
while y < x:
result.append(y)
y = it.next()
result.append(x)
result.append(y)
result.extend(it)
return result
Я перевірив їх зі списками, сформованими за допомогою
l1 = sorted([random.random() for i in range(NITEMS)])
l2 = sorted([random.random() for i in range(NITEMS)])
Для різних розмірів списку я отримую такі терміни (повторюються 100 разів):
merge : 0.079 0.798 9.763 109.044
sort : 0.020 0.217 5.948 106.882
Отже, насправді, схоже, що dbr є правильним, бажано просто використовувати sorted (), якщо ви не очікуєте дуже великих списків, хоча він має гіршу алгоритмічну складність. Точка беззбитковості становить близько мільйона предметів у кожному списку джерел (загалом 2 мільйони).
Однією з переваг підходу злиття є те, що тривіально переписати його як генератор, який використовуватиме значно менше пам'яті (не потрібно проміжного списку).
[Редагувати]
Я повторив це із ситуацією, ближчою до запитання - використовуючи список об'єктів, що містять поле " date", яке є об'єктом дати та часу. Вищезазначений алгоритм було замінено на порівняння .date, а метод сортування змінено на:
return sorted(l1 + l2, key=operator.attrgetter('date'))
Це трохи змінює ситуацію. Порівняння дорожче означає, що число, яке ми виконуємо, стає більш важливим щодо постійної швидкості реалізації. Це означає, що злиття складає втрачені позиції, замість цього перевершуючи метод sort () на 100 000 предметів. Порівняння на основі ще більш складного об'єкта (наприклад, великих рядків або списків), можливо, змістить цей баланс ще більше.
merge : 0.161 2.034 23.370 253.68
sort : 0.111 1.523 25.223 313.20
[1]: Примітка: Насправді я зробив лише 10 повторень для 1 000 000 предметів і відповідно масштабував, оскільки це було досить повільно.