Объединение двух отсортированных списков в Python

У меня есть два списка предметов. Каждый список уже отсортирован по свойству объекта типа datetime. Я хотел бы объединить два списка в один отсортированный список. Это лучший способ просто выполнить сортировку или есть более разумный способ сделать это в Python?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
77
0
112 752
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

Это просто слияние. Относитесь к каждому списку как к стеку и непрерывно выталкивайте меньшую из двух головок стека, добавляя элемент в список результатов, пока один из стеков не станет пустым. Затем добавьте все оставшиеся элементы в получившийся список.

Сортировка слиянием действительно является оптимальным решением.

Ignacio Vazquez-Abrams 21.01.2009 10:41

Но быстрее ли это, чем использование встроенной сортировки Python?

akaihola 21.01.2009 15:59

Это просто слияние, а не сортировка слиянием.

Glenn Maynard 21.07.2009 13:50

@akaihola: Если len(L1 + L2) < 1000000, то sorted(L1 + L2) быстрее stackoverflow.com/questions/464342/…

jfs 13.11.2010 20:09

Ну, наивный подход (объединить 2 списка в большой и отсортировать) будет сложностью O (N * log (N)). С другой стороны, если вы реализуете слияние вручную (я не знаю ни одного готового кода в библиотеках Python для этого, но я не эксперт), сложность будет O (N), что явно быстрее. Идея очень хорошо описана в посте Барри Келли.

Интересно отметить, что алгоритм сортировки Python очень хорош, поэтому производительность, вероятно, будет лучше, чем O (n log n), поскольку алгоритм часто использует закономерности во входных данных. Однако я бы никогда не стал на это полагаться.

Daniel Nadasi 21.01.2009 10:45

Обобщенная сортировка (т. Е. Исключение сортировки по основанию из доменов с ограниченным значением) не может быть выполнена менее чем за O (n log n) на последовательной машине.

Barry Kelly 21.01.2009 13:17
def compareDate(obj1, obj2):
    if obj1.getDate() < obj2.getDate():
        return -1
    elif obj1.getDate() > obj2.getDate():
        return 1
    else:
        return 0



list = list1 + list2
list.sort(compareDate)

Сортирую список на месте. Определите свою собственную функцию для сравнения двух объектов и передайте эту функцию во встроенную функцию сортировки.

НЕ используйте пузырьковую сортировку, у нее ужасная производительность.

Сортировка слиянием определенно будет быстрее, но немного сложнее, если вам придется реализовать ее самостоятельно. Я считать python использует быструю сортировку.

Josh Smeaton 21.01.2009 10:46

Используйте этап сортировки слиянием, он выполняется за O (n) раз.

Из википедия (псевдокод):

function merge(left,right)
    var list result
    while length(left) > 0 and length(right) > 0
        if first(left) ≤ first(right)
            append first(left) to result
            left = rest(left)
        else
            append first(right) to result
            right = rest(right)
    end while
    while length(left) > 0 
        append left to result
    while length(right) > 0 
        append right to result
    return result

Это простое объединение двух отсортированных списков. Взгляните на приведенный ниже пример кода, в котором объединены два отсортированных списка целых чисел.

#!/usr/bin/env python
## merge.py -- Merge two sorted lists -*- Python -*-
## Time-stamp: "2009-01-21 14:02:57 ghoseb"

l1 = [1, 3, 4, 7]
l2 = [0, 2, 5, 6, 8, 9]

def merge_sorted_lists(l1, l2):
    """Merge sort two sorted lists

    Arguments:
    - `l1`: First sorted list
    - `l2`: Second sorted list
    """
    sorted_list = []

    # Copy both the args to make sure the original lists are not
    # modified
    l1 = l1[:]
    l2 = l2[:]

    while (l1 and l2):
        if (l1[0] <= l2[0]): # Compare both heads
            item = l1.pop(0) # Pop from the head
            sorted_list.append(item)
        else:
            item = l2.pop(0)
            sorted_list.append(item)

    # Add the remaining of the lists
    sorted_list.extend(l1 if l1 else l2)

    return sorted_list

