Проверьте, объединяются ли два связанных списка. Если так, где?

Не могли бы вы опубликовать пример вашего кода и массива объектов. Это поможет нам понять проблему, чтобы мы могли ее решить.

Вы также можете посмотреть на пост ниже. Это может помочь вашему делу.

Разница между Array.CopyTo () и Array.CloneTo ()

РЕДАКТИРОВАТЬ:

    var foo = new String[] {"ab" , "cd", "ef"} ;
    var bar = new List();

    foreach(var item in foo)
    {
        bar.Add(item);
    }
    var barArray = bar.ToArray();

    for(int i = 0; i < foo.Length; i++)
    {
        Console.WriteLine(foo[i]);
        Console.WriteLine(barArray[i]);
    }

Приведенный выше код использует список и массивы, поскольку Таким образом, вам не придется индексировать ваш дубликат массива. Вы можете запустить код в dot net fiddle, чтобы увидеть результат. В этом примере я использовал строку, пожалуйста, замените ее на свой объект.

91
задан Richard 29 May 2014 в 03:10
поделиться

9 ответов

Если

  • под «модификация не разрешена» имелось в виду «вы можете изменить, но в конце концов они должны быть восстановлены», и
  • мы могли бы точно перебирать списки дважды

следующий алгоритм был бы решением.

Во-первых, числа. Предположим, что первый список имеет длину a + c , а второй - длину b + c , где c - длина их общего «хвоста». (после точки слияния). Обозначим их следующим образом:

x = a+c
y = b+c

Поскольку длина нам неизвестна, мы вычислим x и y без дополнительных итераций; вы увидите, как это сделать.

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

После этого, когда другой итератор достигнет точки слияния, он не перейдет к общему хвосту. Вместо этого вернется к прежнему началу списка, который до этого достигал точки слияния! Таким образом, прежде чем он достигнет конца измененного списка (то есть бывшего начала другого списка), он сделает a + b + 1 итераций. Назовем его z + 1 .

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

Затем этот указатель выполняет итерацию назад и снова меняет списки. Но теперь он не вернется в начало списка, с которого изначально начинал! Вместо этого он перейдет в начало другого списка!

36
ответ дан 24 November 2019 в 06:35
поделиться
int FindMergeNode(Node headA, Node headB) {
  Node currentA = headA;
  Node currentB = headB;

  // Do till the two nodes are the same
  while (currentA != currentB) {
    // If you reached the end of one list start at the beginning of the other
    // one currentA
    if (currentA.next == null) {
      currentA = headB;
    } else {
      currentA = currentA.next;
    }
    // currentB
    if (currentB.next == null) {
      currentB = headA;
    } else {
      currentB = currentB.next;
    }
  }
  return currentB.data;
}
0
ответ дан 24 November 2019 в 06:35
поделиться

Я тестировал объединить случай на моем FC9 x86_64 и распечатать каждый адрес узла, как показано ниже:

Head A 0x7fffb2f3c4b0
0x214f010
0x214f030
0x214f050
0x214f070
0x214f090
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170


Head B 0x7fffb2f3c4a0
0x214f0b0
0x214f0d0
0x214f0f0
0x214f110
0x214f130
0x214f150
0x214f170

Обратите внимание, потому что я выровнял структуру узла, поэтому, когда malloc () узел, адрес выравнивается по 16 байтам, см. минимум 4 бита . Младшие биты - это 0, то есть 0x0 или 000b. Поэтому, если вы находитесь в том же особом случае (выровненный адрес узла), вы можете использовать эти минимум 4 бита. Например, при перемещении обоих списков от начала до конца установите 1 или 2 из 4 бит адреса посещающего узла, то есть установите флаг;

next_node = node->next;
node = (struct node*)((unsigned long)node | 0x1UL);

Примечание выше флаги не повлияют на реальный адрес узла, а только на ваш СОХРАНЕННЫЙ значение указателя узла.

