[英]Merging 3 linked lists into 1 (Java )
我有一个关于我正在参加的编码课程的最终审查的问题。 它要求将 3 个链表合并为 1 个链表。 我遇到的问题是在合并列表时,我能够按升序合并三个列表,但我缺少第二个列表 23 和 25 的最后 2 个节点。我不知道为什么它停在那里。 问题在这里:
编写一个名为 LinkedTest 的程序:
First List: 2 11 19 21 24
Second List: 14 15 18 23 25
Third List: 3 9 17 20 22
2 3 9 11 14 15 17 18 19 20 21 22 23 24 25
这是我的代码:
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;
}
}
}
输出:
First List: 2 11 19 21 24
Second List: 14 15 18 23 25
Third List: 3 9 17 20 22
Merged List: 2 3 9 11 14 15 17 18 19 20 21 22 24
为什么要限制自己进行 3 向合并? 让我们看一下 N 路合并的一般情况,然后将其应用于 3 路(或普通的无聊的 2 路)。
// 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
(每个列表的第一个元素)。 因为每个列表包含相同数量的元素,您可以简单地从0
迭代到3n
,其中n
是列表s 之一的大小。
在循环内部,您希望使用各自的索引找到每个列表的3
个头元素之间的最小元素。 为此,只需查看元素并将它们相互比较即可。 找到最小值后,您可以将该元素附加到合并列表中并增加相应列表的索引(这很重要,因为不要两次附加相同的元素)。
您将重复此操作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]++
你的问题是你没有写出所有的条件。 让我们看看您的用例。
第一个列表:2 11 19 21 24
第二个名单:14 15 18 23 25
第三个名单:3 9 17 20 22
根据您的代码逻辑,在第一次合并中,我们在first.data < second.data
得到2
。 下一回合,我们在同一位置有 11 < 14。 但在那之后我们现在有
19 21 24
14 15 18 23 25
3 9 17 20 22
并且在接下来的回合中都不会满足任何条件。
我想你的代码是从合并两个列表的代码中修改而来的。 您应该回忆为什么每段代码都有效,并思考为什么您的代码没有按照您认为应该的方式工作。
让我们假设您的原始算法如下所示:
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
数据成员分配给“无论合并其余列表的结果是什么” ”。 请注意,在此版本的代码中,每次只执行其中一个代码块。 (除非数据相等,否则会有bug。修复它留作练习。)只要不终止递归,我们就需要更深入,对吧!
您所做的修改不起作用,因为您将第一个数据与第二个数据进行比较,然后将第二个数据与第三个数据进行比较……然后呢? 让我们制作一个表格,跟踪代码并记录它的行为方式与您希望代码的行为方式。
| 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 是最小元素时,看看您的代码如何失败? 你也需要解决这个问题。
最后,有一种解决方案甚至不需要创建 3 路合并。 从技术上讲, merge(A, merge(B, C))
应该仍然以 O(n) 时间复杂度运行,并且行为也正确。
祝你总决赛好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.