用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. Android Studio JNI/NDK 编程简介(一)

    首先说一下概念及相关的东西: JNI : JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++).从Java1.1开始 ...

  2. lua 高级

    io操作: io.input(filename):指定一个输入流,可以是标准输入stdin,也可以是一个文件路径,返回一个文件句柄: io.output(filename):指定一个输出流,可以是标准 ...

  3. 数据结构中很常见的各种树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    数据结构中常见的树(BST二叉搜索树.AVL平衡二叉树.RBT红黑树.B-树.B+树.B*树) 二叉排序树.平衡树.红黑树 红黑树----第四篇:一步一图一代码,一定要让你真正彻底明白红黑树 --- ...

  4. transition实现自定义tips淡入淡出效果

    transition实现动画的时候,只能实现指定属性的渐变,元素显隐要通过opacity来实现: 做了一个小的demo: <!doctype html> <html> < ...

  5. uva 1368 DNA Consensus String

    这道题挺简单的,刚开始理解错误,以为是从已有的字符串里面求最短的距离,后面才发现是求一个到所有字符串最小距离的字符串,因为这样的字符串可能有多个,所以最后取最小字典序的字符串. 我的思路就是求每一列每 ...

  6. Fliwer:监控植物状态 实现远程浇水

    Fliwer是一款针对植物而研发出来的设备,可以监控土壤水分.光照.温度.空气湿度和肥料是否充足这些指标.结合云端的植物数据和天气预报,它能够自动决定什么时候给植物浇水,甚至提醒你什么时候应该施肥.修 ...

  7. AS

    1.修改注释模板: “File“->“Settings”    File and Code Templates     然后选中Includes tab下面的File Header. 2.代码提 ...

  8. Fiddler抓包测试App接口

    Fiddler抓包测试App接口 使用Fiddler对手机App应用进行抓包,可以对App接口进行测试,也可以了解App传输中流量使用及请求响应情况,从而测试数据传输过程中流量使用的是否合理. 抓包过 ...

  9. python异常处理、反射、socket

    一.isinstance 判断对象是否为类的实例 n1 = print isinstance(n1,int) class A: pass class B(A): pass b= B() print i ...

  10. [珠玑之椟]浅谈代码正确性:循环不变式、断言、debug

    这个主题和代码的实际写作有关,而且内容和用法相互交织,以下只是对于其内容的一个划分.<编程珠玑>上只用了两个章节20页左右的篇幅介绍,如果希望能获得更多的实例和技巧,我比较推崇<程序 ...