You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用优雅的Kotlin函数式编程技巧解决连续驾车行程里程最大值问题

Kotlin函数式解法:找出连续驾车行程链的最大里程总和

需求回顾

给定包含驾车(isCar=true)与非驾车行程的Journey列表,需找出连续2次及以上的驾车行程链,计算每个链的里程总和,最终输出最大值。

数据类定义:

data class Journey(
    val isCar: Boolean = false,
    val mileage: Int,
)

示例输入(正确结果为80):

listOf(
    Journey(true, 10),
    Journey(true, 20),
    Journey(false, 105),
    Journey(true, 1046),
    Journey(false, 130),
    Journey(true, 40),
    Journey(true, 40),
)

优雅函数式实现(无可变集合)

以下提供两种纯函数式解法,均避免使用可变集合,符合现代Kotlin编程风格。

解法一:单次遍历的fold实现(高效)

通过fold单次遍历维护状态,避免多次迭代,效率最优:

fun main() {
    val journeys = listOf(
        Journey(true, 10),
        Journey(true, 20),
        Journey(false, 105),
        Journey(true, 1046),
        Journey(false, 130),
        Journey(true, 40),
        Journey(true, 40),
    )

    val maxConsecutiveMileage = journeys.fold(Triple(0, 0, emptyList<Int>())) { (currentCount, currentSum, validSums), journey ->
        when {
            journey.isCar -> {
                // 累加连续驾车的次数和里程
                Triple(currentCount + 1, currentSum + journey.mileage, validSums)
            }
            else -> {
                // 遇到非驾车行程,若之前连续驾车次数≥2,记录总和
                val updatedSums = if (currentCount >= 2) validSums + currentSum else validSums
                Triple(0, 0, updatedSums)
            }
        }
    }.let { (finalCount, finalSum, validSums) ->
        // 处理遍历结束后剩余的连续驾车链
        val allValidSums = if (finalCount >= 2) validSums + finalSum else validSums
        allValidSums.maxOrNull() ?: 0 // 无符合条件的链时返回0,可按需改为null
    }

    println(maxConsecutiveMileage) // 输出80
}

思路说明:

  • 用Triple作为累加器,保存三个状态:当前连续驾车的次数、当前里程总和、已收集的符合条件的链总和
  • 遍历每个行程时,根据是否为驾车行程更新状态:
    • 驾车行程:累加次数与里程
    • 非驾车行程:若之前连续次数达标,将总和存入有效列表,重置状态
  • 遍历结束后检查最后一段连续链,再取最大值

解法二:分组式实现(直观简洁)

通过分组连续的行程类型,逻辑更直观:

fun main() {
    val journeys = listOf(
        Journey(true, 10),
        Journey(true, 20),
        Journey(false, 105),
        Journey(true, 1046),
        Journey(false, 130),
        Journey(true, 40),
        Journey(true, 40),
    )

    val maxConsecutiveMileage = journeys
        // 生成连续行程的分组编号
        .runningFold(Pair(0, false)) { (groupNum, prevIsCar), journey ->
            if (journey.isCar == prevIsCar) Pair(groupNum, journey.isCar) else Pair(groupNum + 1, journey.isCar)
        }
        .drop(1) // 移除初始的默认分组
        .zip(journeys) // 将分组编号与原行程绑定
        .groupBy({ it.first }, { it.second }) // 按分组编号聚合行程
        .values // 提取所有分组
        .filter { group -> group.all { it.isCar } && group.size >= 2 } // 筛选符合条件的驾车链
        .maxOfOrNull { group -> group.sumOf { it.mileage } } ?: 0 // 计算最大总和

    println(maxConsecutiveMileage) // 输出80
}

思路说明:

  • 用runningFold为连续相同类型的行程生成统一分组编号
  • 通过zip和groupBy将行程按连续类型分组
  • 过滤出全为驾车且长度≥2的分组,计算每组总和后取最大值

原有代码的问题分析

  1. 效率低下:多次遍历列表(filter、map、forEach+while循环),且indexOf可能因重复元素导致索引错误
  2. 可变集合依赖:使用多个mutableListOf,不符合函数式无副作用的原则
  3. 逻辑复杂:嵌套循环和条件判断过多,易出错且可读性差

内容的提问来源于stack exchange,提问作者Storms786

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 21:45:37