Scala 2.13中如何调整maxBy的排序规则实现指定需求?
高效解决Scala词频元组的最优项选择问题(O(n)时间复杂度)
你提到的问题很典型——用maxBy(_._1)只会返回第一个出现的最大计数元组,但我们需要在计数相同时优先选择字典序靠前的单词,同时还要保证O(n)的时间效率,完全可以通过自定义Ordering来让maxBy(或max方法)满足需求,当然用reduce也能实现,下面我详细拆解这两种方案:
方案一:自定义Ordering适配max/maxBy
max和maxBy方法本身就是O(n)的遍历操作,只要给它们传入符合我们需求的排序规则,就能直接得到正确结果。我们的排序逻辑是:
- 优先按计数降序排列(计数越大越靠前)
- 计数相同时,按单词字典序升序排列(字母序越靠前越优先)
写法1:通过转换元组生成Ordering
我们可以把原元组(count, word)转换成(-count, word),因为Scala默认的Ordering是升序,这样-count越大(原count越大)的元组会被认为更大;当-count相同时,字典序小的word会排在前面:
val wordCounts = Seq( (10, "World"), (5, "Something"), (10, "Hello") ) val commonWord = wordCounts.maxBy(identity)(Ordering.by[(Int, String), (Int, String)](t => (-t._1, t._2))) println(commonWord) // 输出 (10, "Hello")
写法2:组合Ordering规则(更直观)
如果觉得转换元组不够直观,还可以直接组合两个Ordering:先按计数降序,计数相同则按单词升序:
val countOrder = Ordering[Int].reverse // 计数降序 val wordOrder = Ordering[String] // 单词字典序升序 // 组合规则:先按countOrder比较,相等时再用wordOrder val combinedOrder = countOrder.on[(Int, String)](_._1).orElse(wordOrder.on(_._2)) val wordCounts = Seq( (10, "World"), (5, "Something"), (10, "Hello") ) val commonWord = wordCounts.max(combinedOrder) println(commonWord) // 输出 (10, "Hello")
方案二:用reduce实现自定义比较
如果你更倾向于手动实现比较逻辑,reduce也是O(n)的选择,它会遍历序列并逐步累积符合条件的元素:
val wordCounts = Seq( (10, "World"), (5, "Something"), (10, "Hello") ) val commonWord = wordCounts.reduce { (currentBest, next) => // 先比计数,计数大的留下 if (currentBest._1 > next._1) currentBest else if (currentBest._1 < next._1) next // 计数相同,选字典序更小的单词 else if (currentBest._2 < next._2) currentBest else next } println(commonWord) // 输出 (10, "Hello")
方案对比
- 自定义Ordering的方式更符合Scala的函数式风格,代码简洁且排序规则可复用,适合需要多次使用该逻辑的场景。
reduce的写法更直白,不需要理解Ordering的组合逻辑,适合快速实现简单的自定义比较。
两种方法都能保证O(n)的时间复杂度,远优于sortBy(_._1).reverse.head这类O(n log n)的方案。
内容的提问来源于stack exchange,提问作者Bar Bokovza
相关产品推荐
相关产品推荐