if __name__ == '__main__':
    print merge_sorted_lists(l1, l2)

Это должно нормально работать с объектами datetime. Надеюсь это поможет.

К сожалению, это контрпродуктивно - обычно слияние было бы O (n), но поскольку вы появляетесь слева от каждого списка (операция O (n)), вы фактически делаете это процесс O (n ** 2) - хуже наивной сортированной (l1 + l2)

Brian 21.01.2009 12:54

@Brian Я на самом деле думаю, что это решение является самым чистым из всех, и я считаю, что вы правы в O (n) сложности извлечения первого элемента из списка. Вы можете устранить эту проблему, используя deque из коллекций, что дает вам O (1) при выталкивании элемента с любой стороны. docs.python.org/2/library/collections.html#collections.deque

mohi666 08.05.2015 03:59

@Brian, head, tail = l[0], l[1:] тоже будет иметь сложность O (n ** 2)?

Nikolay Fominyh 12.02.2016 20:14

@Brian: В качестве альтернативы collections.deque эту проблему также можно решить, создав l1 и l2 в обратном порядке (l1 = l1[::-1], l2 = l2[::-1]), затем работая с правой стороны, а не с левой, заменив if l1[0] <= l2[0]: на if l1[-1] <= l2[-1]:, заменив pop(0) на pop(). и меняем sorted_list.extend(l1 if l1 else l2) на sorted_list.extend(reversed(l1 if l1 else l2))

ShadowRanger 24.03.2018 03:57
Ответ принят как подходящий

Люди, кажется, слишком усложняют это ... Просто объедините два списка, а затем отсортируйте их:

>>> l1 = [1, 3, 4, 7]
>>> l2 = [0, 2, 5, 6, 8, 9]
>>> l1.extend(l2)
>>> sorted(l1)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

..или короче (и без модификации l1):

>>> sorted(l1 + l2)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

..легкий! Кроме того, он использует только две встроенные функции, поэтому при условии, что списки имеют разумный размер, это должно быть быстрее, чем реализация сортировки / слияния в цикле. Что еще более важно, в приведенном выше коде гораздо меньше кода и он очень удобен для чтения.

Если ваши списки большие (более нескольких сотен тысяч, я полагаю), может быть быстрее использовать альтернативный / настраиваемый метод сортировки, но, вероятно, сначала нужно сделать другие оптимизации (например, не хранить миллионы объектов datetime)

Используя timeit.Timer().repeat() (который повторяет функции 1000000 раз), я произвольно сравнил его с решением ghoseb's, и sorted(l1+l2) значительно быстрее:

merge_sorted_lists взял ..

[9.7439379692077637, 9.8844599723815918, 9.552299976348877]

sorted(l1+l2) взял ..

[2.860386848449707, 2.7589840888977051, 2.7682540416717529]

В основном это связано с недостатком решения ghoseb - на самом деле это O (n ** 2), поэтому будет работать хуже, чем сортировка O (n lg (n)). Слияние O (n), вероятно, будет быстрее, чем сортировка, по крайней мере, для достаточно большого входного списка (сортировка вполне может превзойти ее для коротких списков).

Brian 21.01.2009 12:57

Наконец, разумный ответ с учетом фактического сравнительный анализ. :-) --- Кроме того, предпочтительнее поддерживать 1 строку вместо 15-20.

Deestan 21.01.2009 13:10

Сортировка очень короткого списка, созданного путем добавления двух списков, действительно будет очень быстрой, поскольку будут преобладать постоянные накладные расходы. Попробуйте сделать это для списков с несколькими миллионами элементов или файлов на диске с несколькими миллиардами элементов, и вскоре вы поймете, почему слияние предпочтительнее.

Barry Kelly 21.01.2009 13:16

@Barry: Если у вас есть «несколько миллиардов элементов» и требуется скорость, что-нибудь в Python - неправильный ответ.

Deestan 21.01.2009 13:24

