用2d-tree数据结构实现在2维矩形区域内的高效的range search 和 nearest neighbor search。2d-tree有许多的应用,在天体分类、计算机动画、神经网络加速、数据挖掘、图像检索。

range search: 返回所有在query rectangle里的所有点

nearest neighbor search: 返回query point的最近点

下图显示这两种search操作

Geometric Primitives. 在assignment给定了几何图元应该如何表示,如下图

其中关于Point和Rectangle的表示已经定义在了Point2D.java和RectHV.java中,API都已经提供了,这个都不用自己实现。

Point2D的API主要是点的坐标、平方距离、欧几里得距离、点的比较、绘制等,

RectHV主要是一个2维的包围盒,记录矩形的左下角和右上角点的信息,主要API是contains(Point2D)判断点是否在矩形内,intersects(RectHV)是否与另一个矩形相交,以及矩形到点的平方距离和距离,绘制等。源码都可以找到来进行分析。

下面就是要完成的两个任务:Brute-force 实现 和 2d-tree 实现。

需要实现的API是一样的,这里以PointSET为例子,2d-tree也一样:

public class PointSET {
public PointSET() // construct an empty set of points
public boolean isEmpty() // is the set empty?
public int size() // number of points in the set
public void insert(Point2D p) // add the point p to the set (if it is not already in the set)
public boolean contains(Point2D p) // does the set contain the point p?
public void draw() // draw all of the points to standard draw
public Iterable<Point2D> range(RectHV rect) // all points in the set that are inside the rectangle
public Point2D nearest(Point2D p) // a nearest neighbor in the set to p; null if set is empty
}

Brute-force:暴力的实现需要insert()和contains()是在O(logn)的复杂度,nearest()和range()是O(N)的复杂度。

这里用algs4.jar的SET来实现,代码很简单。

range(RectHV): 遍历SET中所有Point与当前RectHV进行包含关系判断

nearest(Point2D):遍历SET中的所有Point与当前Point进行距离判断,不断更新最小距离和最小距离的点,在进行距离判断的时候,用平方距离,开方会影响计算速度。

public class PointSET {
private SET<Point2D> set; // construct an empty set of points
public PointSET() {
set = new SET<Point2D>();
} // is the set empty?
public boolean isEmpty() {
return set.isEmpty();
} // number of points in the set
public int size() {
return set.size();
} // add the point p to the set (if it is not already in the set)
public void insert(Point2D p) {
set.add(p);
} // does the set contain the point p?
public boolean contains(Point2D p) {
return set.contains(p);
} // draw all of the points to standard draw
public void draw() {
for (Point2D p : set) {
StdDraw.point(p.x(), p.y());
}
} // all points in the set that are inside the rectangle
public Iterable<Point2D> range(RectHV rect) {
Queue<Point2D> q = new Queue<Point2D>();
for (Point2D p : set) {
if (rect.contains(p))
q.enqueue(p);
}
return q;
} // a nearest neighbor in the set to p; null if set is empty
public Point2D nearest(Point2D p) {
double mindis = Double.MAX_VALUE;
Point2D ret = null;
for (Point2D s : set) {
double dis = s.distanceSquaredTo(p);
if (dis < mindis) {
mindis = dis;
ret = s;
}
}
return ret;
}
}

2d-tree:这里是使用BST为结构对节点进行组织,每个节点记录下面的相关属性,这个在Possible Progress Step中有提示。通过assignment中的描述和图可以对它有很清晰的认识。

Node节点定义如下:

p记录当前点,rect记录当前点的“包围盒”(轴平行矩阵),lb记录左边或者下边的区域节点,rt记录右边或者上边的区域节点。

private static class Node {
private Point2D p; // the point
// the axis-aligned rectangle corresponding to this node
// the max rectangle include this node, aabb
private RectHV rect;
private Node lb; // the left/bottom subtree
private Node rt; // the right/top subtree
public Node(Point2D p, RectHV rect) {
this.p = p;
this.rect = rect;
lb = null;
rt = null;
}
}

2d-Tree的具体实现只要参考BST的写法就很好实现,insert的时候原本写的是new RectHV,不断进行递归进行构造,但是new的太多,fail test了。后面在insert中直接把RectHV的4个坐标作为参数在Insert中进行递归。

还有一个比较重要的问题是,在insert,get,draw中,要把方向orientation作为参数,用来标示当前应该是左右分还是上下分,draw,insert和get都参照BST的写法,递归实现是十分简洁的。

range()和nearest()都采用BFS广度搜索的方法,遍历这个2d-tree,进行相交和包含的判断,维护有效的节点信息。nearest()也记得使用平方距离,开方影响运行时间。

代码实现如下:

public class KdTree {

