Swift实战:如何优雅地从二叉搜索树中挑出最接近的K个值


文章目录

摘要

在日常开发中,我们经常会遇到"在一堆数据中找出最接近某个值"的需求。尤其在搜索引擎、推荐系统或者地理坐标匹配中,这种"最近匹配"的问题非常常见。LeetCode 的第 272 题《最接近的二叉搜索树值 II》就是一个很贴近实际的算法题。它要求我们在一个二叉搜索树(BST)里,找出与给定目标值最接近的 K 个数。

这篇文章会带你一步步分析题目,写出 Swift 的题解代码,并配上可运行的 Demo 和测试样例。咱们还会结合实际场景,看看这类问题背后的意义,以及怎么在项目中用起来。

描述

题目要求如下:

给定一个二叉搜索树的根节点 root、一个目标值 target 和一个整数 k,找出 BST 中最接近目标值的 k 个数。

示例:

txt 复制代码
输入:root = [4,2,5,1,3], target = 3.714286, k = 2
输出:[4,3]

注意:

  • 你可以假设 k 总是有效的,1 ≤ k ≤ BST 的总节点数。

  • 你可以假设目标值在 BST 中不存在。

  • 答案不要求按顺序输出。

题解答案

思路很简单,但实现起来稍有挑战。我们主要有两种策略:

  1. 中序遍历 + 排序

    先把所有节点值拿出来,然后根据它们和目标值的差距进行排序,最后返回前 K 个。

  2. 使用堆结构进行剪枝优化

    这方法更高效,不需要遍历所有节点,只维护一个大小为 K 的最大堆,保存当前最接近的 K 个值。

考虑到 Swift 标准库里没有原生堆结构,我们这里优先实现第一种"中序遍历 + 排序"的方案,简单直观好调试。

题解代码分析

我们使用中序遍历把 BST 的值按升序放到一个数组里。然后根据每个值与目标值的差异排序,最后取前 K 个。

swift 复制代码
class TreeNode {
    var val: Int
    var left: TreeNode?
    var right: TreeNode?

    init(_ val: Int) {
        self.val = val
    }
}

class Solution {
    func closestKValues(_ root: TreeNode?, _ target: Double, _ k: Int) -> [Int] {
        var nums = [Int]()

        // 中序遍历
        func inorder(_ node: TreeNode?) {
            guard let node = node else { return }
            inorder(node.left)
            nums.append(node.val)
            inorder(node.right)
        }

        inorder(root)

        // 按照和 target 的距离进行排序
        nums.sort { abs(Double($0) - target) < abs(Double($1) - target) }

        // 取前 k 个
        return Array(nums.prefix(k))
    }
}

示例测试及结果

我们来搭一个简单的树,跑一下这个函数。

swift 复制代码
// 构建树:[4,2,5,1,3]
let root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(5)
root.left?.left = TreeNode(1)
root.left?.right = TreeNode(3)

let sol = Solution()
let result = sol.closestKValues(root, 3.714286, 2)
print(result) // 输出:[4, 3]

运行解释:

中序遍历后我们拿到 [1,2,3,4,5]。对比目标值 3.714286,我们发现 4 和 3 是最接近的两个值,所以返回 [4,3]。

时间复杂度

  • 中序遍历:O(n)

  • 排序:O(n log n)

  • 取前 K 个值:O(k)

总计:O(n log n) ,其中 n 是树中节点的数量。

空间复杂度

  • 中序遍历需要额外的数组保存所有节点值:O(n)

  • 排序过程不需要额外空间(使用原地排序)

总计:O(n)

总结

这道题虽然是一个典型的"树 + 排序"问题,但它的思想可以很好地迁移到很多实际场景里。比如:

  • 搜索引擎中找最相关的词条

  • 地图里找离当前位置最近的 K 个地标

  • 数据监控系统中,找最接近阈值的异常点

我们实现的是中序遍历 + 排序的解法,如果你项目中对性能有要求,可以考虑加上最大堆,剪枝优化进一步降低时间复杂度。

未来展望

如果你希望挑战更复杂的变体,可以试试下面这些:

  • 在 BST 中找"最接近目标值的 K 个节点路径"

  • 假设树是不断变化的,怎么用优雅的数据结构支持动态查询?

  • 加入权重,对"接近"的定义做更多解释(比如偏向大值或小值)

相关推荐
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁3 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
孙启超3 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust
此生决int3 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++
CoderYanger3 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
茶底世界之下3 天前
为什么预览、录制、离线导出不能共享同一个背压策略?
ios·swift
2501_916007473 天前
苹果商店iOS应用上架费用全面解析:开发者计划与审核费用计算
android·ios·小程序·https·uni-app·iphone·webview
伞伞悦读3 天前
【第37期】Python JSON 与配置详解:序列化、反序列化、嵌套结构和配置文件
开发语言·python·json