Swift5 基于汇率数组查找兑换路径实现多币种转EUR求和
实现思路
本质是有向图的路径权重计算:把所有币种看作图的节点,汇率对(curFrom→curTo)看作有向边,边的权重就是转换汇率,目标是找到从任意起始币种到EUR节点的可达路径,将路径上的边权相乘得到总转换系数。
这里用深度优先搜索(DFS)实现路径查找,同时记录已访问节点避免死循环,兼容任意步数的多步转换场景。
第一步:预处理汇率数据为邻接表
将现有rates数组转成字典形式的邻接表,大幅优化后续路径查找的效率:
// 生成邻接表:[出发币种: [目标币种: 转换汇率]] private func buildAdjacencyList(from rates: [Rate]) -> [String: [String: Double]] { var adjacencyList: [String: [String: Double]] = [:] for rate in rates { // 字符串类型的汇率转Double,无效值直接跳过 guard let rateValue = Double(rate.curRate) else { continue } // 初始化当前出发币种的子字典 if adjacencyList[rate.curFrom] == nil { adjacencyList[rate.curFrom] = [:] } // 存入对应边的转换权重 adjacencyList[rate.curFrom]?[rate.curTo] = rateValue } return adjacencyList }
第二步:实现通用币种转换系数计算函数
输入起始币种、目标币种(默认EUR)、邻接表,返回总转换系数,无有效路径则返回nil:
private func conversionFactor( from startCurrency: String, to targetCurrency: String = "EUR", adjacencyList: [String: [String: Double]], visitedCurrencies: Set<String> = [] ) -> Double? { // 起始币种本身就是目标币种,转换系数为1 if startCurrency == targetCurrency { return 1.0 } // 已访问过的币种直接返回,避免汇率环导致的死循环 if visitedCurrencies.contains(startCurrency) { return nil } // 邻接表中无当前币种的出边,说明无可达路径 guard let nextCurrencies = adjacencyList[startCurrency] else { return nil } // 标记当前币种为已访问 var newVisited = visitedCurrencies newVisited.insert(startCurrency) // 遍历所有下一级币种,递归查找路径 for (nextCurrency, rate) in nextCurrencies { if let subFactor = conversionFactor( from: nextCurrency, to: targetCurrency, adjacencyList: adjacencyList, visitedCurrencies: newVisited ) { // 找到有效路径,返回当前汇率乘以后续路径的总系数 return rate * subFactor } } // 所有路径均无法到达目标币种 return nil }
第三步:重写求和函数
替换原有仅支持单步转换的逻辑,预先生成邻接表避免重复计算:
// 预生成邻接表,仅在首次访问时计算一次,性能更优 private lazy var adjacencyList: [String: [String: Double]] = { buildAdjacencyList(from: rates) }() func sumTransactions() -> Double { var total: Double = 0 for transaction in itemTransactions { // 金额字符串转Double,无效值直接跳过该笔交易 guard let amount = Double(transaction.amount) else { continue } // 本身就是EUR,直接累加 if transaction.currency == "EUR" { total += amount continue } // 计算当前币种到EUR的总转换系数 guard let factor = conversionFactor( from: transaction.currency, adjacencyList: adjacencyList ) else { // 无有效转换路径的交易可根据业务规则选择跳过或记录日志 continue } total += amount * factor } return total }
注意事项:
- 上述实现找到任意一条有效路径就返回结果,如果存在多条不同转换路径(比如同币种有多个不同中间兑换方式),可根据业务规则调整:如果要取最小/最大转换系数,可遍历所有路径后对比结果,再选择符合规则的系数。
- 针对汇率数量级特别大的场景,也可以用广度优先搜索(BFS)替代DFS,逻辑完全一致,仅遍历顺序不同。
- 原有代码中存在的强解包风险已经全部用可选绑定替换,稳定性更高。
内容的提问来源于stack exchange,提问作者Aleph72
相关产品推荐
相关产品推荐