Собственно, я сделал некоторые тайминги (см. Мой ответ). sorted () действительно работает быстрее в большинстве случаев (менее миллиона элементов). Это, очевидно, изменится с более эффективной реализацией (например, написанной на C) или там, где операции требуют больше времени (например, ввод-вывод на диске)

Brian 21.01.2009 13:39

@Deestan: Я не согласен - бывают случаи, когда на скорость влияют другие факторы. Например. если вы сортируете данные на диске (объединяете 2 файла), время ввода-вывода, вероятно, будет преобладать, и скорость python не будет иметь большого значения, просто количество операций, которые вы выполняете (и, следовательно, алгоритм).

Brian 21.01.2009 13:41

Исходный вопрос гласит, что «каждый список [содержит элементы] типа datetime» .. Я бы побеспокоился об этом задолго до того, как я заново внедряю алгоритм сортировки!

dbr 21.01.2009 14:34

Не забудьте либо передать функцию, которая может сравнивать даты в объекте, либо переопределить метод cmp в объекте. Это то же самое решение, которое я дал ниже (хотя ваши тесты очень приветствуются).

Josh Smeaton 21.01.2009 14:53

Шутки в сторону? Тестирование функции сортировки со списком из 10 записей ??

Seun Osewa 12.01.2010 03:21

Сеун: В ответе не указано, насколько велик список - вполне вероятно, что спрашивающий (или случайный гугльщик) захотел бы отсортировать довольно маленькие списки .. Тест не совсем суть моего ответа, я включил это потому, что глупо утверждать, что «x быстрее, чем y», вообще не предоставляя никаких тестов.

dbr 12.01.2010 23:47

Поскольку python использует timsort, я бы ожидал, что всегда будет быстрее, поскольку он выполняет слияние, как и вы вручную, но вместо этого в c.

Chris Cain 15.04.2012 21:26

Для получения полезных тестов сравните sorted с heapq.merge для большего количества итераций (и большего количества итераций), а также с пользовательскими функциями key.

OrangeDog 20.06.2018 17:11

@Deestan, если у вас есть итерации на несколько миллиардов элементов, тогда нет ничего плохого в использовании Python, хотя использование конкатенации списков и sorted является большой ошибкой.

OrangeDog 20.06.2018 17:13

В решении ghoseb's есть небольшая ошибка, из-за которой оно составляет O (n ** 2), а не O (n). Проблема в том, что это выполняет:

item = l1.pop(0)

Со связанными списками или deques это будет операция 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)

    # l2 will contain last element.
    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 раз):

# items:  1000   10000 100000 1000000
merge  :  0.079  0.798 9.763  109.044 
sort   :  0.020  0.217 5.948  106.882

Фактически, похоже, что dbr прав, просто использование sorted () предпочтительнее, если вы не ожидаете очень больших списков, хотя он имеет худшую алгоритмическую сложность. Точка безубыточности составляет около миллиона элементов в каждом исходном списке (всего 2 миллиона).

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

[Редактировать] Я повторил это с ситуацией, более близкой к вопросу - используя список объектов, содержащий поле «date», которое является объектом datetime. Вышеупомянутый алгоритм был изменен для сравнения с .date, а метод сортировки был изменен на:

return sorted(l1 + l2, key=operator.attrgetter('date'))

Это немного меняет ситуацию. Более дорогое сравнение означает, что число, которое мы выполняем, становится более важным по сравнению с постоянной скоростью реализации. Это означает, что слияние компенсирует потерянные позиции, вместо этого превосходя метод sort () на 100 000 элементов. Сравнение на основе еще более сложного объекта (например, больших строк или списков), вероятно, еще больше изменит этот баланс.

# items:  1000   10000 100000  1000000[1]
merge  :  0.161  2.034 23.370  253.68
sort   :  0.111  1.523 25.223  313.20

[1]: Примечание: на самом деле я сделал только 10 повторов для 1 000 000 элементов и соответственно увеличил масштаб, так как это было довольно медленно.

Спасибо за исправление. Было бы здорово, если бы вы могли точно указать на недостаток и свое исправление :)