    private Node root;
private int N;
private static class Node {
private Point2D p; // the point
// the axis-aligned rectangle corresponding to this node
// the max rectangle include this node, aabb
private RectHV rect;
private Node lb; // the left/bottom subtree
private Node rt; // the right/top subtree
public Node(Point2D p, RectHV rect) {
this.p = p;
this.rect = rect;
lb = null;
rt = null;
}
} private final RectHV CANVAS = new RectHV(0, 0, 1, 1); // construct an empty set of points
public KdTree() {
root = null;
N = 0;
} // is the set empty?
public boolean isEmpty() {
return N == 0;
} // number of points in the set
public int size() {
return N;
} /**************************************
* less
* compare two Point2D with orientation
*************************************/
private int compareTo(Point2D v, Point2D w, int ori) {
if (v.equals(w)) return 0; // same point
else {
if (ori == 0) {
// vertical line
if (v.x() < w.x()) return -1;
else return 1;
} else {
// horizontal line
if (v.y() < w.y()) return -1;
else return 1;
}
}
} /***********************************************
* Insert
**********************************************/ private Node insert(Node x, Point2D p,
double xmin, double ymin, double xmax, double ymax,
int ori) {
if (x == null) {
N++;
return new Node(p, new RectHV(xmin, ymin, xmax, ymax));
}
int cmp = compareTo(p, x.p, ori);
double x0 = xmin, y0 = ymin, x1 = xmax, y1 = ymax;
if (cmp < 0) {
if (ori == 0) x1 = x.p.x();
else y1 = x.p.y();
x.lb = insert(x.lb, p, x0, y0, x1, y1, 1-ori);
}
else if (cmp > 0) {
if (ori == 0) x0 = x.p.x();
else y0 = x.p.y();
x.rt = insert(x.rt, p, x0, y0, x1, y1, 1-ori);
}
return x;
} // add the point p to the set (if it is not already in the set)
public void insert(Point2D p) {
// 0 for vertical, 1 for horizontal
root = insert(root, p,
CANVAS.xmin(), CANVAS.ymin(),
CANVAS.xmax(), CANVAS.ymax(), 0);
} /*******************************************
* contains
*****************************************/
private boolean get(Node x, Point2D p, int ori) {
if (x == null) return false;
int cmp = compareTo(p, x.p, ori);
if (cmp < 0) return get(x.lb, p, 1-ori);
else if (cmp > 0) return get(x.rt, p, 1-ori);
return true;
} // does the set contain the point p?
public boolean contains(Point2D p) {
// 0 for vertical, 1 for horizontal
return get(root, p, 0);
} /***************************************
* Draw()
*************************************/
private void draw(Node x, int ori) {
if (x == null) return;
// draw point
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setPenRadius(.01);
StdDraw.point(x.p.x(), x.p.y());
// draw line
if (ori == 0) {
// vertical
StdDraw.setPenColor(StdDraw.RED);
StdDraw.setPenRadius();
StdDraw.line(x.p.x(), x.rect.ymin(), x.p.x(), x.rect.ymax());
} else {
// horizontal
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.setPenRadius();
StdDraw.line(x.rect.xmin(), x.p.y(), x.rect.xmax(), x.p.y());
}
draw(x.lb, 1-ori);
draw(x.rt, 1-ori);
} // draw all of the points to standard draw
public void draw() {
StdDraw.setScale(0, 1);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setPenRadius();
CANVAS.draw();
draw(root, 0);
} // all points in the set that are inside the rectangle
public Iterable<Point2D> range(RectHV rect) {
Queue<Point2D> points = new Queue<Point2D>();
Queue<Node> queue = new Queue<Node>();
if (root == null) return points;
queue.enqueue(root);
while (!queue.isEmpty()) {
Node x = queue.dequeue();
if (x == null) continue;
if (rect.contains(x.p)) points.enqueue(x.p);
if (x.lb != null && rect.intersects(x.lb.rect)) queue.enqueue(x.lb);
if (x.rt != null && rect.intersects(x.rt.rect)) queue.enqueue(x.rt);
}
return points;
} // a nearest neighbor in the set to p; null if set is empty
public Point2D nearest(Point2D p) {
if (root == null) return null;
Point2D retp = null;
double mindis = Double.MAX_VALUE;
Queue<Node> queue = new Queue<Node>();
queue.enqueue(root);
while (!queue.isEmpty()) {
Node x = queue.dequeue();
double dis = p.distanceSquaredTo(x.p);
if (dis < mindis) {
retp = x.p;
mindis = dis;
}
if (x.lb != null && x.lb.rect.distanceSquaredTo(p) < mindis)
queue.enqueue(x.lb);
if (x.rt != null && x.rt.rect.distanceSquaredTo(p) < mindis)
queue.enqueue(x.rt);
}
return retp;
} }

总结:Last words, 这应该是第一门坚持上完的公开课吧,原来Andrew Ng的ML上了一半后,由于事情太多就把课给荒废了(现在又重新开始新一轮了,fighting!)。

可能这几个Assignment写的都不咋地,但记录回顾一下,还是觉得很有收获。特别感谢Prof.Sedgewick和Coursera平台,给予了一段精彩的旅程。后面的Part II到时候继续跟上。

