Объединение 3 связанных списков в 1 (Java) - PullRequest
0 голосов
/ 19 декабря 2018

У меня вопрос к окончательному обзору для класса кодирования, который я беру.Он просит объединить 3 связанных списка в 1 связанный список.Проблема, с которой я сталкиваюсь, заключается в том, что при объединении списков я могу объединить три списка в порядке возрастания, но мне не хватает последних 2 узлов 2-го списка 23 и 25. Я не могу понять, почему он на этом останавливается.Вопрос здесь:

Напишите программу с именем LinkedTest, которая:

  1. Создает три отсортированных односвязных списка целых чисел, как показано ниже

(Первый список) 2 11 19 21 24

(второй список) 14 15 18 23 25

(третий список) 3 9 17 20 22 * ​​1013 *

Объединяет три связанных списка в новый отсортированный связанный список, как показано ниже: 2 3 9 11 14 15 17 18 19 20 21 22 23 24 25 Возвращает новый отсортированный связанный список Требование: ваша программадолжно иметь временную сложность, меньшую или равную O (nlog n)

Вот мой код:

public class LinkedTest {

public static class ListNode {

    private int data;
    ListNode next;

    public ListNode(int data) {
        this.data = data;
        next = null;
    }
}

ListNode head;

public static void main(String[] args) {

    LinkedTest list = new LinkedTest();

        int[] data1 = { 2, 11, 19, 21, 24 };

        ListNode head1 = new ListNode(data1[0]);

        for (int i = 1; i < data1.length; i++)
            list.push(head1, data1[i]);

       System.out.print("First List: ");
        list.display(head1);

        int[] data2 = { 14, 15, 18, 23, 25 };

        ListNode head2 = new ListNode(data2[0]);

        for (int count = 1; count < data2.length; count++)
            list.push(head2, data2[count]);

       System.out.println(" Second List: ") ;

        list.display(head2);

        int[] data3 = { 3, 9, 17, 20, 22 };

        ListNode head3 = new ListNode(data3[0]);

        for (int count = 1; count < data3.length; count++)
            list.push(head3, data3[count]);

       System.out.println(" Third List: ") ;

        list.display(head3);

        ListNode n = list.LinkedTest(head1, head2, head3);

        System.out.print(" Merged List: ");

        list.display(n);
    }



public ListNode LinkedTest(ListNode first, ListNode second, ListNode third) { 
      ListNode head = null;

        if (first == null && second != null && third != null)

            return second;

        else if (second == null && third != null && first != null)

            return third;

        else if (third == null && first != null && second != null)

            return first;

        else if (first.data < second.data && first.data < third.data) 
        {
            head = first;
            head.next = LinkedTest(first.next, second, third);
        } 
        else if (second.data < third.data && second.data < first.data)
        {
            head = second;
            head.next = LinkedTest(first, second.next, third);
        }

        else if (third.data < first.data && third.data < second.data)
        {
            head = third;
            head.next = LinkedTest(first, second, third.next);
        }

        return head;
    }

    public void push(ListNode head, int n) 
    {
        while (head.next != null)
            head = head.next;
        head.next = new ListNode(n);
    }

    public void display(ListNode head)
    {
        ListNode tempDisplay = head; 
        while (tempDisplay != null) 
        {
            System.out.print(tempDisplay.data);
            tempDisplay = tempDisplay.next; 
    }

    }
}

Вывод:

Первый список: 211192124Второй список: 1415182325 Третий список: 39172022 Объединенный список: 23911141517181920212224

Ответы [ 4 ]

0 голосов
/ 19 декабря 2018

Зачем ограничивать себя трехсторонним слиянием?Давайте посмотрим на общий случай N-way слияния, а затем применим его к 3-way (или простой старый скучный 2-way).

// drop-in replacement for your LinkedTest(), but I like the name better:
// ListNode n = list.merge(head1, head2, head3);
public ListNode merge(ListNode... nodes) {

    // find smallest, keep its index
    int firstIndex = -1;
    int firstValue = 0;
    for (int i=0; i<nodes.length; i++) {
        ListNode n = nodes[i];
        if (n != null && (firstIndex == -1 || n.data < firstValue)) {
            firstIndex = i;
            firstValue = n.data;
        }
    }

    if (firstIndex == -1) {
        // reached the end of all lists
        return null;
    } else {
        // use node with smallest as next head
        ListNode head = nodes[firstIndex];

        // call again with all lists, but skipping head
        // because we are already using it in the result list
        nodes[firstIndex] = head.next;
        head.next = merge(nodes);
        return head;
    }
}

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

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

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

0 голосов
/ 19 декабря 2018

Ваша проблема в том, что вы не написали все условия.Давайте рассмотрим ваш вариант использования.

Первый список: 2 11 19 21 24

Второй список: 14 15 18 23 25

Третий список: 3 9 17 20 22