Baishampayan Ghose 21.01.2009 14:24

@ghoseb: Я дал краткое описание в качестве комментария к вашему сообщению, но теперь я обновил ответ, чтобы дать более подробную информацию - по сути, l.pop () - это операция O (n) для списков. Это можно исправить, отслеживая положение каким-либо другим способом (в качестве альтернативы, вместо этого выскакивая из хвоста и реверсируя в конце)

Brian 21.01.2009 14:42

Можете ли вы провести сравнительный анализ этих же тестов, но сравнивая даты, как того требует вопрос? Я предполагаю, что этот дополнительный метод займет относительно много времени.

Josh Smeaton 21.01.2009 14:57

Я бы сказал, что разница связана с тем, что sort () реализован в c / C++ и скомпилирован против нашего интерпретируемого merge (). merge () должен быть быстрее на равных условиях.

Drakosha 21.01.2009 15:09

Хороший момент Дракоша. Покажите, что сравнительный анализ - действительно единственный способ узнать наверняка.

Josh Smeaton 21.01.2009 15:13

@Josh: Хороший момент - я повторно запустил тайминги, используя код, предполагающий объект с атрибутом datetime. Это действительно влияет на точку, в которой слияние превосходит sort ().

Brian 21.01.2009 16:02

is there a smarter way to do this in Python

Об этом не упоминалось, поэтому я продолжу - в модуле heapq Python 2.6+ есть функция слияния stdlib. Если все, что вам нужно, - это делать что-то, это может быть лучшей идеей. Конечно, если вы хотите реализовать свое собственное, объединение сортировки слиянием - лучший вариант.

>>> list1 = [1, 5, 8, 10, 50]
>>> list2 = [3, 4, 29, 41, 45, 49]
>>> from heapq import merge
>>> list(merge(list1, list2))
[1, 3, 4, 5, 8, 10, 29, 41, 45, 49, 50]

Вот документация.

Я добавил ссылку на heapq.py. merge() реализован как чистая функция Python, поэтому ее легко перенести на более старые версии Python.

jfs 21.01.2009 23:36

Хотя это и верно, это решение кажется на порядок медленнее, чем решение sorted(l1+l2).

Ale 02.09.2017 13:43

@Ale: Это не совсем удивительно. list.sort (в терминах которого реализован sorted) использует TimSort, который оптимизирован для использования преимуществ существующего порядка (или обратного порядка) в базовой последовательности, поэтому, хотя теоретически это O(n log n), в этом случае он намного ближе к O(n) для выполнения сорт. Помимо этого, list.sort CPython реализован на C (без дополнительных затрат на интерпретатор), тогда как heapq.merge в основном реализован на Python и оптимизируется для случая «многих итераций» таким образом, что замедляет случай «двух итераций».

ShadowRanger 24.03.2018 03:43

Преимущество heapq.merge в том, что он не требует, чтобы входы или выходы были list; он может потреблять итераторы / генераторы и создавать генератор, поэтому огромные входы / выходы (не сохраняемые сразу в ОЗУ) могут быть объединены без перебоев подкачки. Он также обрабатывает слияние произвольного количества итераций ввода с меньшими накладными расходами, чем можно было бы ожидать (он использует кучу для координации слияния, поэтому накладные расходы масштабируются с логарифмом количества итераций, не линейно, но, как уже отмечалось, это не ' t имеет значение для случая "двух итераций").

ShadowRanger 24.03.2018 03:51
from datetime import datetime
from itertools import chain
from operator import attrgetter

class DT:
    def __init__(self, dt):
        self.dt = dt

list1 = [DT(datetime(2008, 12, 5, 2)),
         DT(datetime(2009, 1, 1, 13)),
         DT(datetime(2009, 1, 3, 5))]

list2 = [DT(datetime(2008, 12, 31, 23)),
         DT(datetime(2009, 1, 2, 12)),
         DT(datetime(2009, 1, 4, 15))]

list3 = sorted(chain(list1, list2), key=attrgetter('dt'))
for item in list3:
    print item.dt

Выход:

2008-12-05 02:00:00
2008-12-31 23:00:00
2009-01-01 13:00:00
2009-01-02 12:00:00
2009-01-03 05:00:00
2009-01-04 15:00:00

Бьюсь об заклад, это быстрее, чем любой из причудливых алгоритмов слияния на чистом Python, даже для больших данных. heapq.merge Python 2.6 - это совсем другая история.

Короче говоря, если только len(l1 + l2) ~ 1000000 не использует:

L = l1 + l2
L.sort()

merge vs. sort comparison

Описание рисунка и исходный код можно найти здесь.

Рисунок был сгенерирован следующей командой:

$ python make-figures.py --nsublists 2 --maxn=0x100000 -s merge_funcs.merge_26 -s merge_funcs.sort_builtin

Вы сравниваете это с решением для гольфа, а не с тем, которое на самом деле пытается быть эффективным.

OrangeDog 20.06.2018 16:47

@OrangeDog Я не понимаю, о чем вы говорите. Суть ответа в том, что добавление двух списков и их сортировка может быть быстрее для небольшого ввода, чем heapq.merge () из Python 2.6 (несмотря на то, что merge() имеет значение O (n) во времени, O (1) в пространстве, а сортировка - O (n log n) по времени, и весь алгоритм здесь O (n) в пространстве) ¶ Сравнение теперь имеет только историческую ценность.

jfs 20.06.2018 17:30

этот ответ не имеет ничего общего с heapq.merge, вы сравниваете sort с чьим-то кодом-гольфом.

OrangeDog 20.06.2018 17:32

@OrangeDog неправильный. merge_26() взят из модуля heapq Python 2.6.

jfs 20.06.2018 17:39

вы тот, кто сказал «исходный код можно найти здесь» и связался с ответом code-golf. Не обвиняйте людей в том, что они думают, что код, который можно найти там, - это то, что вы тестировали.

OrangeDog 20.06.2018 17:49

@OrangeDog: внимательно прочтите связанный ответ. merge_26() и merge_alabaster() - разные функции.

jfs 20.06.2018 17:55

Рекурсивная реализация ниже. Средняя производительность - O (n).

def merge_sorted_lists(A, B, sorted_list = None):
    if sorted_list == None:
        sorted_list = []

    slice_index = 0
    for element in A:
        if element <= B[0]:
            sorted_list.append(element)
            slice_index += 1
        else:
            return merge_sorted_lists(B, A[slice_index:], sorted_list)

    return sorted_list + B

или генератор с улучшенной пространственной сложностью:

def merge_sorted_lists_as_generator(A, B):
    slice_index = 0
    for element in A:
        if element <= B[0]:
            slice_index += 1
            yield element       
        else:
            for sorted_element in merge_sorted_lists_as_generator(B, A[slice_index:]):
                yield sorted_element
            return        

    for element in B:
        yield element

Реализация сортировки Python "timsort" специально оптимизирована для списков, содержащих упорядоченные разделы. Кроме того, он написан на C.

http://bugs.python.org/file4451/timort.txt
http://en.wikipedia.org/wiki/Timsort

Как уже упоминалось, функция сравнения может вызывать функцию сравнения несколько раз с некоторым постоянным коэффициентом (но, возможно, во многих случаях вызывает ее больше раз за более короткий период!).

I would never rely on this, however. – Daniel Nadasi

Я считаю, что разработчики Python стремятся сохранить временную сортировку или, по крайней мере, сохранить сортировку, равную O (n) в этом случае.

Generalized sorting (i.e. leaving apart radix sorts from limited value domains)
cannot be done in less than O(n log n) on a serial machine. – Barry Kelly

Да, сортировка в общем случае не может быть быстрее. Но поскольку O () является верхней границей, то значение timsort, равное O (n log n) на произвольном вводе, не противоречит тому, что O (n) задано sorted (L1) + sorted (L2).

Если вы хотите сделать это более согласованным с изучением того, что происходит в итерации, попробуйте это

def merge_arrays(a, b):
    l= []

    while len(a) > 0 and len(b)>0:
        if a[0] < b[0]: l.append(a.pop(0))    
        else:l.append(b.pop(0))

    l.extend(a+b)
    print( l )

pop (0) является линейным, поэтому эта версия случайно квадратичная

Allen Downey 16.11.2017 18:20
import random

    n=int(input("Enter size of table 1")); #size of list 1
    m=int(input("Enter size of table 2")); # size of list 2
    tb1=[random.randrange(1,101,1) for _ in range(n)] # filling the list with random
    tb2=[random.randrange(1,101,1) for _ in range(m)] # numbers between 1 and 100
    tb1.sort(); #sort the list 1 
    tb2.sort(); # sort the list 2
    fus=[]; # creat an empty list
    print(tb1); # print the list 1
    print('------------------------------------');
    print(tb2); # print the list 2
    print('------------------------------------');
    i=0;j=0;  # varialbles to cross the list
    while(i<n and j<m):
        if (tb1[i]<tb2[j]):
            fus.append(tb1[i]); 
            i+=1;
        else:
            fus.append(tb2[j]);
            j+=1;

    if (i<n):
        fus+=tb1[i:n];
    if (j<m):
        fus+=tb2[j:m];

    print(fus);

  # this code is used to merge two sorted lists in one sorted list (FUS) without
  #sorting the (FUS)

Неясно, является ли это ответом на вопрос, не говоря уже о том, действительно ли это? Можете ли вы дать какое-то объяснение?

Ben 30.08.2014 16:00

Извините, но я не понял, что вы хотите!

Oussama Ďj Sbaa 30.08.2014 16:10

Вы заметите, что ответы с более высокими оценками (и большинство других) содержат текст, объясняющий, что происходит в ответе, и Зачем этот ответ является ответом на вопрос ..

Ben 30.08.2014 16:11

потому что он объединяет два списка в один отсортированный список, и это ответ на вопрос

Oussama Ďj Sbaa 30.08.2014 16:19

Использовали шаг слияния сортировки слияния. Но я использовал генераторы. Сложность времениНа)

