[英]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.