Этот вопрос может быть старым, но я не мог придумать ответа.
Скажем, есть два списка разной длины, сливающиеся в одной точке ; как мы узнаем, где находится точка слияния?
Условия:
- Мы не знаем длины
- Мы должны анализировать каждый список только один раз.
Ответы:
Если
следующий алгоритм был бы решением.
Во-первых, цифры. Предположим, что первый список имеет длину,
a+c
а второй - длинуb+c
, гдеc
- длина их общего «хвоста» (после точки слияния). Обозначим их так:Так как мы не знаем длины, будем рассчитывать
x
иy
без дополнительных итераций; вы увидите как.Затем мы перебираем каждый список и меняем их местами во время итерации! Если оба итератора достигают точки слияния одновременно, мы узнаем это путем простого сравнения. В противном случае один указатель достигнет точки слияния раньше другого.
После этого, когда другой итератор достигнет точки слияния, он не перейдет к общему хвосту. Вместо этого вернется к прежнему началу списка, который до этого достигал точки слияния! Таким образом, прежде чем он достигнет конца измененного списка (то есть бывшего начала другого списка), он выполнит
a+b+1
общее количество итераций. Назовем этоz+1
.Указатель, который первым достиг точки слияния, будет продолжать повторяться, пока не достигнет конца списка. Количество сделанных итераций должно быть рассчитано и равно
x
.Затем этот указатель выполняет итерацию назад и снова меняет списки. Но теперь он не вернется в начало списка, с которого изначально начинал! Вместо этого он перейдет в начало другого списка! Количество сделанных итераций должно быть рассчитано и равно
y
.Итак, нам известны следующие числа:
Из чего определяем, что
Что решает проблему.
источник
Следующее, безусловно, лучшее из всего, что я видел - O (N), без счетчиков. Я получил его во время собеседования с кандидатом в SN в VisionMap .
Сделайте такой интерактивный указатель: он каждый раз продвигается вперед до конца, а затем переходит в начало противоположного списка и так далее. Создайте два из них, указывая на две головы. Передвигайте каждый из указателей на 1 каждый раз, пока они не встретятся. Это произойдет после одного или двух проходов.
Я до сих пор использую этот вопрос в интервью - но чтобы посмотреть, сколько времени нужно, чтобы кто-то понял, почему это решение работает.
источник
a-b-c-x-y-z
иp-q-x-y-z
. путь первого указателяa,b,c,x,y,z,p,q,x
, путь второго указателяp,q,x,y,z,a,b,c,x
Ответ Павла требует модификации списков, а также повторения каждого списка дважды.
Вот решение, которое требует только итерации каждого списка дважды (первый раз для расчета их длины; если длина указана, вам нужно выполнить итерацию только один раз).
Идея состоит в том, чтобы игнорировать начальные записи более длинного списка (точка слияния не может быть там), чтобы два указателя находились на одинаковом расстоянии от конца списка. Затем переместите их вперед, пока они не сольются.
Это асимптотически то же самое (линейное время), что и мой другой ответ, но, вероятно, имеет меньшие константы, поэтому, вероятно, быстрее. Но я думаю, что мой другой ответ круче.
источник
Хорошо, если вы знаете, что они сольются:
Допустим, вы начинаете с:
1) Пройдите по первому списку, устанавливая каждый следующий указатель на NULL.
Теперь у вас есть:
2) Теперь просмотрите второй список и подождите, пока вы не увидите NULL, это ваша точка слияния.
Если вы не можете быть уверены, что они сливаются, вы можете использовать контрольное значение для значения указателя, но это не так элегантно.
источник
Если бы мы могли повторять списки ровно дважды, тогда я мог бы предоставить метод для определения точки слияния:
источник
Вот решение, быстрое в вычислительном отношении (повторяет каждый список один раз), но использует много памяти:
источник
Вы можете использовать набор узлов. Просмотрите один список и добавьте каждый узел в набор. Затем выполните итерацию по второму списку и для каждой итерации проверьте, существует ли узел в наборе. Если да, то вы нашли свою точку слияния :)
источник
Это, возможно, нарушает условие «анализировать каждый список только один раз», но реализует алгоритм черепахи и зайца (используемый для определения точки слияния и длины цикла циклического списка), поэтому вы начинаете со списка A, а когда вы достигаете NULL в end вы делаете вид, что это указатель на начало списка B, тем самым создавая видимость циклического списка. Затем алгоритм сообщит вам, насколько далеко вниз по списку А находится слияние (переменная «mu» согласно описанию в Википедии).
Кроме того, значение «лямбда» сообщает вам длину списка B, и, если вы хотите, вы можете определить длину списка A во время алгоритма (когда вы перенаправляете ссылку NULL).
источник
Может быть, я слишком упрощаю это, а просто перебираю наименьший список и использую последние узлы
Link
как точку слияния?Итак, где
Data->Link->Link == NULL
находится конечная точка, указываетсяData->Link
как точка слияния (в конце списка).РЕДАКТИРОВАТЬ:
Хорошо, судя по опубликованной вами картинке, вы разбираете два списка, сначала самый маленький. С наименьшим списком вы можете поддерживать ссылки на следующий узел. Теперь, когда вы анализируете второй список, вы сравниваете ссылку, чтобы найти, где Reference [i] является ссылкой в LinkedList [i] -> Link. Это даст точку слияния. Пора объяснять картинками (наложите значения на картинку ОП).
У вас есть связанный список (ссылки показаны ниже):
A->B->C->D->E
У вас есть второй связанный список:
1->2->
В объединенном списке ссылки будут выглядеть следующим образом:
1->2->D->E->
Поэтому вы сопоставляете первый «меньший» список (поскольку объединенный список, который мы считаем, имеет длину 4, а основной список 5)
Прокрутите первый список, сохраните ссылку на ссылки.
Список будет содержать следующие ссылки
Pointers { 1, 2, D, E }
.Теперь пройдемся по второму списку:
Конечно, вы ведете новый список указателей, но это не выходит за рамки спецификации. Однако первый список анализируется ровно один раз, а второй список будет полностью проанализирован только при отсутствии точки слияния. В противном случае он закончится раньше (в точке слияния).
источник
Я протестировал случай слияния на моем FC9 x86_64 и распечатал адрес каждого узла, как показано ниже:
Обратите внимание, потому что я выровнял структуру узла, поэтому, когда malloc () узел, адрес выравнивается по 16 байтам, см. Минимум 4 бита. Младшие биты - это 0, то есть 0x0 или 000b. Поэтому, если вы находитесь в том же особом случае (выровненный адрес узла), вы можете использовать эти минимум 4 бита. Например, при перемещении обоих списков от начала до конца установите 1 или 2 из 4 бит адреса посещающего узла, то есть установите флаг;
Обратите внимание, что флаги не влияют на реальный адрес узла, а влияют только на значение указателя вашего СОХРАНЕННОГО узла.
Если обнаружено, что кто-то установил бит (ы) флага, то первый найденный узел должен быть точкой слияния. после этого вы восстановите адрес узла, очистив установленные вами биты флага. при этом важно соблюдать осторожность при выполнении итерации (например, node = node-> next) для очистки. помните, что вы установили биты флагов, так что сделайте это
Поскольку это предложение восстановит измененные адреса узлов, его можно рассматривать как «без изменений».
источник
Может быть простое решение, но для этого потребуется дополнительное пространство. Идея состоит в том, чтобы пройти по списку и сохранить каждый адрес в хэш-карте, а теперь пройти по другому списку и сопоставить, находится ли адрес в хэш-карте или нет. Каждый список просматривается только один раз. Никаких изменений в списках нет. Длина пока неизвестна. Используемое вспомогательное пространство: O (n), где n - длина первого пройденного списка.
источник
это решение выполняет итерацию каждого списка только один раз ... никаких изменений списка тоже не требуется ... хотя вы можете жаловаться на пространство ..
1) В основном вы выполняете итерацию в list1 и сохраняете адрес каждого узла в массиве (который хранит значение unsigned int)
2) Затем вы выполняете итерацию list2, и для каждого адреса узла ---> вы просматриваете массив, который вы найдете совпадение или нет ... если вы это сделаете, то это будет узел слияния
Надеюсь, это верное решение ...
источник
Нет необходимости изменять какой-либо список. Есть решение, в котором нам нужно пройти по каждому списку только один раз.
источник
источник
Вот наивное решение, не нужно просматривать целые списки.
если ваш структурированный узел имеет три поля, например
скажем, у вас есть две головы (head1 и head2), указывающие на начало двух списков.
Просмотрите оба списка с одинаковой скоростью и установите флаг = 1 (флаг посещенных) для этого узла,
источник
Как насчет этого:
Если вам разрешено проходить каждый список только один раз, вы можете создать новый узел, пройти по первому списку, чтобы каждый узел указывал на этот новый узел, и пройти по второму списку, чтобы увидеть, указывает ли какой-либо узел на ваш новый узел ( это ваша точка слияния). Если второй обход не приводит к вашему новому узлу, то исходные списки не имеют точки слияния.
Если вам разрешено перемещаться по спискам более одного раза, вы можете пройти по каждому списку, чтобы найти их длину, и если они различаются, опустите «лишние» узлы в начале более длинного списка. Затем просто просмотрите оба списка по одному шагу за раз и найдите первый узел слияния.
источник
Шаги в Java:
источник
Мы можем эффективно решить эту проблему, введя поле isVisited. Просмотрите первый список и установите значение "isVisited" равным "true" для всех узлов до конца. Теперь начните со второго и найдите первый узел, где флаг истинен, а Boom - это ваша точка слияния.
источник
Шаг 1: найдите длину обоих списков Шаг 2: Найдите разницу и переместите самый большой список с разницей Шаг 3: Теперь оба списка будут в аналогичном положении. Шаг 4. Просмотрите список, чтобы найти точку слияния
источник
источник
Используйте карту или словарь, чтобы сохранить адрес и значение узла. если адрес уже существует в карте / словаре, то ответом является значение ключа. Я сделал это:
источник
Решение сложности АО (п). Но исходя из предположения.
предположение: оба узла имеют только положительные целые числа.
логика: сделать все целое число list1 отрицательным. Затем пройдите по списку 2, пока не получите отрицательное целое число. Найдя => возьми, снова поменяй знак на положительный и возвращайся.
источник
Мы можем использовать два указателя и перемещаться таким образом, чтобы, если один из указателей имеет значение NULL, мы указываем его на заголовок другого списка и то же самое для другого, таким образом, если длины списков различаются, они встретятся во втором проходе. . Если длина list1 равна n, а list2 равна m, их разница составляет d = abs (nm). Они преодолеют это расстояние и встретятся в точке слияния.
Код:
источник
Вы можете добавить узлы
list1
в хэш-набор и цикл через второй, и если какой-либо узелlist2
уже присутствует в наборе. Если да, то это узел слиянияисточник
Решение с использованием javascript
источник
Если разрешено редактирование связанного списка,
источник