我有一个问题需要对我正在参加的编码课程进行最终审查。它要求将3个链接列表合并为1个链接列表。我遇到的问题是合并列表时,我能够按升序合并三个列表,但是我错过了第二个列表23和25的最后两个节点。我无法弄清楚为什么它停在那里。问题在这里:
编写一个名为LinkedTest的程序,
(第一名)2 11 19 21 24
(第二列表)14 15 18 23 25
(第三列表)3 9 17 20 22
这是我的代码:
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
答案 0 :(得分:0)
系统会为您提供三个排序的列表,并要求您将它们合并为一个排序的列表。实现此目的的算法非常简单。
我建议跟踪三个索引(每个输入 list 一个),并将它们全部初始化为0
(每个 list 的第一个元素)。由于每个 list 包含相同数量的元素,因此您可以简单地从0
迭代到3n
,其中n
是之一的大小列表。
在循环内部,您想通过使用各自的索引来找到每个 list 的3
head 个元素之间的最小元素。为此,只需查看元素并将它们与另一个元素进行比较。找到最小值后,可以将该元素追加到合并列表中,并增加相应的 list 的索引(这对于避免两次相同的元素都非常重要)。
您将重复3n
次(对三个列表中的每个元素一次),结果将是一个合并的 list 升序排序。
假设每个输入 list 的大小始终相等,则此算法为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]++
答案 1 :(得分:0)
您的问题是您没有写所有条件。 让我们看看您的用例。
第一个列表: 2 11 19 21 24
第二个列表: 14 15 18 23 25
第三列表: 3 9 17 20 22
根据您的代码逻辑,在第一次合并中,我们在2
中得到一个first.data < second.data
。下一回合,我们在相同位置有11 <14。但是之后我们有了
19 21 24
14 15 18 23 25
3 9 17 20 22
下一轮将不满足任何条件。
答案 2 :(得分:0)
我想您的代码是从合并两个列表的代码修改而来的。您应该回想一下为什么每段代码都能工作,并思考为什么代码无法按照您认为的方式工作。
让我们假设您的原始算法如下:
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]
,并假设算法的另一部分有效,那么最终将在{{1}之间进行合并},[]
,[3, 4]
,您将为其返回[5, 6]
并丢弃[3, 4]
,这是不正确的。
一种解决方案是在任一列表出局时调用算法的两列表版本。这需要更多的代码(这也是非常重复的),但是从这个特定的代码来看更直接。另一种解决方案是仅在两个列表为空时才终止,但这在递归情况下需要特别注意以检查空值。
该方法的后半部分在列表的第一个元素之间进行比较,并使用较小的元素作为新列表的头,然后将头节点的[5, 6]
数据成员分配给“无论结果如何”合并其余列表为”。请注意,在此版本的代码中,每次都会精确执行其中一个代码块。 (除非数据相等,否则将存在一个错误。应将其作为练习来解决。)只要递归没有终止,我们就需要更深入,对!!
您所做的修改不起作用,因为您正在将第一个数据与第二个数据进行比较,然后将第二个数据与第三个数据进行比较。然后呢?让我们做一个表,跟踪代码并记录其行为以及您希望代码的行为。
next
看看每当C是最小元素时代码如何失败?您也需要解决此问题。
最后,有一种解决方案甚至不需要创建3向合并。从技术上讲,| 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
仍应在O(n)上运行,时间复杂度也应正确发挥作用。
祝你决赛好运!
答案 3 :(得分:0)
为什么要限制自己进行三向合并?让我们看一下N路合并的一般情况,然后将其应用于3路(或普通的老式无聊2路)。
Jasmine
您可以将其视为许多链上带有链接的链,链上带有数字,其中每个链的数字按升序排列。要建立一个按顺序排列所有数字的大链,您可以:
正如Jacob_G所指出的,在回答中,当一个或多个列表为空时,您缺少一些用于选择正确的最小元素的逻辑。 Jacob的回答很好,但是我虽然会向您展示更大的图景:如果您对// 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;
}
}
理解,N
应该一点也不张扬(而且,您会获得对一般想法的洞察力)。< / p>