如何在元组列表中找到所有最小元素?

Posted

技术标签:

【中文标题】如何在元组列表中找到所有最小元素?【英文标题】:How to find all minimum elements in a list of tuples? 【发布时间】:2020-02-10 19:26:51 【问题描述】:

如何找到列表中的所有最小元素?现在我有一个元组列表,即

[(10,'a'),(5,'b'),(1,'c'),(8,'d'),(1,'e')]

所以我想要一个新列表中的列表中所有最小元素的输出。例如

 [(1,'c'),(1,'e')]

我试过了

minimumBy (comparing fst) xs

但这只会返回第一个最小元素。

【问题讨论】:

您计算最小值,然后过滤列表。 【参考方案1】:

在获得第一个值的最小值后,我们可以对这些项目进行列表过滤。因为您在这里要检索最小项目的列表,所以我们也可以通过返回一个空列表来覆盖空列表:

minimumsFst :: Ord a => [(a, b)] -> [(a, b)]
minimumsFst [] = []
minimumsFst xs = filter ((==) minfst . fst) xs
    where minfst = minimum (map fst xs)

例如:

Prelude> minimumsFst [(10,'a'),(5,'b'),(1,'c'),(8,'d'),(1,'e')]
[(1,'c'),(1,'e')]

【讨论】:

【参考方案2】:

Oneliner。关键是排序。

Prelude Data.List> let a = [(1,'c'),(2,'b'),(1,'w')]
Prelude Data.List> (\xs@((m,_):_) -> takeWhile ((== m) . fst ) xs) . sortOn fst $ a
[(1,'c'),(1,'w')]

【讨论】:

排序需要 O(n lg n) 时间,这明显慢于通过最小值/过滤器组合实现的 O(n)。 @chepner 你忘记了懒惰。 :) 惰性如何保证sortOn 可以在 o(n lg n) 时间内产生最小值? (不过,如果花费超过两秒钟的时间思考使用哪种排序算法,我可能会回答我自己的问题......) @chepner 曾经是一个热门话题。当排序是惰性的(on-line)时,take k . sort 是 O(n)(对于小 ks),例如 mergesort。当找到第一个(最小)元素时,只进行了 O(n) 比较,其余的比较树还有待探索。所以对于take n,它是 O(k*n),对于一个小的固定 k,它是 O(n)。 @chepner(更正:take k)。但是这里的 k 不是固定的,你问?好吧,如果 k 很大,这意味着我们有几乎排序的输入,并且排序本身是 O(n)。【参考方案3】:

这是一个一次性工作的解决方案(这里的大多数其他答案都做两遍:一个找到最小值,一个过滤它),并且不依赖于如何实现排序功能以提高效率。

-# LANGUAGE ScopedTypeVariables #-

import Data.Foldable (foldl')

minimumsBy :: forall a. (a -> a -> Ordering) -> [a] -> [a]
minimumsBy _ [] = []
minimumsBy f (x:xs) = postprocess $ foldl' go (x, id) xs
  where
    go :: (a, [a] -> [a]) -> a -> (a, [a] -> [a])
    go acc@(x, xs) y = case f x y of
      LT -> acc
      EQ -> (x, xs . (y:))
      GT -> (y, id)
    postprocess :: (a, [a] -> [a]) -> [a]
    postprocess (x, xs) = x:xs []

请注意,我在这里使用的[a] -> [a] 类型称为difference list,也称为Hughes list。

【讨论】:

【参考方案4】:

你试过了

minimumBy (comparing fst) xs

也可以写成

= head . sortBy (comparing fst) $ xs
= head . sortOn fst $ xs
= head . head . group . sortOn fst $ xs
= head . head . groupBy ((==) `on` fst) . sortOn fst $ xs

这只会返回第一个元素而不是它们的列表,因此只需删除额外的 head 即可获得所需的内容:

=        head . groupBy ((==) `on` fst) . sortOn fst $ xs

当然head 不好,因为它会在[] 输入上出错。相反,我们可以使用安全选项,

= concat . take 1 . groupBy ((==) `on` fst) . sortOn fst $ xs

顺便说一句,任何调用minimum 的解决方案对于空输入列表也是不安全的:

> head []
*** Exception: Prelude.head: empty list

> minimum []
*** Exception: Prelude.minimum: empty list

但是takeWhile 是安全的:

> takeWhile undefined []
[]

编辑:由于懒惰,即使在最坏的情况下,最终版本的整体时间复杂度仍应为 O(n)

【讨论】:

除了链接到实现之外,对于这个事实是否有很好的参考? 它应该在“k最大”之类的下面。 这也有点令人不安,Data.OldList 似乎可以选择对sortBy 使用插入排序(基于USE_REPORT_PRELUDE 的值),尽管似乎没有任何提及Haskell 报告中的排序(1998 年或 2010 年)。 非常令人印象深刻的答案【参考方案5】:

您也可以使用foldr 轻松做到这一点:

minimumsFst :: Ord a => [(a, b)] -> [(a, b)]
minimumsFst xs = go (minfst xs) xs
  where
  go mn ls = foldr (\(x, y) rs -> if (x ==  mn) then (x,y) : rs else rs) [] xs
  minfst ls = minimum (map fst ls)

用你的例子:

   minimumsFst [(10,'a'),(5,'b'),(1,'c'),(8,'d'),(1,'e')]
=> [(1,'c'),(1,'e')]

【讨论】:

我希望这会导致minfst xs 在每一步都被重新计算,这是不必要的昂贵。我宁愿删除参数,并使用... where minfst = minimum (map fst xs) 无论如何这是对过滤器的重新实现,minimum . map fst 是对fst . minimumBy (comparing fst) 的重新实现。 如果你已经用 foldr 重新实现了一些东西,你可以多走一步,把找到的最小值融合在一起。一个文件夹来完成整个工作。 :)

以上是关于如何在元组列表中找到所有最小元素?的主要内容,如果未能解决你的问题,请参考以下文章

在元组列表中查找元素

如何比较和搜索列表中的元素与列表 SML 中的元组

Pyspark 在元组列表上设置

Python基础-04元组

Python基础-04元组

react native - 如何在元组数组中使用 React Hooks?