Если обнаружено, что кто-то установил бит (ы) флага, то первый найденный узел должен быть точкой слияния. после этого вы восстановите адрес узла, очистив установленные вами биты флага. при этом важно соблюдать осторожность при выполнении итерации (например, node = node-> next) для очистки. помните, что вы установили биты флагов, поэтому сделайте так

real_node = (struct node*)((unsigned long)node) & ~0x1UL);
real_node = real_node->next;
node = real_node;

Поскольку это предложение восстановит измененные адреса узлов, его можно рассматривать как «без изменений».

3
ответ дан 24 November 2019 в 06:35
поделиться

Maybe I am over simplifying this, but simply iterate the smallest list and use the last nodes Link as the merging point?

So, where Data->Link->Link == NULL is the end point, giving Data->Link as the merging point (at the end of the list).

EDIT:

Okay, from the picture you posted, you parse the two lists, the smallest first. With the smallest list you can maintain the references to the following node. Now, when you parse the second list you do a comparison on the reference to find where Reference [i] is the reference at LinkedList[i]->Link. This will give the merge point. Time to explain with pictures (superimpose the values on the picture the OP).

You have a linked list (references shown below):

A->B->C->D->E

You have a second linked list:

1->2->

With the merged list, the references would then go as follows:

1->2->D->E->

Therefore, you map the first "smaller" list (as the merged list, which is what we are counting has a length of 4 and the main list 5)

Loop through the first list, maintain a reference of references.

The list will contain the following references Pointers { 1, 2, D, E }.

We now go through the second list:

-> A - Contains reference in Pointers? No, move on
-> B - Contains reference in Pointers? No, move on
-> C - Contains reference in Pointers? No, move on
-> D - Contains reference in Pointers? Yes, merge point found, break.

Sure, you maintain a new list of pointers, but thats not outside the specification. However the first list is parsed exactly once, and the second list will only be fully parsed if there is no merge point. Otherwise, it will end sooner (at the merge point).

3
ответ дан 24 November 2019 в 06:35
поделиться

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

Кроме того, значение «лямбда» сообщает вам длину списка B, и если вы хотите , вы можете определить длину списка A во время алгоритма (когда вы перенаправляете ссылку NULL).

6
ответ дан 24 November 2019 в 06:35
поделиться

Here's a solution, computationally quick (iterates each list once) but uses a lot of memory:

for each item in list a
  push pointer to item onto stack_a

for each item in list b
  push pointer to item onto stack_b

while (stack_a top == stack_b top) // where top is the item to be popped next
  pop stack_a
  pop stack_b

// values at the top of each stack are the items prior to the merged item
8
ответ дан 24 November 2019 в 06:35
поделиться

Ответ Павла требует изменения списков , а также повторения каждого списка дважды.

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

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

lenA = count(listA) //iterates list A
lenB = count(listB) //iterates list B

ptrA = listA
ptrB = listB

//now we adjust either ptrA or ptrB so that they are equally far from the end
while(lenA > lenB):
    ptrA = ptrA->next
    lenA--
while(lenB > lenA):
    prtB = ptrB->next
    lenB--

while(ptrA != NULL):
    if (ptrA == ptrB):
        return ptrA //found merge point
    ptrA = ptrA->next
    ptrB = ptrB->next

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

89
ответ дан 24 November 2019 в 06:35
поделиться

Хорошо, если вы знаете, что они будут объединены:

Допустим, вы начинаете с:

A-->B-->C
        |
        V
1-->2-->3-->4-->5

1) Просмотрите первый список, устанавливая каждый следующий указатель на NULL.

Теперь вы have:

A   B   C

1-->2-->3   4   5

2) Теперь просмотрите второй список и подождите, пока не увидите NULL, это ваша точка слияния.

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

29
ответ дан 24 November 2019 в 06:35
поделиться

If we could iterate lists exactly twice, than I can provide method for determining merge point:

  • iterate both lists and calculate lengths A and B
  • calculate difference of lengths C = |A-B|;
  • start iterating both list simultaneously, but make additional C steps on list which was greater
  • this two pointers will meet each other in the merging point
12
ответ дан 24 November 2019 в 06:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: