lab4 & kd-tree 学习笔记

lab4完成过程及反思 与 kd-tree学习

Part1. lab4

题目大义

给定n组坐标,构建kd-tree,有m次询问,每次询问给出一个目标坐标,返回到此坐标的最近点坐标(距离计算方法为:曼哈顿距离、欧几里得距离、球坐标距离)

什么是kd-tree(详解kd-tree留到part2)

数据规模

节点数:n<=5000,询问数m<=100000

简单分析

建树

在数据结构课学习过平衡树之后,我们都能意识到二叉树的高度对询问的稳定性的显著影响。因此,对于这样的一个新品种二叉树,尽可能把它建的平衡是有必要的(先生,你也不想每次遍历整个链表吧)借鉴平衡树的思维,我们每次取序列中位数,即可保证左右子树规模相近,递归执行即可。

那么问题变成了,如何查找中位数。一个显而易见的想法:每次取中位数都对序列按照关键字排序。树高应该是 $O(logn) $ ,每次排序最快是 nlogn的。虽然对于本题可以接受,但如果,我是说如果,我拿出 $10^{5} $ 的数据,阁下又如何应对?

我们不妨借鉴快速排序的思想,我们只需要确定此次对中位数的划分是正确的即可,而划分的左右两侧并不要求有序。所以可以用快速排序的思想线性的解决这个问题。

好在,STL为我们提供了一个强力的函数:nth_element,可以减轻苦逼大学生的工作量

nth_element
template<typename _RAIter>
    _GLIBCXX20_CONSTEXPR
    void
    nth_element(_RAIter, _RAIter, _RAIter);

  template<typename _RAIter, typename _Compare>
    _GLIBCXX20_CONSTEXPR
    void
    nth_element(_RAIter, _RAIter, _RAIter, _Compare);

这是他的源定义,_Compare是比较函数,而且 _Compare要求只能有两个参数,且这两个参数必须是被比较的同一类的两个元素

template<typename _RandomAccessIterator>
    _GLIBCXX20_CONSTEXPR
    inline void
    nth_element(_RandomAccessIterator __first, _RandomAccessIterator __nth,
		_RandomAccessIterator __last)
    {
      // concept requirements
      __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
				  _RandomAccessIterator>)
      __glibcxx_function_requires(_LessThanComparableConcept<
	    typename iterator_traits<_RandomAccessIterator>::value_type>)
      __glibcxx_requires_valid_range(__first, __nth);
      __glibcxx_requires_valid_range(__nth, __last);
      __glibcxx_requires_irreflexive(__first, __last);

      if (__first == __last || __nth == __last)
	return;

      std::__introselect(__first, __nth, __last,
			 std::__lg(__last - __first) * 2,
			 __gnu_cxx::__ops::__iter_less_iter());
    }

这个是STL实现nth_element的方法,本质上就是以nth为划分的一次快排,所以是O(n)的

求解思路解析

如果没有助教的提示,我们会怎么思考这个问题?

基于kd-tree 我们会有几个思路

1.遍历

每次询问从树根遍历每一个点,记录最短距离,并更新答案。每次查询都是O(n)的,爽翻了

2.遍历+剪枝

从根开始,按一定顺序遍历节点。如果target到当前节点代表的划分的距离大于当前最优距离,那么这个节点的另一个儿子就不用被遍历。加入这个剪枝,可以减少很多不必要的计算,从而提高程序效率。

层序遍历 76分

先序遍历96分

还有一个细节,可以进一步提高运行速度:我们优先查找target所在的那棵子树,这样子可以最快的找到期望解。即使它不是最后的解,但他能最大限度的剪枝,从而提高运算效率。

通过这个方法可以通过这道题,用时:3.350s

反思

这次lab最大的收获便是kd-tree这样的数据结构以及搜索和剪枝的思想。如何让搜索树变得更瘦,其实是这个题的核心问题。

但如何写tree.build函数,也会很大的影响debug的效率(本人曾写了一个100+行的build,而最后的版本只有20行

Part2.kd-tree

留个坑,等我周末学完再写

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值