求助:Haskell实现获取列表第二小值的secondSmallest函数
完善你的
secondSmallest函数 看起来你已经搞定了secondSmallest的基础边界处理——空列表和单元素列表返回Nothing,接下来我们来完成核心逻辑,满足你提出的所有要求:返回列表的第二小值,若最小值重复出现,那这个最小值就视为第二小值。
先明确几个核心规则:
- 列表长度小于2时,返回
Nothing(你已经实现了) - 若最小值在列表中出现至少两次,第二小值就是这个最小值
- 若最小值只出现一次,第二小值是列表中除最小值外的最小元素
下面给你两种实现思路,你可以根据需求选择:
思路一:直观排序法(适合新手理解)
先对列表排序,然后根据排序后的结果判断最小值是否重复,逻辑非常直白:
import Data.List (sort) secondSmallest :: Ord a => [a] -> Maybe a secondSmallest [] = Nothing secondSmallest [x] = Nothing secondSmallest xs = let sortedList = sort xs smallest = head sortedList -- 检查排序后的第二个元素是否和最小值一致(说明最小值重复) hasDuplicateMin = sortedList !! 1 == smallest in if hasDuplicateMin then Just smallest else Just (sortedList !! 1)
这个方法的优点是容易看懂,但排序的时间复杂度是O(n log n),如果处理超大规模列表的话效率会稍低。
思路二:单次遍历高效法(性能更优)
我们可以在一次遍历中同时跟踪当前的最小值和第二小值,这样只需要O(n)的时间复杂度,效率更高:
secondSmallest :: Ord a => [a] -> Maybe a secondSmallest [] = Nothing secondSmallest [x] = Nothing secondSmallest (x:y:rest) = go initialMin initialSecond rest where -- 初始化最小和第二小:取前两个元素的大小关系 (initialMin, initialSecond) = if x < y then (x, y) else (y, x) -- 遍历函数:持续跟踪当前最小和第二小值 go currentMin currentSecond [] = -- 遍历结束后,判断是否存在重复最小值 if currentMin == currentSecond then Just currentMin else Just currentSecond go currentMin currentSecond (z:zs) | z < currentMin = -- 遇到比当前最小值更小的,更新最小和第二小 go z currentMin zs | z == currentMin = -- 遇到重复的最小值,把第二小值也设为最小值 go currentMin currentMin zs | z < currentSecond = -- 遇到比第二小值小但不比最小值小的,更新第二小 go currentMin z zs | otherwise = -- 当前元素不影响最小和第二小,继续遍历 go currentMin currentSecond zs
这个方法只需要遍历列表一次,而且能精准处理所有边界情况,比如全是相同元素的列表、最小值多次出现的列表等。
测试验证你的示例
用你给出的例子验证:
secondSmallest [1.0]→Nothing✔️secondSmallest [1,1,2]→Just 1✔️secondSmallest [5,3,7,2,3,1]→Just 2✔️
再补充几个测试场景:
secondSmallest [2,2,2]→Just 2(所有元素都是最小值)secondSmallest [3,1,4,1,5]→Just 1(最小值重复)secondSmallest [10,9,8,7,6]→Just 7(最小值只出现一次)
内容的提问来源于stack exchange,提问作者Aelin
相关产品推荐
相关产品推荐

