用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. mysql Can't connet MySQL server to '@localhost'

    10063/10060/10038好像都能解决 mysql -nt -remove mysql -nt install

  3. win 7 IIS 配置

    http://jingyan.baidu.com/article/219f4bf723bcb2de442d38ed.html win7旗舰版系统 点击开始→控制面板,然后再点击程序,勿点击卸载程序,否 ...

  4. Hibernate对象的状态

    站在持久化的角度, Hibernate 把对象分为 4 种状态: 1. 持久化状态 2. 临时状态 3. 游离状态 4. 删除状态 Session 的特定方法能使对象从一个状态转换到另一个状态. 下面 ...

  5. Repeater控件 ---属性(ItemCommand事件)

    epeater的Command操作:1.ItemCommand事件 - 在Repeater中所有能触发事件的控件,都会来触发这一个事件 2.CommandName - 判断点击的是什么按钮,e.Com ...

  6. nginx配置文件注释

    #定义Nginx运行的用户和用户组user www www; #nginx进程数,建议设置为等于CPU总核心数.worker_processes 8; #全局错误日志定义类型,[ debug | in ...

  7. css兼容tooltip提示框方法

    最终效果图: 基本原理 先设定一个背景色的普通div盒子,然后使用上篇post得到的三角型图标,把div盒子设置为相对定位模式,三角型图标设置为绝对定位,位置相对于div盒子,调整到合适的位置.这样就 ...

  8. Xshell

    http://baike.baidu.com/link?url=5lc5IxVVauitYSbqlOXJBvrvM3qVfMhzz6x_xu2cOMb108Ln9Wk7iJ3f46vG0kBninKw ...

  9. [INS-32025] 所选安装与指定 Oracle 主目录中已安装的软件冲突

    windows server 2008 r2 enterprise下的解决办法为:删除C:\Program Files (x86)\Oracle\Inventory\ContentsXML目录下的in ...

  10. 搭建本地MAVEN NEXUS 服务

    下载 http://120.192.76.70/cache/www.sonatype.org/downloads/nexus-latest-bundle.zip?ich_args=232fba36ed ...