В соответствии с логикой вашего кода, при первом слиянии мы получим 2 в first.data < second.data.Следующий ход у нас 11 <14 в том же месте.Но после этого у нас теперь есть <br>19 21 24
14 15 18 23 25
3 9 17 20 22 * ​​1014 *, и ни одно из условий не будет выполнено в следующих ходах.

0 голосов
/ 19 декабря 2018

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

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

public ListNode LinkedTest(ListNode first, ListNode second) {

    ListNode head = null;

    if (first == null)

        return second;

    else if (second == null)

        return first;

    else if (first.data < second.data) 
    {
        head = first;
        head.next = LinkedTest(first.next, second);
    } 
    else if (second.data < first.data)
    {
        head = second;
        head.next = LinkedTest(first, second.next);
    }

    return head;
}

Первая половина метода проверяет необходимость завершения рекурсии.Здесь код завершается, когда один из двух списков достиг конца.Например, если вы объединяете список [1, 3, 5, 7] со списком [2, 4, 6, 8, 9, 10], вы в конечном итоге объедините список [] со списком [8, 9, 10].В этот момент вам нужно будет завершить работу, вернув список и, следовательно, завершив рекурсию.

Внесенные вами изменения в алгоритм неверны, поскольку при трехстороннем слиянии вы не можете просто вернуть второй список, когдаВ первом списке нет узлов.Критерии завершения стали более сложными, и вам следует обратить внимание на то, когда вам действительно нужно прекратить рекурсию.Например, если у вас есть попытка объединить [1, 2], [3, 4], [5, 6], предполагая, что другая часть алгоритма работает, то вы получите объединение между [], [3, 4], [5, 6] для которого вы собираетесь вернуть [3, 4] и отбросить [5, 6], что неверно.

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


Вторая половина метода сравниваетпервые элементы списков и использует меньший элемент в качестве заголовка нового списка, а затем назначает элемент данных next узла заголовка «каким бы ни был результат объединения остальных списков».Обратите внимание, что в этой версии кода каждый раз выполняется ровно один из блоков кода.(Если данные не равны, тогда будет ошибка. Исправление будет оставлено в качестве упражнения.) Пока рекурсия не завершена, нам нужно идти глубже, верно!

Модификация, которую выmade не работает, потому что вы сравниваете первые данные со вторыми данными, затем со вторыми и третьими .. тогда что?Давайте создадим таблицу, отследим код и запишем, как он ведет себя, и как бы вы хотели, чтобы код вел себя.

| Order | Your code   | Expected | Result  |
|-------|-------------|----------|---------|
| A<B<C | head = A    | head = A | Correct |
| A<C<B | head = A    | head = A | Correct |
| B<A<C | head = B    | head = B | Correct |
| B<C<A | head = B    | head = B | Correct |
| C<A<B | head = A    | head = C | WRONG!  |
| C<B<A | head = null | head = C | WRONG!  | * note: no recursion in this case

Посмотрите, как ваш код дает сбой, когда C является наименьшим элементом?Вам тоже нужно это исправить.


Наконец, есть одно решение, которое даже не требует создания трехстороннего слияния.Технически, merge(A, merge(B, C)) все еще должен работать за O (n), сложность также ведет себя правильно.

Удачи в финале!

0 голосов
/ 19 декабря 2018

Вам даны три отсортированных списка , и вас просят объединить их в один отсортированный список .Алгоритм достижения этого очень прост.

Я рекомендую отслеживать три индекса (по одному на каждый вход список ) и инициализировать их все как 0 (первый элемент каждого списка ).Поскольку каждый список содержит одинаковое количество элементов, вы можете просто выполнить итерацию от 0 до 3n, где n - это размер одного из списка с.

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

Вы будете повторять это 3n раз (по одному разу для каждого элемента в трех списках ), и результатом будет один объединенный список , который отсортированв порядке возрастания.

Предполагая, что каждый вход список всегда будет равным по размеру, этот алгоритм равен O(3n), который упрощен до O(n).

Псевдо-решение кода может выглядеть примерно так:

list1 = [2, 11, 19, 21, 24]
list2 = [14, 15, 18, 23, 25]
list3 = [3, 9, 17, 20, 22]
merged_list = []

indexes = [0, 0, 0]

for i in 0..15:
    minValue = Integer.MAX_VALUE
    minIndex = -1

    if indexes[0] < len(list1) and list1[indexes[0]] < minValue:
        minValue = list1[indexes[0]]
        minIndex = 0

    if indexes[1] < len(list2) and list2[indexes[1]] < minValue:
        minValue = list2[indexes[1]]
        minIndex = 1

    if indexes[2] < len(list3) and list3[indexes[2]] < minValue:
        minValue = list3[indexes[2]]
        minIndex = 2

    merged_list += minValue
    indexes[minIndex]++
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...