def merge(lst1,lst2):
    len1=len(lst1)
    len2=len(lst2)
    i,j=0,0
    while(i<len1 and j<len2):
        if (lst1[i]<lst2[j]):
                yield lst1[i]
                i+=1
        else:
                yield lst2[j]
                j+=1
    if (i==len1):
        while(j<len2):
                yield lst2[j]
                j+=1
    elif (j==len2):
        while(i<len1):
                yield lst1[i]
                i+=1
l1=[1,3,5,7]
l2=[2,4,6,8,9]
mergelst=(val for val in merge(l1,l2))
print(*mergelst)
def merge_sort(a,b):

    pa = 0
    pb = 0
    result = []

    while pa < len(a) and pb < len(b):
        if a[pa] <= b[pb]:
            result.append(a[pa])
            pa += 1
        else:
            result.append(b[pb])
            pb += 1

    remained = a[pa:] + b[pb:]
    result.extend(remained)


return result

Реализация этапа слияния в сортировке слиянием, который выполняет итерацию по обоим спискам:

def merge_lists(L1, L2):
    """
    L1, L2: sorted lists of numbers, one of them could be empty.

    returns a merged and sorted list of L1 and L2.
    """

    # When one of them is an empty list, returns the other list
    if not L1:
        return L2
    elif not L2:
        return L1

    result = []
    i = 0
    j = 0

    for k in range(len(L1) + len(L2)):
        if L1[i] <= L2[j]:
            result.append(L1[i])
            if i < len(L1) - 1:
                i += 1
            else:
                result += L2[j:]  # When the last element in L1 is reached,
                break             # append the rest of L2 to result.
        else:
            result.append(L2[j])
            if j < len(L2) - 1:
                j += 1
            else:
                result += L1[i:]  # When the last element in L2 is reached,
                break             # append the rest of L1 to result.

    return result