不得不感叹,国外的MOOC平台做的相当的完美,提供了这么多好的资源,国内估计也有类似的吧,没去具体了解过。一定程度上真是把大学搬进了家里,不过感觉仅凭MOOC上几周课程来对领域或者部分的知识,作为一个较为(较为深入?)了解比较恰当,如果要熟练运用和掌握,还需要很长的路要走,Study hungry! Study foolish!

Programming Assignment 5: Kd-Trees的更多相关文章

  1. 课程一(Neural Networks and Deep Learning),第三周(Shallow neural networks)—— 3.Programming Assignment : Planar data classification with a hidden layer

    Planar data classification with a hidden layer Welcome to the second programming exercise of the dee ...

  2. Algorithms: Design and Analysis, Part 1 - Programming Assignment #1

    自我总结: 1.编程的思维不够,虽然分析有哪些需要的函数,但是不能比较好的汇总整合 2.写代码能力,容易挫败感,经常有bug,很烦心,耐心不够好 题目: In this programming ass ...

  3. Algorithms : Programming Assignment 3: Pattern Recognition

    Programming Assignment 3: Pattern Recognition 1.题目重述 原题目:Programming Assignment 3: Pattern Recogniti ...

  4. Programming Assignment 2: Randomized Queues and Deques

    实现一个泛型的双端队列和随机化队列,用数组和链表的方式实现基本数据结构,主要介绍了泛型和迭代器. Dequeue. 实现一个双端队列,它是栈和队列的升级版,支持首尾两端的插入和删除.Deque的API ...

  5. 课程一(Neural Networks and Deep Learning),第二周(Basics of Neural Network programming)—— 2、编程作业常见问题与答案(Programming Assignment FAQ)

    Please note that when you are working on the programming exercise you will find comments that say &q ...

  6. Programming Assignment 4: 8 Puzzle

    The Problem. 求解8数码问题.用最少的移动次数能使8数码还原. Best-first search.使用A*算法来解决,我们定义一个Seach Node,它是当前搜索局面的一种状态,记录了 ...

  7. coursera普林斯顿算法课part1里Programming Assignment 2最后的extra challenge

    先附上challenge要求: 博主最近在刷coursera普林斯顿大学算法课part1部分的作业,Programming Assignment2最后的这个extra challenge当初想了一段时 ...

  8. Programming Assignment 2: Deques and Randomized Queues

    编程作业二 作业链接:Deques and Randomized Queues & Checklist 我的代码:Deque.java & RandomizedQueue.java & ...

  9. Programming Assignment 4: Boggle

    编程作业四 作业链接:Boggle & Checklist 我的代码:BoggleSolver.java 问题简介 Boggle 是一个文字游戏,有 16 个每面都有字母的骰子,开始随机将它们 ...

随机推荐

  1. Node.js初探之hello world

    昨天公司内部培训,主讲人王老板对Node.js评价很高,连用几个“变态”来形容,恰好今天周末,有时间来认识下Node.js,对一门新语言最好的认识,是让其输出“hello world”,今天我就利用N ...

  2. Jquery异步上传图片

    网页中这样: <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head id=& ...

  3. lintcode-【简单题】合并区间

    题目: 给出若干闭合区间,合并所有重叠的部分. 样例: 给出的区间列表 => 合并后的区间列表: [ [ [1, 3], [1, 6], [2, 6], => [8, 10], [8, 1 ...

  4. Android--Toast(吐司)的基本使用

    1.学习Android开发,不能从单方面的知识去考虑问题,要从全面出发. 2.Toast(吐司) 1.打开eclipse 选择文件存放的位置 点击Dbms,测试是否连接成功(海马模拟器或者夜神模拟器) ...

  5. Ubuntu14.04安装MySQL-python异常: mysql_config: not found,Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-MJWMPd/MySQL-python/

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABUoAAAE8CAIAAACZ6RwMAAAgAElEQVR4nOydaVxTx9fHn9dhKWqltv

  6. Java和C++中的static

    1.Java类中的static变量和static方法会在类装载的过程中就得到内存分配,然后就会进行初始化工作.最多可能会被初始化3次,静态代码块的执行在main方法之前. static变量不可以在构造 ...

  7. mysql 主从复制原理

    主从形式   mysql主从复制 灵活 一主一从 主主复制 一主多从---扩展系统读取的性能,因为读是在从库读取的: 多主一从---5.7开始支持 联级复制---     用途及条件   mysql主 ...

  8. COM的永久接口

    COM的永久接口

  9. Explode TArray

    function Explode(const Separator, S: string; Limit: Integer = 0): TArray;var SepLen : Integer; F, P ...

  10. 7.29 H5学习笔记

    常用实体字符小于号  < 大于号  > 和号  & 引号  " 撇号  &apos; 分  ¢ 镑  £ 日元  ¥ 欧元  € 小节  § 版权  © 注册  ...