commit 7f5a224a40c61d555756cd648cbf573456ac16d5 Author: En Yi Date: Fri Aug 30 14:58:22 2019 +0800 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29a2f67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +Week*/* +!.gitignore +!Week*/src/ +*.zip +*.jar diff --git a/Week1/src/Percolation.java b/Week1/src/Percolation.java new file mode 100644 index 0000000..b657509 --- /dev/null +++ b/Week1/src/Percolation.java @@ -0,0 +1,124 @@ +import edu.princeton.cs.algs4.WeightedQuickUnionUF; + +import edu.princeton.cs.algs4.StdRandom; + +public class Percolation { + private boolean[][] grid; + private int size; + private WeightedQuickUnionUF grid_uf; + private WeightedQuickUnionUF grid_uf_2; //This is to check for isFull to prevent backwash + private int n_open_sites; + private int end_site; + + // creates n-by-n grid, with all sites initially blocked + public Percolation(int n){ + if (n<=0){ + throw new IllegalArgumentException("Invalid size: " + n); + } + + grid = new boolean[n][n]; + for(int i = 0; i < n; i++){ + for(int j = 0; j < n; j++){ + grid[i][j] = false; + } + } + size = n; + grid_uf = new WeightedQuickUnionUF(n*n+2); // Need extra 2 virtual sites + grid_uf_2 = new WeightedQuickUnionUF(n*n+1); // Need extra 1 virtual sites + end_site = n*n+1; + } + + // opens the site (row, col) if it is not open already + public void open(int row, int col){ + if (row<=0 || row>size || col<=0 || col>size){ + throw new IllegalArgumentException("Invalid row and col values: (" + row + ", " + col + ")"); + } + + if (!isOpen(row, col)){ + grid[row-1][col-1] = true; + n_open_sites++; + int index = col + (row - 1) * size; + + if (row == 1){ + grid_uf.union(0, index); + grid_uf_2.union(0, index); + } + if (row == size){ + grid_uf.union(end_site, index); + } + + // Check up + if(row>1 && isOpen(row-1, col)){ + grid_uf.union(index - size, index); + grid_uf_2.union(index - size, index); + } + // Check down + if(row1 && isOpen(row, col-1)){ + grid_uf.union(index-1, index); + grid_uf_2.union(index-1, index); + } + // Check right + if(colsize || col<=0 || col>size){ + throw new IllegalArgumentException("Invalid row and col values: (" + row + ", " + col + ")"); + } + return grid[row-1][col-1]; + } + + // is the site (row, col) full? + public boolean isFull(int row, int col){ + if (row<=0 || row>size || col<=0 || col>size){ + throw new IllegalArgumentException("Invalid row and col values: (" + row + ", " + col + ")"); + } + return grid_uf_2.connected(0, col + (row - 1) * size); + } + + // returns the number of open sites + public int numberOfOpenSites(){ + return n_open_sites; + } + + // does the system percolate? + public boolean percolates(){ + return grid_uf.connected(0, end_site); + } + + // test client (optional) + public static void main(String[] args){ + int n = 8; + Percolation grid = new Percolation(n); + int row, col; + while(!grid.percolates()){ + row = StdRandom.uniform(1, n+1); + col = StdRandom.uniform(1, n+1); + System.out.print(row); + System.out.println(" " + col); + grid.open(row, col); + } + System.out.println(grid.numberOfOpenSites()); + System.out.println((double)grid.numberOfOpenSites()/(n*n)); + for(int i = 1; i <= n; i++){ + for(int j = 1; j <= n; j++){ + if (grid.isFull(i, j)) + System.out.printf("%1s", 2); + else + System.out.printf("%1s", grid.isOpen(i, j)?1:0); + } + System.out.println(""); + } + } +} \ No newline at end of file diff --git a/Week1/src/PercolationStats.java b/Week1/src/PercolationStats.java new file mode 100644 index 0000000..19744c4 --- /dev/null +++ b/Week1/src/PercolationStats.java @@ -0,0 +1,78 @@ +import edu.princeton.cs.algs4.StdRandom; +import edu.princeton.cs.algs4.StdStats; +//import edu.princeton.cs.algs4.Stopwatch; + +public class PercolationStats { + private int trials; + private double results_mean; + private double results_stddev; + + // perform independent trials on an n-by-n grid + public PercolationStats(int n, int trials){ + if (n<=0 || trials<=0){ + throw new IllegalArgumentException("n and trials should be non-zero: (" + n + " " + trials + ")"); + } + + this.trials = trials; + double[] trial_results = new double[trials]; + Percolation grid; + for (int i=0;i implements Iterable{ + private class Node{ + Item item; + Node next; + Node prev; + } + + private Node first = null; + private Node last = null; + private int length = 0; + + // construct an empty deque + public Deque(){ + + } + + // is the deque empty? + public boolean isEmpty(){ + return (length == 0); + } + + // return the number of items on the deque + public int size(){ + return length; + } + + // add the item to the front + public void addFirst(Item item){ + if (item == null){ + throw new IllegalArgumentException("Null item is not accepted."); + } + Node new_node = new Node(); + new_node.item = item; + new_node.next = first; + new_node.prev = null; + if (first == null){ + last = new_node; + }else{ + first.prev = new_node; + } + first = new_node; + length++; + } + + // add the item to the back + public void addLast(Item item){ + if (item == null){ + throw new IllegalArgumentException("Null item is not accepted."); + } + Node new_node = new Node(); + new_node.item = item; + new_node.next = null; + new_node.prev = last; + if (last == null){ + first = new_node; + }else{ + last.next = new_node; + } + last = new_node; + length++; + } + + // remove and return the item from the front + public Item removeFirst(){ + if (isEmpty()){ + throw new NoSuchElementException("Queue is empty."); + } + Item item = first.item; + first = first.next; + if(first != null){ + first.prev = null; + } + length--; + + if(first == null){ + last = null; + } + + return item; + } + + // remove and return the item from the back + public Item removeLast(){ + if (isEmpty()){ + throw new NoSuchElementException("Queue is empty."); + } + + Item item = last.item; + if (length == 1){ + first = null; + last = null; + }else{ + last = last.prev; + last.next = null; + } + length--; + + return item; + + } + + // return an iterator over items in order from front to back + public Iterator iterator(){ + return new dequeIterator(); + } + + private class dequeIterator implements Iterator{ + private Node current = first; + + public boolean hasNext(){ + return current != null; + } + + public void remove(){ + throw new UnsupportedOperationException("Remove operation is not supported."); + } + + public Item next(){ + if (!hasNext()){ + throw new NoSuchElementException("No next item."); + } + Item item = current.item; + current = current.next; + return item; + } + + } + + // unit testing (required) + public static void main(String[] args){ + Deque test_deque = new Deque(); + + System.out.println(test_deque.isEmpty()); + test_deque.addFirst(2); + test_deque.addLast(5); + System.out.println(test_deque.removeLast()); + System.out.println(test_deque.removeFirst()); + test_deque.addFirst(1); + test_deque.addFirst(2); + test_deque.addFirst(3); + test_deque.addFirst(4); + test_deque.removeLast(); + + test_deque.addFirst(3); + test_deque.addFirst(7); + test_deque.addFirst(9); + test_deque.addLast(13); + test_deque.addLast(19); + test_deque.addLast(1); + System.out.println("Size: " + test_deque.size()); + Iterator iter = test_deque.iterator(); + while(iter.hasNext()){ + System.out.print(iter.next()); + System.out.print(" "); + } + System.out.println(""); + } +} \ No newline at end of file diff --git a/Week2/src/Permutation.java b/Week2/src/Permutation.java new file mode 100644 index 0000000..f9a815c --- /dev/null +++ b/Week2/src/Permutation.java @@ -0,0 +1,49 @@ +import java.util.Iterator; +import edu.princeton.cs.algs4.StdIn; +import edu.princeton.cs.algs4.StdRandom; + +public class Permutation { + public static void main(String[] args){ + if (args.length < 1){ + throw new IllegalArgumentException("Only 1 argument is required"); + } + int k = Integer.parseInt(args[0]); + + /*RandomizedQueue queue = new RandomizedQueue(); + + while(!StdIn.isEmpty()){ + queue.enqueue(StdIn.readString()); + } + Iterator iter = queue.iterator(); + for (int i=0;i0){ + RandomizedQueue queue = new RandomizedQueue(); + String str; + int n = 0; + while(!StdIn.isEmpty()){ + str = StdIn.readString(); + n++; + if (queue.size() < k){ + queue.enqueue(str); + }else{ + int i = StdRandom.uniform(1, n+1); + if (i <= k){ + queue.dequeue(); + queue.enqueue(str); + } + } + } + Iterator iter = queue.iterator(); + while (iter.hasNext()){ + System.out.println(iter.next()); + } + + } + + + } +} \ No newline at end of file diff --git a/Week2/src/RandomizedQueue.java b/Week2/src/RandomizedQueue.java new file mode 100644 index 0000000..47d06e9 --- /dev/null +++ b/Week2/src/RandomizedQueue.java @@ -0,0 +1,135 @@ +import java.util.Iterator; +import java.util.NoSuchElementException; +import edu.princeton.cs.algs4.StdRandom; + +public class RandomizedQueue implements Iterable { + private Item[] queue; + private int length = 0; + + // construct an empty randomized queue + public RandomizedQueue(){ + queue = (Item[]) new Object[1]; + } + + // is the randomized queue empty? + public boolean isEmpty(){ + return (length == 0); + } + + // return the number of items on the randomized queue + public int size(){ + return length; + } + + // add the item + public void enqueue(Item item){ + if (item == null){ + throw new IllegalArgumentException("Null item is not accepted."); + } + if (length == queue.length){ + resize(2 * queue.length); + } + queue[length++] = item; + } + + // remove and return a random item + public Item dequeue(){ + if (isEmpty()){ + throw new NoSuchElementException("Queue is empty."); + } + + int index = StdRandom.uniform(0, length); + Item item = queue[index]; + queue[index] = queue[length-1]; + queue[--length] = null; + + if (length > 0 && length == queue.length/4){ + resize(queue.length/2); + } + return item; + } + + // return a random item (but do not remove it) + public Item sample(){ + if (isEmpty()){ + throw new NoSuchElementException("Queue is empty."); + } + return queue[StdRandom.uniform(0, length)]; + } + + private void resize(int capacity) { + Item[] copy = (Item[]) new Object[capacity]; + for (int i = 0; i < length; i++){ + copy[i] = queue[i]; + } + queue = copy; + } + + // return an independent iterator over items in random order + public Iterator iterator(){ + return new queueIterator(); + } + + private class queueIterator implements Iterator{ + private int current = 0; + private Item[] iter_list; + + private queueIterator(){ + iter_list = (Item[]) new Object[length]; + + for (int i = 0; i < length; i++){ + iter_list[i] = queue[i]; + } + StdRandom.shuffle(iter_list); + } + + public boolean hasNext(){ + return current != iter_list.length; + } + + public void remove(){ + throw new UnsupportedOperationException("Remove operation is not supported."); + } + + public Item next(){ + if (!hasNext()){ + throw new NoSuchElementException("No next item."); + } + return iter_list[current++]; + } + + } + + // unit testing (required) + public static void main(String[] args){ + RandomizedQueue test_deque = new RandomizedQueue(); + System.out.println(test_deque.isEmpty()); + test_deque.enqueue(2); + test_deque.enqueue(3); + System.out.println("Size: "+test_deque.size()); + System.out.println(test_deque.dequeue()); + System.out.println(test_deque.dequeue()); + + for(int i=0;i<10;i++){ + test_deque.enqueue(i); + } + System.out.println("Size: "+test_deque.size()); + System.out.println(test_deque.sample()); + System.out.println(test_deque.sample()); + + Iterator iter = test_deque.iterator(); + Iterator iter2 = test_deque.iterator(); + + while(iter.hasNext()){ + System.out.print(iter.next()); + System.out.print(" "); + } + System.out.println(""); + while(iter2.hasNext()){ + System.out.print(iter2.next()); + System.out.print(" "); + } + System.out.println(""); + } + +} \ No newline at end of file diff --git a/Week3/src/BruteCollinearPoints.java b/Week3/src/BruteCollinearPoints.java new file mode 100644 index 0000000..113eb4f --- /dev/null +++ b/Week3/src/BruteCollinearPoints.java @@ -0,0 +1,81 @@ +import java.util.Arrays; + +public class BruteCollinearPoints { + private int num_of_segments; + private LineSegment[] line_segments; + + // finds all line segments containing 4 points + public BruteCollinearPoints(Point[] points){ + if (points == null){ + throw new IllegalArgumentException("Points cannot be null."); + } + for(int i=0; i=4){ + for(p=0;p<=original_copy.length-4;p++){ + for(q=p+1;q<=original_copy.length-3;q++){ + s1 = original_copy[p].slopeTo(original_copy[q]); + + for(r=q+1;r<=original_copy.length-2;r++){ + if (original_copy[p].slopeTo(original_copy[r]) != s1) + continue; + + for(s=r+1;s<=original_copy.length-1;s++){ + if (original_copy[p].slopeTo(original_copy[s]) != s1) + continue; + + if (num_of_segments == line_segments.length){ + resize(2*num_of_segments); + } + line_segments[num_of_segments++] = new LineSegment(original_copy[p], original_copy[s]); + + + } + } + } + } + if (num_of_segments > 0) + resize(num_of_segments); + + } + + + } + + // the number of line segments + public int numberOfSegments(){ + return num_of_segments; + } + + // the line segments + public LineSegment[] segments(){ + LineSegment[] return_array= new LineSegment[num_of_segments]; + for (int i=0;i=4){ + for(p=0;p<=original_copy.length-4;p++){ + Point[] points_copy = new Point[original_copy.length-p]; + for(int i=p;i= 4){ + boolean dupe = false; + + if (num_of_segments>0){ + for (int i=0;ip or q + * is null + */ + public LineSegment(Point p, Point q) { + if (p == null || q == null) { + throw new NullPointerException("argument is null"); + } + this.p = p; + this.q = q; + } + + + /** + * Draws this line segment to standard draw. + */ + public void draw() { + p.drawTo(q); + } + + /** + * Returns a string representation of this line segment + * This method is provide for debugging; + * your program should not rely on the format of the string representation. + * + * @return a string representation of this line segment + */ + public String toString() { + return p + " -> " + q; + } + + /** + * Throws an exception if called. The hashCode() method is not supported because + * hashing has not yet been introduced in this course. Moreover, hashing does not + * typically lead to good *worst-case* performance guarantees, as required on this + * assignment. + * + * @throws UnsupportedOperationException if called + */ + public int hashCode() { + throw new UnsupportedOperationException(); + } + +} + diff --git a/Week3/src/Point.java b/Week3/src/Point.java new file mode 100644 index 0000000..9b703c9 --- /dev/null +++ b/Week3/src/Point.java @@ -0,0 +1,154 @@ +/****************************************************************************** + * Compilation: javac Point.java + * Execution: java Point + * Dependencies: none + * + * An immutable data type for points in the plane. + * For use on Coursera, Algorithms Part I programming assignment. + * + ******************************************************************************/ + +import java.util.Comparator; +import edu.princeton.cs.algs4.StdDraw; + +public class Point implements Comparable { + + private final int x; // x-coordinate of this point + private final int y; // y-coordinate of this point + + /** + * Initializes a new point. + * + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + */ + public Point(int x, int y) { + /* DO NOT MODIFY */ + this.x = x; + this.y = y; + } + + /** + * Draws this point to standard draw. + */ + public void draw() { + /* DO NOT MODIFY */ + StdDraw.point(x, y); + } + + /** + * Draws the line segment between this point and the specified point + * to standard draw. + * + * @param that the other point + */ + public void drawTo(Point that) { + /* DO NOT MODIFY */ + StdDraw.line(this.x, this.y, that.x, that.y); + } + + /** + * Returns the slope between this point and the specified point. + * Formally, if the two points are (x0, y0) and (x1, y1), then the slope + * is (y1 - y0) / (x1 - x0). For completeness, the slope is defined to be + * +0.0 if the line segment connecting the two points is horizontal; + * Double.POSITIVE_INFINITY if the line segment is vertical; + * and Double.NEGATIVE_INFINITY if (x0, y0) and (x1, y1) are equal. + * + * @param that the other point + * @return the slope between this point and the specified point + */ + public double slopeTo(Point that) { + if (this.x == that.x && this.y == that.y){ + return Double.NEGATIVE_INFINITY; + } + if (this.x == that.x){ + return Double.POSITIVE_INFINITY; + } + if (this.y == that.y){ + return +0.0; + } + return (double)(that.y - this.y) / (that.x - this.x); + } + + /** + * Compares two points by y-coordinate, breaking ties by x-coordinate. + * Formally, the invoking point (x0, y0) is less than the argument point + * (x1, y1) if and only if either y0 < y1 or if y0 = y1 and x0 < x1. + * + * @param that the other point + * @return the value 0 if this point is equal to the argument + * point (x0 = x1 and y0 = y1); + * a negative integer if this point is less than the argument + * point; and a positive integer if this point is greater than the + * argument point + */ + public int compareTo(Point that) { + + if (this.y == that.y){ + return this.x - that.x; + }else{ + return this.y - that.y; + } + /*if (this.y - that.y > 0){ + return 1; + } + else if(this.y - that.y < 0){ + return -1; + } + else{ + if (this.x - that.x > 0) + return 1; + else if (this.x - that.x < 0) + return -1; + else + return 0; + }*/ + + } + + /** + * Compares two points by the slope they make with this point. + * The slope is defined as in the slopeTo() method. + * + * @return the Comparator that defines this ordering on points + */ + public Comparator slopeOrder() { + return new SlopeOrder(); + } + + private class SlopeOrder implements Comparator{ + public int compare(Point p1, Point p2){ + double s1 = slopeTo(p1); + double s2 = slopeTo(p2); + + //return (int) (s1-s2); + if (s1 < s2) + return -1; + else if (s1 > s2) + return 1; + else + return 0; + } + } + + + /** + * Returns a string representation of this point. + * This method is provide for debugging; + * your program should not rely on the format of the string representation. + * + * @return a string representation of this point + */ + public String toString() { + /* DO NOT MODIFY */ + return "(" + x + ", " + y + ")"; + } + + /** + * Unit tests the Point data type. + */ + public static void main(String[] args) { + /* YOUR CODE HERE */ + } +} diff --git a/Week3/src/TestClient.java b/Week3/src/TestClient.java new file mode 100644 index 0000000..f8d6778 --- /dev/null +++ b/Week3/src/TestClient.java @@ -0,0 +1,46 @@ +import edu.princeton.cs.algs4.StdDraw; +import edu.princeton.cs.algs4.StdOut; +import edu.princeton.cs.algs4.In; + +public class TestClient{ + public static void main(String[] args) { + + // read the n points from a file + In in = new In(args[0]); + int n = in.readInt(); + /*int n = 8; + int[] x_all = new int[]{10000, 0, 3000, 7000, 20000, 3000, 14000, 6000}; + int[] y_all = new int[]{0, 10000, 7000, 3000, 21000, 4000, 15000, 7000};*/ + + /*int n = 6; + int[] x_all = new int[]{19000, 18000, 32000, 21000, 1234, 14000}; + int[] y_all = new int[]{10000, 10000, 10000, 10000, 5678, 10000};*/ + + + Point[] points = new Point[n]; + for (int i = 0; i < n; i++) { + int x = in.readInt(); + int y = in.readInt(); + points[i] = new Point(x, y); + //points[i] = new Point(x_all[i], y_all[i]); + } + + // draw the points + StdDraw.enableDoubleBuffering(); + //32768 + StdDraw.setXscale(0, 32768); + StdDraw.setYscale(0, 32768); + for (Point p : points) { + p.draw(); + } + StdDraw.show(); + + // print and draw the line segments + FastCollinearPoints collinear = new FastCollinearPoints(points); + for (LineSegment segment : collinear.segments()) { + StdOut.println(segment); + segment.draw(); + } + StdDraw.show(); +} +} \ No newline at end of file diff --git a/Week4/src/Board.java b/Week4/src/Board.java new file mode 100644 index 0000000..9e7210d --- /dev/null +++ b/Week4/src/Board.java @@ -0,0 +1,233 @@ +import java.util.Iterator; +import java.util.NoSuchElementException; + +public class Board { + private int[][] board; + private final int board_dim; + private int hamming_dist; + private int manhattan_dist; + private int empty_pos; + // create a board from an n-by-n array of tiles, + // where tiles[row][col] = tile at (row, col) + public Board(int[][] tiles){ + if (tiles == null){ + throw new IllegalArgumentException("inital board cannot be null."); + } + board = new int[tiles.length][]; + hamming_dist = 0; + manhattan_dist = 0; + + // Make a copy of the board, and calculate the distance + int val = 0; + int row, col; + for(int i = 0; i < tiles.length; i++){ + board[i] = tiles[i].clone(); + for(int j = 0; j < tiles.length; j++){ + val = tiles[i][j]; + if(val > 0){ + row = (val-1)/tiles.length; + col = (val-1)%tiles.length; + if (i != row || j != col){ + hamming_dist++; + manhattan_dist += Math.abs(row-i)+Math.abs(col-j); + } + } + else{ + empty_pos = i*tiles.length + j; + } + } + + } + + board_dim = tiles.length; + + + } + + // string representation of this board + public String toString(){ + String tile_str = ""; + tile_str += board_dim + "\n"; + for(int i = 0; i < board_dim; i++){ + for(int j = 0; j < board_dim; j++){ + tile_str += board[i][j] + " "; + } + tile_str += "\n"; + } + return tile_str; + } + + // board dimension n + public int dimension(){ + return board_dim; + } + + // number of tiles out of place + public int hamming(){ + return hamming_dist; + } + + // sum of Manhattan distances between tiles and goal + public int manhattan(){ + return manhattan_dist; + } + + // is this board the goal board? + public boolean isGoal(){ + int last_val = board_dim * board_dim; + for(int i=0; i neighbors(){ + return new neighbourIterable(); + } + + // a board that is obtained by exchanging any pair of tiles + public Board twin(){ + int[][] new_board = new int[board_dim][board_dim]; + + for(int i = 0; i < board_dim; i++) + new_board[i] = board[i].clone(); + if (board_dim>1){ + int empty_row = empty_pos/board_dim; + int empty_col= empty_pos%board_dim; + + int target_row = 0; + if (empty_row == 0 && empty_col<2) + target_row = 1; + + new_board[target_row][0] += new_board[target_row][1]; + new_board[target_row][1] = new_board[target_row][0] - new_board[target_row][1]; + new_board[target_row][0] -= new_board[target_row][1]; + } + + return new Board(new_board); + } + + private class neighbourIterable implements Iterable{ + private Board[] neighbours; + private int n_neighbours; + + public neighbourIterable(){ + int empty_row = empty_pos/board_dim; + int empty_col= empty_pos%board_dim; + + neighbours = new Board[4]; + n_neighbours=0; + int[][] new_board = new int[board_dim][board_dim]; + + for(int i = 0; i < board_dim; i++) + new_board[i] = board[i].clone(); + if (empty_row > 0){ + new_board[empty_row][empty_col] = new_board[empty_row-1][empty_col]; + new_board[empty_row-1][empty_col] = 0; + neighbours[n_neighbours++] = new Board(new_board); + new_board[empty_row-1][empty_col] = new_board[empty_row][empty_col]; + new_board[empty_row][empty_col] = 0; + } + if (empty_row < board_dim-1){ + new_board[empty_row][empty_col] = new_board[empty_row+1][empty_col]; + new_board[empty_row+1][empty_col] = 0; + neighbours[n_neighbours++] = new Board(new_board); + new_board[empty_row+1][empty_col] = new_board[empty_row][empty_col]; + new_board[empty_row][empty_col] = 0; + } + + if (empty_col > 0){ + new_board[empty_row][empty_col] = new_board[empty_row][empty_col-1]; + new_board[empty_row][empty_col-1] = 0; + neighbours[n_neighbours++] = new Board(new_board); + new_board[empty_row][empty_col-1] = new_board[empty_row][empty_col]; + new_board[empty_row][empty_col] = 0; + + } + if (empty_col < board_dim-1){ + new_board[empty_row][empty_col] = new_board[empty_row][empty_col+1]; + new_board[empty_row][empty_col+1] = 0; + neighbours[n_neighbours++] = new Board(new_board); + new_board[empty_row][empty_col+1] = new_board[empty_row][empty_col]; + new_board[empty_row][empty_col] = 0; + } + + } + + public Iterator iterator(){ + return new neighbourIterator(); + } + + private class neighbourIterator implements Iterator{ + int current = 0; + + public boolean hasNext(){ + return current < n_neighbours; + } + + public void remove(){ + throw new UnsupportedOperationException("Remove operation is not supported."); + } + + public Board next(){ + if (!hasNext()){ + throw new NoSuchElementException("No next item."); + } + return neighbours[current++]; + } + } + } + + + // unit testing (not graded) + public static void main(String[] args){ + int size = 3; + int[][] tiles = new int[size][size]; + for (int i=0;i board_neighbours = board.neighbors(); + Iterator iter = board_neighbours.iterator(); + + while(iter.hasNext()){ + System.out.println(iter.next().toString()); + } + + + } + +} \ No newline at end of file diff --git a/Week4/src/Solver.java b/Week4/src/Solver.java new file mode 100644 index 0000000..c977e6f --- /dev/null +++ b/Week4/src/Solver.java @@ -0,0 +1,287 @@ +//import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; +import edu.princeton.cs.algs4.MinPQ; +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.StdOut; + +public class Solver { + + // This class is a linked list for storing checked boards + /*private class CheckedBoard{ + public Board board; + public CheckedBoard next; + }*/ + + // This class is used to store the state + private class BoardState implements Comparable{ + public Board current_board; + public BoardState prev_state; + public int moves; + private int priority; + + public BoardState(Board board, BoardState prev_state, int moves){ + current_board = board; + this.prev_state = prev_state; + this.moves = moves; + //priority = board.hamming() + moves; + priority = board.manhattan() + moves; + } + + public int compareTo(BoardState that) { + return this.priority - that.priority; + } + + } + + private boolean solvable; + private BoardState final_state; + + // find a solution to the initial board (using the A* algorithm) + public Solver(Board initial){ + if (initial == null){ + throw new IllegalArgumentException("inital board cannot be null."); + } + + // Initialise the initial BoardState + BoardState initialBoardState = new BoardState(initial, null, 0); + + // Initialise minPQ and put the initial BoardState + MinPQ priority_queue = new MinPQ(); + priority_queue.insert(initialBoardState); + + // Get also the twin version and initial its own minPQ + MinPQ priority_queue_twin = new MinPQ(); + priority_queue_twin.insert(new BoardState(initial.twin(), null, 0)); + + //Initiate checkedboard linked list + /*CheckedBoard head = new CheckedBoard(); + head.board = initialBoardState.current_board; + head.next = null; + + //Do the same for the twin + CheckedBoard head_twin = new CheckedBoard(); + head_twin.board = initial.twin(); + head_twin.next = null;*/ + + BoardState state = initialBoardState; + Iterable state_neighbours; + Iterator iter; + //CheckedBoard new_board; + + // While both PQ is not empty + while(!(priority_queue.isEmpty() && priority_queue_twin.isEmpty())){ + // delmin + if (!priority_queue.isEmpty()){ + state = priority_queue.delMin(); + + //check isGoal + if (!state.current_board.isGoal()){ + //If not, get the neighbours. + state_neighbours = state.current_board.neighbors(); + iter = state_neighbours.iterator(); + + //For each neighbour + Board next_board; + while(iter.hasNext()){ + next_board = iter.next(); + boolean checked = false; + BoardState current_state = state; + for(int i=0;i<1;i++){ + current_state = current_state.prev_state; + if (current_state == null){ + break; + } + else{ + if(next_board.equals(current_state.current_board)){ + checked = true; + break; + } + } + + } + /*CheckedBoard current_checkedBoard = head; + // Check if the board has been checked before + while(current_checkedBoard != null){ + if(next_board.equals(current_checkedBoard.board)){ + checked = true; + break; + } + current_checkedBoard = current_checkedBoard.next; + }*/ + //If not create a new BoardState + if (!checked){ + BoardState new_state = new BoardState(next_board, state, state.moves+1); + //Push to PQ + priority_queue.insert(new_state); + // Put current board in checkedBoard list + /*new_board = new CheckedBoard(); + new_board.board = next_board; + new_board.next = head; + head = new_board;*/ + } + } + }else{ + //Otherwise, break + break; + } + + } + + if (!priority_queue_twin.isEmpty()){ + // REPEAT FOR THE TWIN + state = priority_queue_twin.delMin(); + + //check isGoal + if (!state.current_board.isGoal()){ + //If not, get the neighbours. + state_neighbours = state.current_board.neighbors(); + iter = state_neighbours.iterator(); + //For each neighbour, check if in checkedState + Board next_board; + while(iter.hasNext()){ + next_board = iter.next(); + boolean checked = false; + BoardState current_state = state; + for(int i=0;i<1;i++){ + current_state = current_state.prev_state; + if (current_state == null){ + break; + } + else{ + if(next_board.equals(current_state.current_board)){ + checked = true; + break; + } + } + } + /*CheckedBoard current_checkedBoard = head_twin; + while(current_checkedBoard != null){ + if(next_board.equals(current_checkedBoard.board)){ + checked = true; + break; + } + current_checkedBoard = current_checkedBoard.next; + }*/ + //If not create a new BoardState, set prev to this + if (!checked){ + BoardState new_state = new BoardState(next_board, state, state.moves+1); + //Push to PQ + priority_queue_twin.insert(new_state); + // Put current board in checkedBoard + /*new_board = new CheckedBoard(); + new_board.board = state.current_board; + new_board.next = head_twin; + head_twin = new_board;*/ + } + } + }else{ + //Otherwise, break + break; + } + } + + + } + + // The puzzle must be solvable for either one of them + // Check if the starting board is original or twin + // If original, solvable; otherwise no. + BoardState current_state = state; + while(current_state.prev_state != null){ + current_state = current_state.prev_state; + } + solvable = current_state.current_board.equals(initialBoardState.current_board); + if (solvable){ + final_state = state; + }else{ + final_state = initialBoardState; + } + + + } + + // is the initial board solvable? (see below) + public boolean isSolvable(){ + return solvable; + } + + // min number of moves to solve initial board + public int moves(){ + if (solvable) + return final_state.moves; + return -1; + } + + // sequence of boards in a shortest solution + public Iterable solution(){ + if (solvable) + return new solutionIterable(); + return null; + } + + private class solutionIterable implements Iterable{ + Board[] solution_boards; + + public solutionIterable(){ + //generate the iterable by tracing back from + //the final BoardState using prev_board recursively + //Number fo recursions determined by moves; + solution_boards = new Board[final_state.moves+1]; + BoardState current_state = final_state; + for(int i=final_state.moves;i>=0;i--){ + solution_boards[i] = current_state.current_board; + current_state = current_state.prev_state; + } + + } + + public Iterator iterator(){ + return new solutionIterator(); + } + + private class solutionIterator implements Iterator{ + int current = 0; + + public boolean hasNext(){ + return current < solution_boards.length; + } + + public void remove(){ + throw new UnsupportedOperationException("Remove operation is not supported."); + } + + public Board next(){ + if (!hasNext()){ + throw new NoSuchElementException("No next item."); + } + return solution_boards[current++]; + } + } + } + + // test client (see below) + public static void main(String[] args){// create initial board from file + In in = new In(args[0]); + int n = in.readInt(); + int[][] tiles = new int[n][n]; + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + tiles[i][j] = in.readInt(); + Board initial = new Board(tiles); + StdOut.println(initial.toString()); + + // solve the puzzle + Solver solver = new Solver(initial); + + // print solution to standard output + if (!solver.isSolvable()) + StdOut.println("No solution possible"); + else { + StdOut.println("Minimum number of moves = " + solver.moves()); + for (Board board : solver.solution()) + StdOut.println(board); + } + } + +} \ No newline at end of file diff --git a/Week5/src/KdTree.java b/Week5/src/KdTree.java new file mode 100644 index 0000000..a6b5d5e --- /dev/null +++ b/Week5/src/KdTree.java @@ -0,0 +1,473 @@ +import edu.princeton.cs.algs4.RectHV; +import edu.princeton.cs.algs4.Point2D; +import edu.princeton.cs.algs4.StdDraw; +import edu.princeton.cs.algs4.In; + +import java.util.Iterator; + +public class KdTree { + private final int VERTICAL = 0; + private final int HORIZONTAL = 1; + + private class pointNode{ + public Point2D p; + public pointNode left = null; + public pointNode right = null; + public int line_type; + } + + private pointNode root; + private int n_points; + + // construct an empty set of points + public KdTree(){ + root = null; + n_points = 0; + } + + // is the set empty? + public boolean isEmpty(){ + return (root == null); + } + // number of points in the set + public int size(){ + return n_points; + } + // add the point to the set (if it is not already in the set) + public void insert(Point2D p){ + if (p == null){ + throw new IllegalArgumentException("Point is null."); + } + + if (isEmpty()){ + root = new pointNode(); + root.p = p; + root.line_type = VERTICAL; + n_points++; + }else{ + add(p, root); + } + } + + private void add(Point2D p, pointNode node){ + // Check if current node is p + if (node.p.equals(p)){ + return; + } + + if (node.line_type == VERTICAL){ + //compare x to decide traversal direction + if (p.x() < node.p.x()){ + // check if the direction is null + if (node.left == null){ + // insert node if null + node.left = new pointNode(); + node.left.p = p; + node.left.line_type = HORIZONTAL; + n_points++; + }else{ + // recurse add if not + add(p, node.left); + } + }else{ + if (node.right == null){ + node.right = new pointNode(); + node.right.p = p; + node.right.line_type = HORIZONTAL; + n_points++; + }else{ + // recurse add if not + add(p, node.right); + } + } + }else{ + if (p.y() < node.p.y()){ + // check if the direction is null + if (node.left == null){ + // insert node if null + node.left = new pointNode(); + node.left.p = p; + node.left.line_type = VERTICAL; + n_points++; + }else{ + // recurse add if not + add(p, node.left); + } + }else{ + if (node.right == null){ + node.right = new pointNode(); + node.right.p = p; + node.right.line_type = VERTICAL; + n_points++; + }else{ + // recurse add if not + add(p, node.right); + } + } + } + } + + // does the set contain point p? + public boolean contains(Point2D p){ + if (p == null){ + throw new IllegalArgumentException("Point is null."); + } + + if (isEmpty()){ + return false; + }else{ + return search(p, root); + } + } + + private boolean search(Point2D p, pointNode node){ + // Check if current node is p + if (node.p.equals(p)){ + return true; + } + + if (node.line_type == VERTICAL){ + //compare x to decide traversal direction + if (p.x() < node.p.x()){ + if (node.left != null) + return search(p, node.left); + return false; + }else{ + if (node.right != null) + return search(p, node.right); + return false; + } + }else{ + if (p.y() < node.p.y()){ + if (node.left != null) + return search(p, node.left); + return false; + }else{ + if (node.right != null) + return search(p, node.right); + return false; + } + } + } + + // draw all points to standard draw + public void draw(){ + if (!isEmpty()){ + inorder_draw(root, 0, 0, 1, 1); + } + //StdDraw.show(); + + } + + private void inorder_draw(pointNode node, double xmin, double ymin, double xmax, double ymax){ + if (node.left != null){ + if(node.line_type == VERTICAL){ + inorder_draw(node.left, xmin, ymin, node.p.x(), ymax); + }else{ + inorder_draw(node.left, xmin, ymin, xmax, node.p.y()); + } + } + + //Insert draw routine + StdDraw.setPenRadius(0.005); + if(node.line_type == VERTICAL){ + StdDraw.setPenColor(StdDraw.RED); + StdDraw.line(node.p.x(), ymin, node.p.x(), ymax); + }else{ + StdDraw.setPenColor(StdDraw.BLUE); + StdDraw.line(xmin, node.p.y(), xmax, node.p.y()); + } + StdDraw.setPenColor(StdDraw.BLACK); + StdDraw.setPenRadius(0.01); + StdDraw.point(node.p.x(), node.p.y()); + + if (node.right != null){ + if(node.line_type == VERTICAL){ + inorder_draw(node.right, node.p.x(), ymin, xmax, ymax); + }else{ + inorder_draw(node.right, xmin, node.p.y(), xmax, ymax); + } + + } + + } + + // all points that are inside the rectangle (or on the boundary) + public Iterable range(RectHV rect){ + if (rect == null){ + throw new IllegalArgumentException("Rect is null."); + } + + if (!isEmpty()){ + Point2D[] p_array = new Point2D[n_points]; + int[] n = new int[1]; + n[0] = 0; + range_search(root, rect, p_array, n, 0, 0, 1, 1); + + return new point_iterable(p_array, n[0]); + + } + return null; + + + } + + private void range_search(pointNode node, RectHV rect, Point2D[] p_array, int[] n, double xmin, double ymin, double xmax, double ymax){ + // Check for point + if (rect.contains(node.p)){ + p_array[n[0]++] = node.p; + } + if (node.left != null){ + // Check for rect intersection + // Then traverse + if(node.line_type == VERTICAL){ + if (rect.intersects(new RectHV(xmin, ymin, node.p.x(), ymax))) + range_search(node.left, rect, p_array, n, xmin, ymin, node.p.x(), ymax); + }else{ + if (rect.intersects(new RectHV(xmin, ymin, xmax, node.p.y()))) + range_search(node.left, rect, p_array, n, xmin, ymin, xmax, node.p.y()); + } + } + + if (node.right != null){ + if(node.line_type == VERTICAL){ + if (rect.intersects(new RectHV(node.p.x(), ymin, xmax, ymax))) + range_search(node.right, rect, p_array, n, node.p.x(), ymin, xmax, ymax); + }else{ + if (rect.intersects(new RectHV(xmin, node.p.y(), xmax, ymax))) + range_search(node.right, rect, p_array, n, xmin, node.p.y(), xmax, ymax); + } + + } + + } + + // a nearest neighbor in the set to point p; null if the set is empty + public Point2D nearest(Point2D p){ + if (p == null){ + throw new IllegalArgumentException("Point is null."); + } + + if (isEmpty()){ + return null; + } + + + Point2D[] nearest_p = new Point2D[1]; + nearest_p[0] = root.p; + double[] shortest_dist = new double[1]; + shortest_dist[0] = p.distanceSquaredTo(root.p); + + //System.out.println(root.p.toString()); + + // Update the nearest point so far if there is none or one is found + + double straight_dist = Math.pow(p.x() - root.p.x(), 2); + boolean to_left = p.x() < root.p.x(); + + //can probably generalise this + if (to_left){ + if (root.left != null) + nearest_neighbour(root.left , p, nearest_p, shortest_dist, 0, 0, root.p.x(), 1); + + if (root.right != null && straight_dist < shortest_dist[0]){ + // Need a separate check for other side + // Get the two intersecting points which the short distance circle + // makes with the root vertical line + double x_dist = Math.abs(root.p.x() - p.x()); + double y_dist = Math.sqrt(shortest_dist[0] - x_dist * x_dist); + double y_min = Math.max(p.y() - y_dist, 0); + double y_max = Math.min(p.y() + y_dist, 1); + + nearest_neighbour(root.right, p, nearest_p, shortest_dist, root.p.x(), 0, 1, 1); + } + }else{ + if (root.right != null) + nearest_neighbour(root.right , p, nearest_p, shortest_dist, root.p.x(), 0, 1, 1); + + if (root.left != null && straight_dist < shortest_dist[0]){ + // Need a separate check for other side + // Get the two intersecting points which the short distance circle + // makes with the root vertical line + + nearest_neighbour(root.left, p, nearest_p, shortest_dist, 0, 0, root.p.x(), 1); + } + + } + return nearest_p[0]; + } + + private void nearest_neighbour(pointNode node, Point2D p, Point2D[] nearest_p, double[] shortest_dist, double xmin, double ymin, double xmax, double ymax){ + //System.out.println(node.p.toString()); + + // This is a check for the points opposite of the query point + double dist = p.distanceSquaredTo(node.p); + // Update the nearest point so far if there is none or one is found + if (dist < shortest_dist[0]){ + nearest_p[0] = node.p; + shortest_dist[0] = dist; + } + + RectHV rect; + double point_dist; + // This is the rigorous version of the checking + // Using on the distance of the query point to the rectangle enclosed by the spillter + // Comparing to the shortest distance to see if there is an overlap + + if (node.line_type == HORIZONTAL){ + + // Have a check which direction to go + boolean to_left = p.y() < node.p.y(); + + if (to_left){ + rect = new RectHV(xmin, ymin , xmax, node.p.y()); + point_dist = rect.distanceSquaredTo(p); + if (node.left != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist, xmin, ymin , xmax, node.p.y()); + } + + rect = new RectHV(xmin, node.p.y() , xmax, ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.right != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist, xmin, node.p.y() , xmax, ymax); + } + + }else{ + rect = new RectHV(xmin, node.p.y() , xmax, ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.right != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist, xmin, node.p.y() , xmax, ymax); + } + rect = new RectHV(xmin, ymin , xmax, node.p.y()); + point_dist = rect.distanceSquaredTo(p); + if (node.left != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist, xmin, ymin , xmax, node.p.y()); + } + } + + + }else{ + // For vertical line + boolean to_left = p.x() < node.p.x(); + + if (to_left){ + + rect = new RectHV(xmin, ymin , node.p.x(), ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.left != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist, xmin, ymin , node.p.x(), ymax); + } + rect = new RectHV(node.p.x(), ymin , xmax, ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.right != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist, node.p.x(), ymin , xmax, ymax); + } + }else{ + rect = new RectHV(node.p.x(), ymin , xmax, ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.right != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist, node.p.x(), ymin , xmax, ymax); + } + rect = new RectHV(xmin, ymin , node.p.x(), ymax); + point_dist = rect.distanceSquaredTo(p); + if (node.left != null && point_dist < shortest_dist[0]){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist, xmin, ymin , node.p.x(), ymax); + } + } + + } + } + + /*private void nearest_neighbour(pointNode node, Point2D p, Point2D[] nearest_p, double[] shortest_dist){ + System.out.println(node.p.toString()); + + double dist = p.distanceSquaredTo(node.p); + // Update the nearest point so far if one is found + if (dist < shortest_dist[0]){ + nearest_p[0] = node.p; + shortest_dist[0] = dist; + } + + // This is the simplified version of the checking + // Using on the straight line dist from the splitter + // Make use of the fact that the query point is on the same side + // Check which direction to go on the splitter + double straight_dist; + boolean to_left; + if(node.line_type == VERTICAL){ + straight_dist = Math.pow(p.x() - node.p.x(), 2); + to_left = p.x() < node.p.x(); + }else{ + straight_dist = Math.pow(p.y() - node.p.y(), 2); + to_left = p.y() < node.p.y(); + } + + // Go on the side with the point + // After checking the side of the splitter with the point, + // Only go to the other side if the straight distance is less than the shortest distance + // This is because the circle by the shortest distance overlaps both side of the spiltter if so + if (to_left){ + if (node.left != null){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist); + } + if (node.right != null && straight_dist < shortest_dist[0]){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist); + } + }else{ + if (node.right != null){ + nearest_neighbour(node.right, p, nearest_p, shortest_dist); + } + if (node.left != null && straight_dist < shortest_dist[0]){ + nearest_neighbour(node.left, p, nearest_p, shortest_dist); + } + } + } + */ + private class point_iterable implements Iterable { + Point2D[] points_array; + + public point_iterable(Point2D[] points, int n){ + points_array = new Point2D[n]; + for(int i=0;i iterator(){ + return new point_iterator(); + } + + private class point_iterator implements Iterator{ + private int current = 0; + + public boolean hasNext(){ + return current < points_array.length; + } + + public Point2D next(){ + return points_array[current++]; + } + } + + } + + // unit testing of the methods (optional) + public static void main(String[] args) { + + KdTree kdtree = new KdTree(); + In in = new In("test_traversal.txt"); + while (!in.isEmpty()) { + double x = in.readDouble(); + double y = in.readDouble(); + Point2D p = new Point2D(x, y); + + + System.out.println("inserting"); + kdtree.insert(p); + System.out.println(kdtree.size()); + } + System.out.println(kdtree.contains(new Point2D(0.48, 0.04))); + System.out.println(kdtree.contains(new Point2D(-2, 0))); + System.out.println("Nearest: " + kdtree.nearest(new Point2D(0.46875, 0.125))); + } + } \ No newline at end of file diff --git a/Week5/src/KdTreeVisualizer.java b/Week5/src/KdTreeVisualizer.java new file mode 100644 index 0000000..8628800 --- /dev/null +++ b/Week5/src/KdTreeVisualizer.java @@ -0,0 +1,54 @@ +/****************************************************************************** + * Compilation: javac KdTreeVisualizer.java + * Execution: java KdTreeVisualizer + * Dependencies: KdTree.java + * + * Add the points that the user clicks in the standard draw window + * to a kd-tree and draw the resulting kd-tree. + * + ******************************************************************************/ + +import edu.princeton.cs.algs4.Point2D; +import edu.princeton.cs.algs4.RectHV; +import edu.princeton.cs.algs4.StdDraw; +import edu.princeton.cs.algs4.StdOut; +import edu.princeton.cs.algs4.In; + +public class KdTreeVisualizer { + + public static void main(String[] args) { + RectHV rect = new RectHV(0.0, 0.0, 1.0, 1.0); + StdDraw.enableDoubleBuffering(); + KdTree kdtree = new KdTree(); + /*String filename = args[0]; + In in = new In(filename); + while (!in.isEmpty()) { + double x = in.readDouble(); + double y = in.readDouble(); + Point2D p = new Point2D(x, y); + kdtree.insert(p); + }*/ + while (true) { + if (StdDraw.isMousePressed()) { + double x = StdDraw.mouseX(); + double y = StdDraw.mouseY(); + StdOut.printf("%8.6f %8.6f\n", x, y); + Point2D p = new Point2D(x, y); + if (rect.contains(p)) { + //StdOut.printf("%8.6f %8.6f\n", x, y); + kdtree.insert(p); + StdDraw.clear(); + kdtree.draw(); + StdDraw.show(); + } + } + StdDraw.pause(20); + } + /*StdDraw.setPenColor(StdDraw.GREEN); + StdDraw.setPenRadius(0.01); + StdDraw.point(0.46875, 0.125); + kdtree.draw(); + StdDraw.show();*/ + + } +} diff --git a/Week5/src/NearestNeighborVisualizer.java b/Week5/src/NearestNeighborVisualizer.java new file mode 100644 index 0000000..1a16164 --- /dev/null +++ b/Week5/src/NearestNeighborVisualizer.java @@ -0,0 +1,64 @@ +/****************************************************************************** + * Compilation: javac NearestNeighborVisualizer.java + * Execution: java NearestNeighborVisualizer input.txt + * Dependencies: PointSET.java KdTree.java + * + * Read points from a file (specified as a command-line argument) and + * draw to standard draw. Highlight the closest point to the mouse. + * + * The nearest neighbor according to the brute-force algorithm is drawn + * in red; the nearest neighbor using the kd-tree algorithm is drawn in blue. + * + ******************************************************************************/ + +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.Point2D; +import edu.princeton.cs.algs4.StdDraw; + +public class NearestNeighborVisualizer { + + public static void main(String[] args) { + + // initialize the two data structures with point from file + String filename = args[0]; + In in = new In(filename); + PointSET brute = new PointSET(); + KdTree kdtree = new KdTree(); + while (!in.isEmpty()) { + double x = in.readDouble(); + double y = in.readDouble(); + Point2D p = new Point2D(x, y); + kdtree.insert(p); + brute.insert(p); + } + + // process nearest neighbor queries + StdDraw.enableDoubleBuffering(); + while (true) { + + // the location (x, y) of the mouse + double x = StdDraw.mouseX(); + double y = StdDraw.mouseY(); + Point2D query = new Point2D(x, y); + + // draw all of the points + StdDraw.clear(); + StdDraw.setPenColor(StdDraw.BLACK); + StdDraw.setPenRadius(0.01); + brute.draw(); + + // draw in red the nearest neighbor (using brute-force algorithm) + StdDraw.setPenRadius(0.03); + StdDraw.setPenColor(StdDraw.RED); + brute.nearest(query).draw(); + StdDraw.setPenRadius(0.02); + + // draw in blue the nearest neighbor (using kd-tree algorithm) + StdDraw.setPenColor(StdDraw.BLUE); + kdtree.nearest(query).draw(); + + StdDraw.show(); + StdDraw.pause(40); + } + } +} diff --git a/Week5/src/PointSET.java b/Week5/src/PointSET.java new file mode 100644 index 0000000..edac731 --- /dev/null +++ b/Week5/src/PointSET.java @@ -0,0 +1,125 @@ +import edu.princeton.cs.algs4.RectHV; +import edu.princeton.cs.algs4.Point2D; +import edu.princeton.cs.algs4.SET; + +import java.util.Iterator; + +public class PointSET { + private SET set_points; + + // construct an empty set of points + public PointSET(){ + set_points = new SET(); + } + + // is the set empty? + public boolean isEmpty(){ + return set_points.isEmpty(); + } + // number of points in the set + public int size(){ + return set_points.size(); + } + // add the point to the set (if it is not already in the set) + public void insert(Point2D p){ + if (p == null){ + throw new IllegalArgumentException("Point is null."); + } + if (!set_points.contains(p)) + set_points.add(p); + } + // does the set contain point p? + public boolean contains(Point2D p){ + return set_points.contains(p); + } + // draw all points to standard draw + public void draw(){ + Iterator iter = set_points.iterator(); + + while (iter.hasNext()){ + iter.next().draw(); + } + //StdDraw.show(); + + } + // all points that are inside the rectangle (or on the boundary) + public Iterable range(RectHV rect){ + if (rect == null){ + throw new IllegalArgumentException("Rect is null."); + } + Iterator iter = set_points.iterator(); + + Point2D p; + Point2D[] p_array = new Point2D[set_points.size()]; + int n = 0; + while (iter.hasNext()){ + p = iter.next(); + if (rect.contains(p)){ + p_array[n++] = p; + } + } + + return new point_iterable(p_array, n); + + } + // a nearest neighbor in the set to point p; null if the set is empty + public Point2D nearest(Point2D p){ + if (p == null){ + throw new IllegalArgumentException("Point is null."); + } + + if (set_points.isEmpty()){ + return null; + } + + Point2D current_p; + double current_dist = 0; + Point2D nearest_p = null; + double shortest_dist = -1; + + Iterator iter = set_points.iterator(); + while (iter.hasNext()){ + current_p = iter.next(); + current_dist = p.distanceSquaredTo(current_p); + if (shortest_dist < 0 || current_dist < shortest_dist){ + nearest_p = current_p; + shortest_dist = p.distanceSquaredTo(current_p); + } + } + + return nearest_p; + } + + private class point_iterable implements Iterable { + Point2D[] points_array; + + public point_iterable(Point2D[] points, int n){ + points_array = new Point2D[n]; + for(int i=0;i iterator(){ + return new point_iterator(); + } + + private class point_iterator implements Iterator{ + private int current = 0; + + public boolean hasNext(){ + return current < points_array.length; + } + + public Point2D next(){ + return points_array[current++]; + } + } + + } + + // unit testing of the methods (optional) + public static void main(String[] args) { + + } + } \ No newline at end of file diff --git a/Week5/src/RangeSearchVisualizer.java b/Week5/src/RangeSearchVisualizer.java new file mode 100644 index 0000000..ce1e556 --- /dev/null +++ b/Week5/src/RangeSearchVisualizer.java @@ -0,0 +1,99 @@ +/****************************************************************************** + * Compilation: javac RangeSearchVisualizer.java + * Execution: java RangeSearchVisualizer input.txt + * Dependencies: PointSET.java KdTree.java + * + * Read points from a file (specified as a command-line argument) and + * draw to standard draw. Also draw all of the points in the rectangle + * the user selects by dragging the mouse. + * + * The range search results using the brute-force algorithm are drawn + * in red; the results using the kd-tree algorithms are drawn in blue. + * + ******************************************************************************/ + +import edu.princeton.cs.algs4.In; +import edu.princeton.cs.algs4.Point2D; +import edu.princeton.cs.algs4.RectHV; +import edu.princeton.cs.algs4.StdDraw; + +public class RangeSearchVisualizer { + + public static void main(String[] args) { + + // initialize the data structures from file + String filename = args[0]; + In in = new In(filename); + PointSET brute = new PointSET(); + KdTree kdtree = new KdTree(); + while (!in.isEmpty()) { + double x = in.readDouble(); + double y = in.readDouble(); + Point2D p = new Point2D(x, y); + kdtree.insert(p); + brute.insert(p); + } + + double x0 = 0.0, y0 = 0.0; // initial endpoint of rectangle + double x1 = 0.0, y1 = 0.0; // current location of mouse + boolean isDragging = false; // is the user dragging a rectangle + + // draw the points + StdDraw.clear(); + StdDraw.setPenColor(StdDraw.BLACK); + StdDraw.setPenRadius(0.01); + brute.draw(); + StdDraw.show(); + + // process range search queries + StdDraw.enableDoubleBuffering(); + while (true) { + + // user starts to drag a rectangle + if (StdDraw.isMousePressed() && !isDragging) { + x0 = x1 = StdDraw.mouseX(); + y0 = y1 = StdDraw.mouseY(); + isDragging = true; + } + + // user is dragging a rectangle + else if (StdDraw.isMousePressed() && isDragging) { + x1 = StdDraw.mouseX(); + y1 = StdDraw.mouseY(); + } + + // user stops dragging rectangle + else if (!StdDraw.isMousePressed() && isDragging) { + isDragging = false; + } + + // draw the points + StdDraw.clear(); + StdDraw.setPenColor(StdDraw.BLACK); + StdDraw.setPenRadius(0.01); + brute.draw(); + + // draw the rectangle + RectHV rect = new RectHV(Math.min(x0, x1), Math.min(y0, y1), + Math.max(x0, x1), Math.max(y0, y1)); + StdDraw.setPenColor(StdDraw.BLACK); + StdDraw.setPenRadius(); + rect.draw(); + + // draw the range search results for brute-force data structure in red + StdDraw.setPenRadius(0.03); + StdDraw.setPenColor(StdDraw.RED); + for (Point2D p : brute.range(rect)) + p.draw(); + + // draw the range search results for kd-tree in blue + StdDraw.setPenRadius(0.02); + StdDraw.setPenColor(StdDraw.BLUE); + for (Point2D p : kdtree.range(rect)) + p.draw(); + + StdDraw.show(); + StdDraw.pause(20); + } + } +}