2009-07-21 4 views
2

В комментариях к this answer приведена идея, что преобразование простого связанного списка может быть выполнено только в O (nlog (n)), а не в O (n) времени.Есть ли алгоритм O (nlog (n)) для инвертирования простого связанного списка?

Это определенно неверно - инверсия O (n) не является проблемой - просто пройдите по списку и измените указатели, когда вы идете. Требуются три временных указателя - это постоянная дополнительная память.

Я полностью понимаю, что O (nlog (n)) хуже (медленнее), чем O (n).

Но из любопытства - что может быть алгоритмом O (nlog (n)) для инверсии просто связанного списка? Предпочтительным является алгоритм с постоянной дополнительной памятью.

+13

ли как О (п), а на 'i'th узел, do' log (i) 'nops. –

+9

Как быстрый педантичный nitpick: ваш алгоритм 'O (n)' ** является алгоритмом 'O (n logN)', поэтому один ответ на ваш вопрос - это немодифицированный алгоритм O (n). 'O (n log n)' - это набор алгоритмов, которые растут не быстрее, чем n log n, и включают в себя те, которые являются «O (n)» и даже «O (1)». Чтобы ограничить те в тех же асимптотических границах в обоих направлениях, вам нужно сказать 'Θ (n)'. (На практике большинство людей на самом деле означает 'Θ (n)', когда говорят «O (n)») – Brian

ответ

11

Я думаю, вы смущены. Вы говорите O (n log (n)), что на самом деле хуже, чем O (n). Возможно, вы имеете в виду O (log n)? Если это так, ответ - нет. Вы не можете инвертировать связанный список в O (log n). O (n) тривиально (и очевидное решение). O (n log (n)) не имеет большого смысла.

Редактировать: Итак, вы имеете в виду O (n log (n)). Тогда ответ «да». Как? Легко. Вы сортируете:

  1. Подсчитайте длину списка. Стоимость: O (n);
  2. Создайте массив такого же размера;
  3. Скопируйте элементы связанного списка в массив в случайный заказ, положив первоначальный заказ как часть элемента. Например: [A, B, C] -> [(B, 2), (C, 3), (A, 1)]. Стоимость: O (n);
  4. Сортируйте массив, используя эффективную сортировку (например, быструю сортировку) в инвертированном исходном порядке, например [(C, 3), (B, 2), (A, 1)]. Стоимость: O (n log (n));
  5. Создайте связанный список из реверсивного массива. Стоимость: O (n).

Общая стоимость: O (п журнал (п))

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

Редактировать 2: Я изначально не поместил элементы списка в произвольном порядке, но понял, что вы можете утверждать, что эффективный сортировка по уже отсортированному списку была меньше O (n log (n)), даже если вы реверсировали его. Теперь я не полностью убежден, что это так, но вышеупомянутая редакция устраняет эту потенциальную критику.

И да, это патологический вопрос (и ответ). Конечно, вы можете сделать это в O (n).

+0

Нет, я спрашиваю именно о худшем алгоритме. – sharptooth

+3

Он знает, что это еще хуже, вот и весь вопрос. Он просто хочет знать, есть ли алгоритм для выполнения инверсии, который является O (n log n), но на первый взгляд не выглядит глупо. – balpha

1

Ну ...

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

Это очень неэффективно, но я считаю, что это O (nlog (n)) ...

Что-то вроде следующего в псевдокоде (если у вас есть len функция, которая возвращает длину списка в O (N) и sub_list(list, start_id, end_id) функцию, которая возвращает подмножество списка, начиная с START_ID и заканчивая end_id в O (N)):

function invert(list) 

    if len(list) == 1 return list 

    if len(list) == 2: 
     new_list = list.next 
     new_list.next = list 
     return new_list 

    middle_of_list = len(list)/2 <-- integer division 

    first_half = invert (sub_list(list, 1, middle_of_list)) 
    second_half = invert (sub_list(list, middle_of_list+2, len(list)) 

    first_half.next = second_half 

    return first_half 
7

Каждый алгоритм O (n) также является O (n log n), поэтому ответ да.

+0

Я думаю, мы все поняли, что OP означает «O (n log n), но не O (n)». Во всяком случае, поскольку с самого начала вопрос патологический, и вы правы, +1. – balpha

+0

@balpha: Патологические вопросы рисовать патологические nitpickers :) –

+0

+1. Это правильный ответ на этот вопрос, но я считаю, что OP означает Big-Theta (n * log * n), а не Big-Oh. –

1

Тупой идея, но О (п войти п), а не О (п)

  • Присвоить каждый элемент списка уникальный ID. Идентификатор каждого преемника должен быть больше идентификатора элемента (O (n))
  • Сортировка элементов по возрастанию с использованием ключа id как ключа с использованием любого алгоритма сортировки на основе сравнения (O (n log n))
  • Создайте новый список, используя, выдаваемой сортировка элементов (O (N))
0

Если вы педантичным, то этот алгоритм O (N журнал N), так как указатели имеют размер не менее log n и должны быть назначены n раз.

Но на самом деле машины имеют фиксированный размер слова, поэтому это обычно не принимается во внимание.

0

Если вопрос действительно для алгоритма Ω (nlgn), возможно, этот чрезмерно сложный алгоритм будет делать?

  • построить сбалансированное дерево структуры из связанного списка
  • каждый лист содержит как исходное значение из связанного списка и номер индекса значения в связанном списке; использовать индекс как дерево ключа
  • траверс дерева, чтобы сообщить все листья в порядке, обратном первоначальному индексу
  • создать новый связанный список из сопоставления значений