[英]Is there a cleaner or more efficient way to remove all values in a sorted linked list that appear in another sorted linked list?
我目前正在為我將在秋季參加的課程做一些練習題,我遇到了這個問題:
編寫可以添加到
LinkedIntList
class 的方法 removeAll。 該方法應該有效地從整數的排序列表中刪除出現在第二個整數排序列表中的所有值。 例如,假設LinkedIntList
變量list1
和list2
引用以下列表:
list1
: [1, 3, 5, 7]
list2
: [1, 2, 3, 4, 5]如果調用
list1.removeAll(list2)
,則列表應在調用后存儲以下值:
list1
:[7]
list2
: [1, 2, 3, 4, 5]兩個列表都保證按排序(非遞減)順序排列,盡管其中一個列表中可能存在重復項。 因為列表是排序的,所以您可以通過一次數據非常有效地解決這個問題。 您的解決方案需要在O(M + N)時間內運行,其中M和N是兩個列表的長度。
您可能不會調用 class 的任何其他方法來解決此問題,您可能不會構造任何新節點,也可能不會使用任何輔助數據結構來解決此問題(如數組、
ArrayList
、Queue
、String
等。 )。 您也不能更改節點的任何數據字段。 您必須通過重新排列列表的鏈接來解決此問題。
為了解決這個問題,我們提供了一個自定義的 LinkedIntList 和 ListNode class。
public class LinkedIntList {
private ListNode front; // head of the list
...
}
public class ListNode {
public int data; // value stored in this node of the list
public ListNode next; // reference to next node in the list (null if end of list)
...
}
我在 O(M + N) 時間內一次性解決了這個問題。 但是,我想知道是否有任何方法可以更有效地解決這個問題。 我在 if 語句中有一個嵌套的 if-else 塊,我認為它可以被清理掉。 我在下面包含了我的代碼。
public void removeAll(LinkedIntList list2) {
if (this.front != null && list2.front != null) {
// iterators for the list to remove values from
ListNode prev = null;
ListNode current = this.front;
// iterator for the list where values from list1 may appear
ListNode list2Current = list2.front;
// reference to the front of list1
ListNode head = current;
while (current != null && list2Current != null) {
while (current != null && current.data < list2Current.data) {
prev = current;
current = current.next;
}
while (current != null && list2Current != null && list2Current.data < current.data) {
list2Current = list2Current.next;
}
if (current != null && list2Current != null && current.data == list2Current.data) {
// prev can only be null if current is still pointing at the front of list1
if (prev == null) {
ListNode nextup = current.next;
current.next = null;
current = nextup;
head = current;
} else {
prev.next = current.next;
current = prev.next;
}
} else if (current != null) {
prev = current;
current = current.next;
}
}
front = head;
}
}
這個問題沒有比O(M+N)
更快的解決方案,因為兩個列表的所有元素都應該被迭代。 但是,解決方案可以更簡單地編碼,例如:
public void removeAll(LinkedIntList list2) {
ListNode previous = front;
ListNode current = front;
ListNode toRemove = list2.front;
while (current != null && toRemove != null) {
if (current.data < toRemove.data) {
previous = current;
current = current.next;
} else if (current.data > toRemove.data) {
toRemove = toRemove.next;
} else {
if (current == front) {
front = current.next;
} else {
previous.next = current.next;
}
current = current.next;
}
}
}
您對解決方案有了正確的想法。 這個問題要求您至少檢查列表中的每個條目一次,因此根據定義,您不能比單程線性時間做得更好。 我相信您找到了最佳解決方案。
關於您的代碼的一些注釋:
if (prev == null) {
ListNode nextup = current.next;
current.next = null;
current = nextup;
}
您分離了要刪除的節點,但從未將下一個節點(新的current
)附加到列表中。 您的列表頭仍將指向“已刪除”節點。 (我認為您可能已嘗試在循環結束時使用front = head
來解決此問題,但您從未更新head
,因此這實際上沒有任何效果。我將完全刪除該行。)
這是我將如何重寫if
:
if (current != null && list2Current != null && current.data == list2Current.data) {
if (prev == null) {
this.first = current.next;
} else {
prev.next = current.next;
prev = current;
}
current = current.next;
}
關於else if
:兩個while
循環退出的唯一方法是 if
所以理論上,你甚至不需要檢查是否相等。 在這種情況下,你們都不需要繼續並最終進入else
分支。
還有其他方法可以使用更簡潔的代碼(如@MartinBG's answer)來制作相同的解決方案,但我試圖專門清理你的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.