L1 = [1, 3, 5]
L2 = [2, 4, 6, 8]
merge_lists(L1, L2)               # Should return [1, 2, 3, 4, 5, 6, 8]
merge_lists([], L1)               # Should return [1, 3, 5]

Я все еще изучаю алгоритмы, пожалуйста, дайте мне знать, можно ли улучшить код в каком-либо аспекте, ваши отзывы приветствуются, спасибо!

Это мое решение в линейное время без редактирования l1 и l2:

def merge(l1, l2):
  m, m2 = len(l1), len(l2)
  newList = []
  l, r = 0, 0
  while l < m and r < m2:
    if l1[l] < l2[r]:
      newList.append(l1[l])
      l += 1
    else:
      newList.append(l2[r])
      r += 1
  return newList + l1[l:] + l2[r:]

Надеюсь это поможет. Довольно просто и понятно:

l1 = [1, 3, 4, 7]

l2 = [0, 2, 5, 6, 8, 9]

l3 = l1 + l2

l3.sort ()

печать (l3)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

OP не спрашивал, как добавлять и сортировать списки, спрашивал, есть ли лучший или более «Python» способ сделать это в их контексте.

ForeverZer0 27.06.2018 05:22

Этот код имеет временную сложность O (n) и может объединять списки любого типа данных, учитывая количественную функцию в качестве параметра func. Он создает новый объединенный список и не изменяет ни один из списков, переданных в качестве аргументов.

def merge_sorted_lists(listA,listB,func):
    merged = list()
    iA = 0
    iB = 0
    while True:
        hasA = iA < len(listA)
        hasB = iB < len(listB)
        if not hasA and not hasB:
            break
        valA = None if not hasA else listA[iA]
        valB = None if not hasB else listB[iB]
        a = None if not hasA else func(valA)
        b = None if not hasB else func(valB)
        if (not hasB or a<b) and hasA:
            merged.append(valA)
            iA += 1
        elif hasB:
            merged.append(valB)
            iB += 1
    return merged

в сложности O(m+n)

def merge_sorted_list(nums1: list, nums2:list) -> list:
        m = len(nums1)
        n = len(nums2)
        
        nums1 = nums1.copy()
        nums2 = nums2.copy()
        nums1.extend([0 for i in range(n)])
        while m > 0 and n > 0:
            if nums1[m-1] >= nums2[n-1]:
                nums1[m+n-1] = nums1[m-1]
                m -= 1
            else:
                nums1[m+n-1] = nums2[n-1]
                n -= 1
        if n > 0:
            nums1[:n] = nums2[:n]
        return nums1

l1 = [1, 3, 4, 7]    
l2 =  [0, 2, 5, 6, 8, 9]    
print(merge_sorted_list(l1, l2))

выход

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

если у вас небольшой список, просто объедините их и отсортируйте с помощью встроенной функции, например:

def in_built_sort(list1, list2):
    // time complexity : O(nlog n) 
    return sorted(list1 + list2)

но если у вас есть МИЛЛИАРД или МИЛЛИАРД элементов в обоих списках, используйте этот метод:

def custom_sort(list1, list2):
    // time complexity : O(n)
    merged_list_size = len(list1) + len(list2)
    merged_list = [None] * merged_list_size

    current_index_list1 = 0
    current_index_list2 = 0
    current_index_merged = 0
    while current_index_merged < merged_list_size:
        if (not current_index_list1 >= len(list1) and (current_index_list2 >= len(list2) or list1[current_index_list1] < list2[current_index_list2])):
            merged_list[current_index_merged] = list1[current_index_list1]
            current_index_list1 += 1
        else:
            merged_list[current_index_merged] = list2[current_index_list2]
            current_index_list2 += 1

        current_index_merged += 1
    return merged_list

Тест производительности: протестировано против 10 миллионов случайных элементов в обоих списках и повторено 10 раз.

print(timeit.timeit(stmt=in_built_sort, number=10))  // 250.44088469999997  O(nlog n)   
print(timeit.timeit(stmt=custom_sort, number=10)) // 125.82338159999999  O(n)

Другие вопросы по теме