我有一個Scala中的元素列表,我正在尋找一種方法來在發現重複時拆分列表。當發現重複時拆分列表scala
例如:List(x,y,z,e,r,y,g,a)
將被轉換爲List(List(x,y,z,e,r),List(y,g,a))
或List(x,y,z,x,y,z)
到List(x,y,z), List(x,y,z)
並List(x,y,z,y,g,x)
到List(x,y,z), List(y,g,x)
是否有比迭代和,並分別cheking每個元素的更有效的方法是什麼?
我有一個Scala中的元素列表,我正在尋找一種方法來在發現重複時拆分列表。當發現重複時拆分列表scala
例如:List(x,y,z,e,r,y,g,a)
將被轉換爲List(List(x,y,z,e,r),List(y,g,a))
或List(x,y,z,x,y,z)
到List(x,y,z), List(x,y,z)
並List(x,y,z,y,g,x)
到List(x,y,z), List(y,g,x)
是否有比迭代和,並分別cheking每個元素的更有效的方法是什麼?
快速和髒O(n)
使用O(n)
額外的存儲器:
import scala.collection.mutable.HashSet
import scala.collection.mutable.ListBuffer
val list = List("x", "y", "z", "e", "r", "y", "g", "a", "x", "m", "z")
var result = new ListBuffer[ListBuffer[String]]()
var partition = new ListBuffer[String]()
list.foreach { i =>
if (partition.contains(i)) {
result += partition
partition = new ListBuffer[String]()
}
partition += i
}
if (partition.nonEmpty) {
result += partition
}
result
ListBuffer(ListBuffer(X,Y,Z,E,R),ListBuffer(Y,G,A,X,M,Z ))
這個清單工作正常。但是它檢查列表中的每一個事件,這意味着對於列表:「x」,「y」,「z」,「e」,「r」,「y」,「g」,「a」,「x 「,」m「,」z「它將返回ListBuffer(ListBuffer(x,y,z,e,r),ListBuffer(y,g,a),ListBuffer(x,m),ListBuffer(z))。當我想要它返回: ListBuffer(ListBuffer(x,y,z,e,r),ListBuffer(y,g,a,x,m,z)) – Kratos
@Kratos啊...我誤解了原始問題。您可以通過檢查項目是否是'lookup'而不是'partition'來輕鬆修復它。我會編輯我的答案 – vsminkov
是的。我在第一種情況下在分區創建下添加了一個lookup.clear()。謝謝@vsminkov。你真的幫了! – Kratos
您基本上需要迭代查找表。我可以提供有關以下不可變和功能性tailrec實現的幫助。
import scala.collection.immutable.HashSet
import scala.annotation.tailrec
val list = List("x","y","z","e","r","y","g","a", "x", "m", "z", "ll")
def splitListOnDups[A](list: List[A]): List[List[A]] = {
@tailrec
def _split(list: List[A], cList: List[A], hashSet: HashSet[A], lists: List[List[A]]): List[List[A]] = {
list match {
case a :: Nil if hashSet.contains(a) => List(a) +: (cList +: lists)
case a :: Nil => (a +: cList) +: lists
case a :: tail if hashSet.contains(a) => _split(tail, List(a), hashSet, cList +: lists)
case a :: tail => _split(tail, a +: cList, hashSet + a, lists)
}
}
_split(list, List[A](), HashSet[A](), List[List[A]]()).reverse.map(_.reverse)
}
def splitListOnDups2[A](list: List[A]): List[List[A]] = {
@tailrec
def _split(list: List[A], cList: List[A], hashSet: HashSet[A], lists: List[List[A]]): List[List[A]] = {
list match {
case a :: Nil if hashSet.contains(a) => List(a) +: (cList +: lists)
case a :: Nil => (a +: cList) +: lists
case a :: tail if hashSet.contains(a) => _split(tail, List(a), HashSet[A](), cList +: lists)
case a :: tail => _split(tail, a +: cList, hashSet + a, lists)
}
}
_split(list, List[A](), HashSet[A](), List[List[A]]()).reverse.map(_.reverse)
}
splitListOnDups(list)
// List[List[String]] = List(List(x, y, z, e, r), List(y, g, a), List(x, m), List(z, ll))
splitListOnDups2(list)
// List[List[String]] = List(List(x, y, z, e, r), List(y, g, a, x, m, z, ll))
對所有答案,這是沒有評論的向下投票遊行? –
我認爲這是。讓樂隊演奏吧! –
我怎麼能改變這個功能,以便在找到重複的時候我會分割,但是沒有保留之前發現的歷史記錄?例如在你的結果列表數組中,最後三個應該在一起,因爲它們不包含重複項。 – Kratos
一個聲明尾遞歸版本(但不是很有效,因爲contains
名單上的)
var xs = List('x','y','z','e','r','y','g','a')
def splitAtDuplicates[A](splits: List[List[A]], right: List[A]): List[List[A]] =
if (right.isEmpty)// done
splits.map(_.reverse).reverse
else if (splits.head contains right.head) // need to split here
splitAtDuplicates(List()::splits, right)
else // continue building current sublist
splitAtDuplicates((right.head :: splits.head)::splits.tail, right.tail)
加快步伐了Set
來跟蹤我們到目前爲止看到:
def splitAtDuplicatesOptimised[A](seen: Set[A],
splits: List[List[A]],
right: List[A]): List[List[A]] =
if (right.isEmpty)
splits.map(_.reverse).reverse
else if (seen(right.head))
splitAtDuplicatesOptimised(Set(), List() :: splits, right)
else
splitAtDuplicatesOptimised(seen + right.head,
(right.head :: splits.head) :: splits.tail,
right.tail)
然而,除了你的答案以外的所有答案都被低估了...:D –
沒有downvote其中任何一個! –
哦......你誤解了。我真的不想這麼說。我只是提到了這個有趣的事實。這就是爲什麼我們需要表情符號的原因。 –
該解決方案提供的一些注意事項:
O(n^2)
更好,但我並沒有對「表現」做出聲明。foldLeft
一點,這是一個自然的方式去做這件事。O(n)
(累計)調用,可能實際上並不需要(取決於你在做什麼)。下面是代碼:
def partition(ls: List[String]): List[ListSet[String]] = {
ls.foldLeft(List(ListSet.empty[String]))((partitionedLists, elem:String) => {
if(partitionedLists.head.contains(elem)) {
ListSet(elem) :: partitionedLists
} else {
(partitionedLists.head + elem) :: partitionedLists.tail
}
})
}
partition(List("x","y","z","e","r","y","g","a"))
// res0: List[scala.collection.immutable.ListSet[String]] = List(ListSet(r, e, z, y, x), ListSet(a, g, y))
我使用ListSet
獲得Set
和排序,這是適用於您的使用案例雙方的利益。
foldLeft
是一個函數,它接受一個累加器值(在本例中爲List(ListSet.empty[String])
),並在它通過集合中的元素時對其進行修改。如果我們像這裏所做的那樣將累加器構造成段的列表,那麼當我們完成時它將具有原始列表的所有有序段。
這樣我就不得不依靠listSet來保持列表的順序。該實現雖然工作。謝謝。 – Kratos
您是否正在尋找*每個*重複或只是第一個分裂? –
迭代和檢查可能沒有更高效。如何查看「下一個」元素是否是重複的而不查看它? –
我正在尋找每個副本來拆分列表。最後,我想要多個不包含重複的列表 – Kratos