Description: |
This is one algorithm where dynamic create a binary tree printer no need to code changes. I am also thinking how to create a mWay tree printer But that is not generating
|
Code 1: |
/**
*
* @author kartik
* Blog java2blogs.blogspot.com
* This is one algorithm where dynamic create a binary tree printer no need to code changes
*/
package com.kartik.tree;
import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * The {@code BTreePrinter} class provides a basic capability for creating * drawings with your programs. It uses a simple graphics model that allows you * to create drawings consisting of points, lines, squares, circles, and other * geometric shapes in a window on your computer and to save the drawings to a * file. Standard drawing also includes facilities for text, color, pictures, * and animation, along with user interaction via the keyboard and mouse. * <p> * <b>Getting started.</b> To use standard drawing, you must have * <tt>BTreePrinter.class</tt> in your Java classpath. If you used our autoinstaller, * you should be all set. Otherwise, download <a href = * "http://www.kartikchandramandal.blogspot.in/2015/10/how-to-draw-atree-from-array-of-integer.html" * >BTreePrinter.java</a> and put a copy in your working directory. * <p> * Now, type the following short program into your editor: * * <pre> * public class TestBTreePrinter { * public static void main(String[] args) { * Integer[] array = { 45, 23, 11, 89, 77, 98, 4, 28, 65, 43 }; * BTreePrinter.printNode(BTreePrinter.drawTree(array)); * * Character[] arrayChar = { 'K', 'A', 'R', 'T', 'I', 'K', 'M', 'A', 'N', * 'D', 'A', 'L' }; * BTreePrinter.printNode(BTreePrinter.drawTree(arrayChar)); * } * } * </pre> * * If you compile and execute the program, you should see a window appear with a * thick magenta line and a blue point. This program illustrates the two main * types of methods in standard drawing—methods that draw geometric shapes * and methods that control drawing parameters. The methods * {@code BTreePrinter.line()} and {@code BTreePrinter.point()} draw lines and * points; the methods {@code BTreePrinter.setPenRadius()} and * {@code BTreePrinter.setPenColor()} control the line thickness and color. * <p> * <b>Points and lines.</b> You can draw points and line segments with the * following methods: * <ul> * <li> {@link #point(double x, double y)} * <li> {@link #line(double x1, double y1, double x2, double y2)} * </ul> * <p> * The <em>x</em>- and <em>y</em>-coordinates must be in the drawing area * (between 0 and 1 and by default) or the points and lines will not be visible. * <p> * <b>Squares, circles, rectangles, and ellipses.</b> You can draw squares, * circles, rectangles, and ellipses using the following methods: * <ul> * <li> {@link #circle(double x, double y, double radius)} * <li> * {@link #ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis)} * <li> {@link #square(double x, double y, double radius)} * <li> {@link #rectangle(double x, double y, double halfWidth, double halfHeight)} * </ul> * <p> * All of these methods take as arguments the location and size of the shape. * The location is always specified by the <em>x</em>- and <em>y</em> * -coordinates of its <em>center</em>. The size of a circle is specified by its * radius and the size of an ellipse is specified by the lengths of its * semi-major and semi-minor axes. The size of a square or rectangle is * specified by its half-width or half-height. The convention for drawing * squares and rectangles is parallel to those for drawing circles and ellipses, * but may be unexpected to the uninitiated. * <p> * The methods above trace outlines of the given shapes. The following methods * draw filled versions: * <ul> * <li> {@link #filledCircle(double x, double y, double radius)} * <li> * {@link #filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis)} * <li> {@link #filledSquare(double x, double y, double radius)} * <li> * {@link #filledRectangle(double x, double y, double halfWidth, double halfHeight)} * </ul> * <p> * <b>Circular arcs.</b> You can draw circular arcs with the following method: * <ul> * <li> {@link #arc(double x, double y, double radius, double angle1, double angle2)} * </ul> * <p> * The arc is from the circle centered at (<em>x</em>, <em>y</em>) of the * specified radius. The arc extends from angle1 to angle2. By convention, the * angles are <em>polar</em> (counterclockwise angle from the <em>x</em>-axis) * and represented in degrees. For example, * {@code BTreePrinter.arc(0.0, 0.0, 1.0, 0, 90)} draws the arc of the unit circle * from 3 o'clock (0 degrees) to 12 o'clock (90 degrees). * <p> * <b>Polygons.</b> You can draw polygons with the following methods: * <ul> * <li> {@link #polygon(double[] x, double[] y)} * <li> {@link #filledPolygon(double[] x, double[] y)} * </ul> * <p> * The points in the polygon are ({@code x[i]}, {@code y[i]}). For example, the * following code fragment draws a filled diamond with vertices (0.1, 0.2), * (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1): * * <pre> * double[] x = { 0.1, 0.2, 0.3, 0.2 }; * double[] y = { 0.2, 0.3, 0.2, 0.1 }; * BTreePrinter.filledPolygon(x, y); * </pre> * <p> * <b>Pen size.</b> The pen is circular, so that when you set the pen radius to * <em>r</em> and draw a point, you get a circle of radius <em>r</em>. Also, * lines are of thickness 2<em>r</em> and have rounded ends. The default pen * radius is 0.005 and is not affected by coordinate scaling. This default pen * radius is about 1/200 the width of the default canvas, so that if you draw * 100 points equally spaced along a horizontal or vertical line, you will be * able to see individual circles, but if you draw 200 such points, the result * will look like a line. * <ul> * <li> {@link #setPenRadius(double radius)} * </ul> * <p> * For example, {@code BTreePrinter.setPenRadius(0.025)} makes the thickness of the * lines and the size of the points to be five times the 0.005 default. To draw * points with the minimum possible radius (one pixel on typical displays), set * the pen radius to 0.0. * <p> * <b>Pen color.</b> All geometric shapes (such as points, lines, and circles) * are drawn using the current pen color. By default, it is black. You can * change the pen color with the following methods: * <ul> * <li> {@link #setPenColor(int red, int green, int blue)} * <li> {@link #setPenColor(Color color)} * </ul> * <p> * The first method allows you to specify colors using the RGB color system. * This <a href = "http://johndyer.name/lab/colorpicker/">color picker</a> is a * convenient way to find a desired color. The second method allows you to * specify colors using the {@link Color} data type that is discussed in Chapter * 3. Until then, you can use this method with one of these predefined colors in * standard drawing: {@link #BLACK}, {@link #BLUE}, {@link #CYAN}, * {@link #DARK_GRAY}, {@link #GRAY}, {@link #GREEN}, {@link #LIGHT_GRAY}, * {@link #MAGENTA}, {@link #ORANGE}, {@link #PINK}, {@link #RED}, * {@link #WHITE}, and {@link #YELLOW}. For example, * {@code BTreePrinter.setPenColor(BTreePrinter.MAGENTA)} sets the pen color to magenta. * <p> * <b>Canvas size.</b> By default, all drawing takes places in a 512-by-512 * canvas. The canvas does not include the window title or window border. You * can change the size of the canvas with the following method: * <ul> * <li> {@link #setCanvasSize(int width, int height)} * </ul> * <p> * This sets the canvas size to be <em>width</em>-by-<em>height</em> pixels. It * also erases the current drawing and resets the coordinate system, pen radius, * pen color, and font back to their default values. Ordinarly, this method is * called once, at the very beginning of a program. For example, * {@code BTreePrinter.setCanvasSize(800, 800)} sets the canvas size to be 800-by-800 * pixels. * <p> * <b>Canvas scale and coordinate system.</b> By default, all drawing takes * places in the unit square, with (0, 0) at lower left and (1, 1) at upper * right. You can change the default coordinate system with the following * methods: * <ul> * <li> {@link #setXscale(double xmin, double xmax)} * <li> {@link #setYscale(double ymin, double ymax)} * <li> {@link #setScale(double min, double max)} * </ul> * <p> * The arguments are the coordinates of the minimum and maximum <em>x</em>- or * <em>y</em>-coordinates that will appear in the canvas. For example, if you * wish to use the default coordinate system but leave a small margin, you can * call {@code BTreePrinter.setScale(-.05, 1.05)}. * <p> * These methods change the coordinate system for subsequent drawing commands; * they do not affect previous drawings. These methods do not change the canvas * size; so, if the <em>x</em>- and <em>y</em>-scales are different, squares * will become rectangles and circles will become ellipsoidal. * <p> * <b>Text.</b> You can use the following methods to annotate your drawings with * text: * <ul> * <li> {@link #text(double x, double y, String text)} * <li> {@link #text(double x, double y, String text, double degrees)} * <li> {@link #textLeft(double x, double y, String text)} * <li> {@link #textRight(double x, double y, String text)} * </ul> * <p> * The first two methods write the specified text in the current font, centered * at (<em>x</em>, <em>y</em>). The second method allows you to rotate the text. * The last two methods either left- or right-align the text at (<em>x</em>, * <em>y</em>). * <p> * The default font is a Sans Serif font with point size 16. You can use the * following method to change the font: * <ul> * <li> {@link #setFont(Font font)} * </ul> * <p> * You use the {@link Font} data type to specify the font. This allows you to * choose the face, size, and style of the font. For example, the following code * fragment sets the font to Arial Bold, 60 point. * * <pre> * Font font = new Font("Arial", Font.BOLD, 60); * BTreePrinter.setFont(font); * BTreePrinter.text(0.5, 0.5, "Hello, World"); * </pre> * <p> * <b>Images.</b> You can use the following methods to add images to your * drawings: * <ul> * <li> {@link #picture(double x, double y, String filename)} * <li> {@link #picture(double x, double y, String filename, double degrees)} * <li> {@link #picture(double x, double y, String filename, double width)} * <li> * {@link #picture(double x, double y, String filename, double width, double degrees)} * </ul> * <p> * These methods draw the specified image, centered at (<em>x</em>, <em>y</em>). * The supported image formats are JPEG, PNG, and GIF. The image will display at * its native size, independent of the coordinate system. Optionally, you can * rotate the image a specified number of degrees counterclockwise or rescale it * to fit inside a width-by-height pixel bounding box. * <p> * <b>Saving to a file.</b> You save your image to a file using the * <em>File -> Save</em> menu option. You can also save a file programatically * using the following method: * <ul> * <li> {@link #save(String filename)} * </ul> * <p> * The supported image formats are JPEG and PNG. The filename must have either * the extension .jpg or .png. We recommend using PNG for drawing that consist * solely of geometric shapes and JPEG for drawings that contains pictures. * <p> * <b>Clearing the canvas.</b> To clear the entire drawing canvas, you can use * the following methods: * <ul> * <li> {@link #clear()} * <li> {@link #clear(Color color)} * </ul> * <p> * The first method clears the canvas to white; the second method allows you to * specify a color of your choice. For example, * {@code BTreePrinter.clear(BTreePrinter.LIGHT_GRAY)} clears the canvas to a shade of * gray. Most often, these two methods are used in conjunction with animation * mode. * <p> * <b>Animations.</b> Animation mode is one of the trickier features of standard * drawing. The following two methods control the way in which objects are * drawn: * <ul> * <li> {@link #show()} * <li> {@link #show(int t)} * </ul> * <p> * By default, animation mode is off, which means that as soon as you call a * drawing method—such as {@code point()} or {@code line()}—the * results appear on the screen. {@code BTreePrinter.show()} turns off animation * mode. * <p> * You can call {@link #show(int t)} to turn on animation mode. This defers all * drawing to the screen until you are aready to display them. Once you are * ready to display them, you call {@link #show(int t)} again, which transfer * the offscreen drawing to the screen and waits for the specified number of * milliseconds. In conjuction with {@link #clear()}, you can create the * illusion of movement by iterating the following three steps: * <ul> * <li>Clear the background canvas. * <li>Draw geometric objects. * <li>Show the drawing and wait for a short while. * </ul> * <p> * Waiting for a short while is essential; otherwise, the drawing will appear * and disappear so quickly that your animation will flicker. * <p> * Here is a simple example of an animation: * <p> * <b>Keyboard and mouse inputs.</b> Standard drawing has very basic support for * keyboard and mouse input. It is much less powerful than most user interface * libraries provide, but also much simpler. You can use the following methods * to intercept mouse events: * <ul> * <li> {@link #mousePressed()} * <li> {@link #mouseX()} * <li> {@link #mouseY()} * </ul> * <p> * The first method tells you whether a mouse button is currently being pressed. * The last two methods tells you the <em>x</em>- and <em>y</em>-coordinates of * the mouse's current position, using the same coordinate system as the canvas * (the unit square, by default). You should use these methods in an animation * loop that waits a short while before trying to poll the mouse for its current * state. You can use the following methods to intercept keyboard events: * <ul> * <li> {@link #hasNextKeyTyped()} * <li> {@link #nextKeyTyped()} * <li> {@link #isKeyPressed(int keycode)} * </ul> * <p> * If the user types lots of keys, they will be saved in a list until you * process them. The first method tells you whether the user has typed a key * (that your program has not yet processed). The second method returns the next * key that the user typed (that your program has not yet processed) and removes * it from the list of saved keystrokes. The third method tells you whether a * key is currently being pressed. * <p> * <b>Accessing control parameters.</b> You can use the following methods to * access the current pen color, pen radius, and font: * <ul> * <li> {@link #getPenColor()} * <li> {@link #getPenRadius()} * <li> {@link #getFont()} * </ul> * <p> * These methods are useful when you want to temporarily change a control * parameter and reset it back to its original value. * <p> * <b>Corner cases.</b> To avoid clutter, the API doesn't explicitly refer to * arguments that are null, infinity, or NaN. * <ul> * <li>Any method that is passed a {@code null} argument will throw a * {@link NullPointerException}. * <li>Except as noted in the APIs, drawing an object outside (or partly * outside) the canvas is permitted—however, only the part of the object * that appears inside the canvas will be visible. * <li>Except as noted in the APIs, all methods accept {@link Double#NaN}, * {@link Double#POSITIVE_INFINITY}, and {@link Double#NEGATIVE_INFINITY} as * arugments. An object drawn with an <em>x</em>- or <em>y</em>-coordinate that * is NaN will behave as if it is outside the canvas, and will not be visible. * </ul> * <p> * <b>Performance tricks.</b> Standard drawing is capable of drawing large * amounts of data. Here are a few tricks and tips: * <ul> * <li>Use <em>animation mode</em> for static drawing with a large number of * objects. That is, call {@code BTreePrinter.show(0)} before and after the sequence * of drawing commands. The bottleneck operation is not drawing the geometric * shapes but rather drawing them to the screen. By using animation mode, you * draw all of the shapes to an offscreen buffer, then copy them all at once to * the screen. * <li>When using <em>animation mode</em>, call {@code show()} only once per * frame, not after drawing each object. * <li>If you call {@code picture()} multiple times with the same filename, Java * will cache the image, so you do not incur the cost of reading from a file * each time. * <li>Do not call {@code setFont()} in an animation loop (unless you really * need to change the font in each iteration). It can cause flicker. * </ul> * <p> * <b>Known bugs and issues.</b> * <ul> * <li>The {@code picture()} methods may not draw the portion of the image that * is inside the canvas if the center point (<em>x</em>, <em>y</em>) is outside * the canvas. This bug appears only on some systems. * <li>Some methods may not draw the portion of the geometric object that is * inside the canvas if the <em>x</em>- or <em>y</em>-coordinates are infinite. * This bug appears only on some systems. * </ul> * <p> * <b>Reference.</b> For additional documentation, see <a * href="http://www.kartikchandramandal.blogspot.in/2015/10/how-to-draw-atree-from-array-of-integer.html">Section 1.5</a> of * <em>Introduction to Programming in Java: An Interdisciplinary Approach</em> * by Kartik Chandra Mandal. * * @author Kartik Chandra Mandal */ public class BTreePrinter<T> { /** * * @param <T> * @param root */ public static <T> void printNode(Node<T> root) { int maxLevel = BTreePrinter.maxLevel(root); printNodeInternal(Collections.singletonList(root), 1, maxLevel); } /** * * @param <T> * @param nodes * @param level * @param maxLevel */ private static <T> void printNodeInternal(List<Node<T>> nodes, int level, int maxLevel) { if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes)) return; int floor = maxLevel - level; int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0))); int firstSpaces = (int) Math.pow(2, (floor)) - 1; int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1; BTreePrinter.printWhitespaces(firstSpaces); List<Node<T>> newNodes = new ArrayList<Node<T>>(); for (Node<T> node : nodes) { if (node != null) { System.out.print(node.data); newNodes.add(node.left); newNodes.add(node.right); } else { newNodes.add(null); newNodes.add(null); System.out.print(" "); } BTreePrinter.printWhitespaces(betweenSpaces); } System.out.println(""); for (int i = 1; i <= endgeLines; i++) { for (int j = 0; j < nodes.size(); j++) { BTreePrinter.printWhitespaces(firstSpaces - i); if (nodes.get(j) == null) { BTreePrinter.printWhitespaces(endgeLines + endgeLines + i + 1); continue; } if (nodes.get(j).left != null) System.out.print("/"); else BTreePrinter.printWhitespaces(1); BTreePrinter.printWhitespaces(i + i - 1); if (nodes.get(j).right != null) System.out.print("\\"); else BTreePrinter.printWhitespaces(1); BTreePrinter.printWhitespaces(endgeLines + endgeLines - i); } System.out.println(""); } printNodeInternal(newNodes, level + 1, maxLevel); } /** * * @param count */ private static void printWhitespaces(int count) { for (int i = 0; i < count; i++) System.out.print(" "); } /** * * @param node * @return */ private static <T> int maxLevel(Node<T> node) { if (node == null) return 0; return Math.max(BTreePrinter.maxLevel(node.left), BTreePrinter.maxLevel(node.right)) + 1; } /** * * @param list * @return */ private static <T> boolean isAllElementsNull(List<T> list) { for (Object object : list) { if (object != null) return false; } return true; } public static <T> Node<T> drawTree(T[] input) { Node<T> node = null; Map<String, Node<T>> mapNode = new HashMap<String, Node<T>>(); int i = 0, c = 0; int count = 0; for (i = 0; i < input.length; i++) { if (i > 0) { for (c = 1; c <= square(i); c++) { String stringKey = "n" + i + c; if (count < input.length) { node = new Node<T>(input[count]); mapNode.put(stringKey, node); } count++; } } else { String stringKey = "n" + i + c; node = new Node<T>(input[count]); mapNode.put(stringKey, node); count++; } } Map<String, Node<T>> sortMapNode = new TreeMap<String, Node<T>>(mapNode); Node<T> root = null; Node<T> prent = null; int countVal = 1; int pointerCount = 0; for (Map.Entry<String, Node<T>> entry : sortMapNode.entrySet()) { String key = entry.getKey(); char p = key.charAt(1); char q = key.charAt(2); int k = Integer.parseInt(String.valueOf(p)); int k1 = Integer.parseInt(String.valueOf(p)); int k2 = Integer.parseInt(String.valueOf(q)); if (k > 0) { String newKey = null; if (k1 % 2 == 0) { if ((k1 + k2) % 2 == 0) { newKey = "n" + (k1 - 1) + (k2 / 2); prent = (Node<T>) mapNode.get(newKey); prent.right = entry.getValue(); countVal++; pointerCount++; } else { if (k2 % 2 == 0) { newKey = "n" + (k1 - 1) + (k2 / 2); prent = (Node<T>) mapNode.get(newKey); prent.right = entry.getValue(); countVal++; pointerCount++; } else { newKey = "n" + (k1 - 1) + countVal; prent = (Node<T>) mapNode.get(newKey); prent.left = entry.getValue(); pointerCount++; } } } else { if ((k1 + k2) % 2 == 0) { if (k1 == 1) { newKey = "n00"; prent = (Node<T>) mapNode.get(newKey); if (k2 == 1) { prent.left = entry.getValue(); } pointerCount++; } else { newKey = "n" + (k1 - 1) + countVal; prent = (Node<T>) mapNode.get(newKey); prent.left = entry.getValue(); pointerCount++; } } else { if (k1 == 1) { newKey = "n00"; prent = (Node<T>) mapNode.get(newKey); if (k2 == 2) { prent.right = entry.getValue(); countVal++; } pointerCount++; } else { if (k2 % 2 == 0) { newKey = "n" + (k1 - 1) + (k2 / 2); prent = (Node<T>) mapNode.get(newKey); prent.right = entry.getValue(); countVal++; pointerCount++; } else { newKey = "n" + (k1 - 1) + countVal; prent = (Node<T>) mapNode.get(newKey); prent.left = entry.getValue(); pointerCount++; } } } } if (pointerCount == square(k1)) { countVal = 1; pointerCount = 0; } } else { root = entry.getValue(); } } return root; } /** * * @param a * @return */ public static int square(int a) { int n = 1; for (int i = 0; i < a; i++) { n = n * 2; } return n; } public static void main(String... args) { Integer[] array = { 45, 23, 11, 89, 77, 98, 4, 28, 65, 43 }; BTreePrinter.printNode(BTreePrinter.drawTree(array)); Character[] arrayChar = { 'K', 'A', 'R', 'T', 'I', 'K', 'M', 'A', 'N', 'D', 'A', 'L' }; BTreePrinter.printNode(BTreePrinter.drawTree(arrayChar)); } } |
Example 1 Draw Binary Tree : |
45
/ \
/ \
/ \
/ \
23 11
/ \ / \
/ \ / \
89 77 98 4
/ \ /
28 65 43
K
/ \
/ \
/ \
/ \
A R
/ \ / \
/ \ / \
T I K M
/ \ / \ /
A N D A L
|
Example 2 Custom Tree Printer: |
package com.kartik.tree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* The {@code BTreePrinter} class provides a basic capability for creating
* drawings with your programs. It uses a simple graphics model that allows you
* to create drawings consisting of points, lines, squares, circles, and other
* geometric shapes in a window on your computer and to save the drawings to a
* file. Standard drawing also includes facilities for text, color, pictures,
* and animation, along with user interaction via the keyboard and mouse.
* <p>
* <b>Getting started.</b> To use standard drawing, you must have
* <tt>BTreePrinter.class</tt> in your Java classpath. If you used our autoinstaller,
* you should be all set. Otherwise, download <a href =
* "http://www.kartikchandramandal.blogspot.in/2015/10/how-to-draw-atree-from-array-of-integer.html"
* >BTreePrinter.java</a> and put a copy in your working directory.
* <p>
* Now, type the following short program into your editor:
*
* <pre>
* public class TestBTreePrinter {
* public static void main(String[] args) {
* Integer[] array = { 45, 23, 11, 89, 77, 98, 4, 28, 65, 43 };
* BTreePrinter.printNode(BTreePrinter.drawTree(array));
*
* Character[] arrayChar = { 'K', 'A', 'R', 'T', 'I', 'K', 'M', 'A', 'N',
* 'D', 'A', 'L' };
* BTreePrinter.printNode(BTreePrinter.drawTree(arrayChar));
* }
* }
* </pre>
*
* If you compile and execute the program, you should see a window appear with a
* thick magenta line and a blue point. This program illustrates the two main
* types of methods in standard drawing—methods that draw geometric shapes
* and methods that control drawing parameters. The methods
* {@code BTreePrinter.line()} and {@code BTreePrinter.point()} draw lines and
* points; the methods {@code BTreePrinter.setPenRadius()} and
* {@code BTreePrinter.setPenColor()} control the line thickness and color.
* <p>
* <b>Points and lines.</b> You can draw points and line segments with the
* following methods:
* <ul>
* <li> {@link #point(double x, double y)}
* <li> {@link #line(double x1, double y1, double x2, double y2)}
* </ul>
* <p>
* The <em>x</em>- and <em>y</em>-coordinates must be in the drawing area
* (between 0 and 1 and by default) or the points and lines will not be visible.
* <p>
* <b>Squares, circles, rectangles, and ellipses.</b> You can draw squares,
* circles, rectangles, and ellipses using the following methods:
* <ul>
* <li> {@link #circle(double x, double y, double radius)}
* <li>
* {@link #ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #square(double x, double y, double radius)}
* <li> {@link #rectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* All of these methods take as arguments the location and size of the shape.
* The location is always specified by the <em>x</em>- and <em>y</em>
* -coordinates of its <em>center</em>. The size of a circle is specified by its
* radius and the size of an ellipse is specified by the lengths of its
* semi-major and semi-minor axes. The size of a square or rectangle is
* specified by its half-width or half-height. The convention for drawing
* squares and rectangles is parallel to those for drawing circles and ellipses,
* but may be unexpected to the uninitiated.
* <p>
* The methods above trace outlines of the given shapes. The following methods
* draw filled versions:
* <ul>
* <li> {@link #filledCircle(double x, double y, double radius)}
* <li>
* {@link #filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis)}
* <li> {@link #filledSquare(double x, double y, double radius)}
* <li>
* {@link #filledRectangle(double x, double y, double halfWidth, double halfHeight)}
* </ul>
* <p>
* <b>Circular arcs.</b> You can draw circular arcs with the following method:
* <ul>
* <li> {@link #arc(double x, double y, double radius, double angle1, double angle2)}
* </ul>
* <p>
* The arc is from the circle centered at (<em>x</em>, <em>y</em>) of the
* specified radius. The arc extends from angle1 to angle2. By convention, the
* angles are <em>polar</em> (counterclockwise angle from the <em>x</em>-axis)
* and represented in degrees. For example,
* {@code BTreePrinter.arc(0.0, 0.0, 1.0, 0, 90)} draws the arc of the unit circle
* from 3 o'clock (0 degrees) to 12 o'clock (90 degrees).
* <p>
* <b>Polygons.</b> You can draw polygons with the following methods:
* <ul>
* <li> {@link #polygon(double[] x, double[] y)}
* <li> {@link #filledPolygon(double[] x, double[] y)}
* </ul>
* <p>
* The points in the polygon are ({@code x[i]}, {@code y[i]}). For example, the
* following code fragment draws a filled diamond with vertices (0.1, 0.2),
* (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1):
*
* <pre>
* double[] x = { 0.1, 0.2, 0.3, 0.2 };
* double[] y = { 0.2, 0.3, 0.2, 0.1 };
* BTreePrinter.filledPolygon(x, y);
* </pre>
* <p>
* <b>Pen size.</b> The pen is circular, so that when you set the pen radius to
* <em>r</em> and draw a point, you get a circle of radius <em>r</em>. Also,
* lines are of thickness 2<em>r</em> and have rounded ends. The default pen
* radius is 0.005 and is not affected by coordinate scaling. This default pen
* radius is about 1/200 the width of the default canvas, so that if you draw
* 100 points equally spaced along a horizontal or vertical line, you will be
* able to see individual circles, but if you draw 200 such points, the result
* will look like a line.
* <ul>
* <li> {@link #setPenRadius(double radius)}
* </ul>
* <p>
* For example, {@code BTreePrinter.setPenRadius(0.025)} makes the thickness of the
* lines and the size of the points to be five times the 0.005 default. To draw
* points with the minimum possible radius (one pixel on typical displays), set
* the pen radius to 0.0.
* <p>
* <b>Pen color.</b> All geometric shapes (such as points, lines, and circles)
* are drawn using the current pen color. By default, it is black. You can
* change the pen color with the following methods:
* <ul>
* <li> {@link #setPenColor(int red, int green, int blue)}
* <li> {@link #setPenColor(Color color)}
* </ul>
* <p>
* The first method allows you to specify colors using the RGB color system.
* This <a href = "http://johndyer.name/lab/colorpicker/">color picker</a> is a
* convenient way to find a desired color. The second method allows you to
* specify colors using the {@link Color} data type that is discussed in Chapter
* 3. Until then, you can use this method with one of these predefined colors in
* standard drawing: {@link #BLACK}, {@link #BLUE}, {@link #CYAN},
* {@link #DARK_GRAY}, {@link #GRAY}, {@link #GREEN}, {@link #LIGHT_GRAY},
* {@link #MAGENTA}, {@link #ORANGE}, {@link #PINK}, {@link #RED},
* {@link #WHITE}, and {@link #YELLOW}. For example,
* {@code BTreePrinter.setPenColor(BTreePrinter.MAGENTA)} sets the pen color to magenta.
* <p>
* <b>Canvas size.</b> By default, all drawing takes places in a 512-by-512
* canvas. The canvas does not include the window title or window border. You
* can change the size of the canvas with the following method:
* <ul>
* <li> {@link #setCanvasSize(int width, int height)}
* </ul>
* <p>
* This sets the canvas size to be <em>width</em>-by-<em>height</em> pixels. It
* also erases the current drawing and resets the coordinate system, pen radius,
* pen color, and font back to their default values. Ordinarly, this method is
* called once, at the very beginning of a program. For example,
* {@code BTreePrinter.setCanvasSize(800, 800)} sets the canvas size to be 800-by-800
* pixels.
* <p>
* <b>Canvas scale and coordinate system.</b> By default, all drawing takes
* places in the unit square, with (0, 0) at lower left and (1, 1) at upper
* right. You can change the default coordinate system with the following
* methods:
* <ul>
* <li> {@link #setXscale(double xmin, double xmax)}
* <li> {@link #setYscale(double ymin, double ymax)}
* <li> {@link #setScale(double min, double max)}
* </ul>
* <p>
* The arguments are the coordinates of the minimum and maximum <em>x</em>- or
* <em>y</em>-coordinates that will appear in the canvas. For example, if you
* wish to use the default coordinate system but leave a small margin, you can
* call {@code BTreePrinter.setScale(-.05, 1.05)}.
* <p>
* These methods change the coordinate system for subsequent drawing commands;
* they do not affect previous drawings. These methods do not change the canvas
* size; so, if the <em>x</em>- and <em>y</em>-scales are different, squares
* will become rectangles and circles will become ellipsoidal.
* <p>
* <b>Text.</b> You can use the following methods to annotate your drawings with
* text:
* <ul>
* <li> {@link #text(double x, double y, String text)}
* <li> {@link #text(double x, double y, String text, double degrees)}
* <li> {@link #textLeft(double x, double y, String text)}
* <li> {@link #textRight(double x, double y, String text)}
* </ul>
* <p>
* The first two methods write the specified text in the current font, centered
* at (<em>x</em>, <em>y</em>). The second method allows you to rotate the text.
* The last two methods either left- or right-align the text at (<em>x</em>,
* <em>y</em>).
* <p>
* The default font is a Sans Serif font with point size 16. You can use the
* following method to change the font:
* <ul>
* <li> {@link #setFont(Font font)}
* </ul>
* <p>
* You use the {@link Font} data type to specify the font. This allows you to
* choose the face, size, and style of the font. For example, the following code
* fragment sets the font to Arial Bold, 60 point.
*
* <pre>
* Font font = new Font("Arial", Font.BOLD, 60);
* BTreePrinter.setFont(font);
* BTreePrinter.text(0.5, 0.5, "Hello, World");
* </pre>
* <p>
* <b>Images.</b> You can use the following methods to add images to your
* drawings:
* <ul>
* <li> {@link #picture(double x, double y, String filename)}
* <li> {@link #picture(double x, double y, String filename, double degrees)}
* <li> {@link #picture(double x, double y, String filename, double width)}
* <li>
* {@link #picture(double x, double y, String filename, double width, double degrees)}
* </ul>
* <p>
* These methods draw the specified image, centered at (<em>x</em>, <em>y</em>).
* The supported image formats are JPEG, PNG, and GIF. The image will display at
* its native size, independent of the coordinate system. Optionally, you can
* rotate the image a specified number of degrees counterclockwise or rescale it
* to fit inside a width-by-height pixel bounding box.
* <p>
* <b>Saving to a file.</b> You save your image to a file using the
* <em>File -> Save</em> menu option. You can also save a file programatically
* using the following method:
* <ul>
* <li> {@link #save(String filename)}
* </ul>
* <p>
* The supported image formats are JPEG and PNG. The filename must have either
* the extension .jpg or .png. We recommend using PNG for drawing that consist
* solely of geometric shapes and JPEG for drawings that contains pictures.
* <p>
* <b>Clearing the canvas.</b> To clear the entire drawing canvas, you can use
* the following methods:
* <ul>
* <li> {@link #clear()}
* <li> {@link #clear(Color color)}
* </ul>
* <p>
* The first method clears the canvas to white; the second method allows you to
* specify a color of your choice. For example,
* {@code BTreePrinter.clear(BTreePrinter.LIGHT_GRAY)} clears the canvas to a shade of
* gray. Most often, these two methods are used in conjunction with animation
* mode.
* <p>
* <b>Animations.</b> Animation mode is one of the trickier features of standard
* drawing. The following two methods control the way in which objects are
* drawn:
* <ul>
* <li> {@link #show()}
* <li> {@link #show(int t)}
* </ul>
* <p>
* By default, animation mode is off, which means that as soon as you call a
* drawing method—such as {@code point()} or {@code line()}—the
* results appear on the screen. {@code BTreePrinter.show()} turns off animation
* mode.
* <p>
* You can call {@link #show(int t)} to turn on animation mode. This defers all
* drawing to the screen until you are aready to display them. Once you are
* ready to display them, you call {@link #show(int t)} again, which transfer
* the offscreen drawing to the screen and waits for the specified number of
* milliseconds. In conjuction with {@link #clear()}, you can create the
* illusion of movement by iterating the following three steps:
* <ul>
* <li>Clear the background canvas.
* <li>Draw geometric objects.
* <li>Show the drawing and wait for a short while.
* </ul>
* <p>
* Waiting for a short while is essential; otherwise, the drawing will appear
* and disappear so quickly that your animation will flicker.
* <p>
* Here is a simple example of an animation:
* <p>
* <b>Keyboard and mouse inputs.</b> Standard drawing has very basic support for
* keyboard and mouse input. It is much less powerful than most user interface
* libraries provide, but also much simpler. You can use the following methods
* to intercept mouse events:
* <ul>
* <li> {@link #mousePressed()}
* <li> {@link #mouseX()}
* <li> {@link #mouseY()}
* </ul>
* <p>
* The first method tells you whether a mouse button is currently being pressed.
* The last two methods tells you the <em>x</em>- and <em>y</em>-coordinates of
* the mouse's current position, using the same coordinate system as the canvas
* (the unit square, by default). You should use these methods in an animation
* loop that waits a short while before trying to poll the mouse for its current
* state. You can use the following methods to intercept keyboard events:
* <ul>
* <li> {@link #hasNextKeyTyped()}
* <li> {@link #nextKeyTyped()}
* <li> {@link #isKeyPressed(int keycode)}
* </ul>
* <p>
* If the user types lots of keys, they will be saved in a list until you
* process them. The first method tells you whether the user has typed a key
* (that your program has not yet processed). The second method returns the next
* key that the user typed (that your program has not yet processed) and removes
* it from the list of saved keystrokes. The third method tells you whether a
* key is currently being pressed.
* <p>
* <b>Accessing control parameters.</b> You can use the following methods to
* access the current pen color, pen radius, and font:
* <ul>
* <li> {@link #getPenColor()}
* <li> {@link #getPenRadius()}
* <li> {@link #getFont()}
* </ul>
* <p>
* These methods are useful when you want to temporarily change a control
* parameter and reset it back to its original value.
* <p>
* <b>Corner cases.</b> To avoid clutter, the API doesn't explicitly refer to
* arguments that are null, infinity, or NaN.
* <ul>
* <li>Any method that is passed a {@code null} argument will throw a
* {@link NullPointerException}.
* <li>Except as noted in the APIs, drawing an object outside (or partly
* outside) the canvas is permitted—however, only the part of the object
* that appears inside the canvas will be visible.
* <li>Except as noted in the APIs, all methods accept {@link Double#NaN},
* {@link Double#POSITIVE_INFINITY}, and {@link Double#NEGATIVE_INFINITY} as
* arugments. An object drawn with an <em>x</em>- or <em>y</em>-coordinate that
* is NaN will behave as if it is outside the canvas, and will not be visible.
* </ul>
* <p>
* <b>Performance tricks.</b> Standard drawing is capable of drawing large
* amounts of data. Here are a few tricks and tips:
* <ul>
* <li>Use <em>animation mode</em> for static drawing with a large number of
* objects. That is, call {@code BTreePrinter.show(0)} before and after the sequence
* of drawing commands. The bottleneck operation is not drawing the geometric
* shapes but rather drawing them to the screen. By using animation mode, you
* draw all of the shapes to an offscreen buffer, then copy them all at once to
* the screen.
* <li>When using <em>animation mode</em>, call {@code show()} only once per
* frame, not after drawing each object.
* <li>If you call {@code picture()} multiple times with the same filename, Java
* will cache the image, so you do not incur the cost of reading from a file
* each time.
* <li>Do not call {@code setFont()} in an animation loop (unless you really
* need to change the font in each iteration). It can cause flicker.
* </ul>
* <p>
* <b>Known bugs and issues.</b>
* <ul>
* <li>The {@code picture()} methods may not draw the portion of the image that
* is inside the canvas if the center point (<em>x</em>, <em>y</em>) is outside
* the canvas. This bug appears only on some systems.
* <li>Some methods may not draw the portion of the geometric object that is
* inside the canvas if the <em>x</em>- or <em>y</em>-coordinates are infinite.
* This bug appears only on some systems.
* </ul>
* <p>
* <b>Reference.</b> For additional documentation, see <a
* href="http://www.kartikchandramandal.blogspot.in/2015/10/how-to-draw-atree-from-array-of-integer.html">Section 1.5</a> of
* <em>Introduction to Programming in Java: An Interdisciplinary Approach</em>
* by Kartik Chandra Mandal.
*
* @author Kartik Chandra Mandal
*/
public class BTreePrinter<T> {
/**
*
* @param <T>
* @param root
*/
public static <T> void printNode(Node<T> root) {
int maxLevel = BTreePrinter.maxLevel(root);
printNodeInternal(Collections.singletonList(root), 1, maxLevel);
}
/**
*
* @param <T>
* @param nodes
* @param level
* @param maxLevel
*/
private static <T> void printNodeInternal(List<Node<T>> nodes, int level,
int maxLevel) {
if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes))
return;
int floor = maxLevel - level;
int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
int firstSpaces = (int) Math.pow(2, (floor)) - 1;
int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;
BTreePrinter.printWhitespaces(firstSpaces);
List<Node<T>> newNodes = new ArrayList<Node<T>>();
for (Node<T> node : nodes) {
if (node != null) {
System.out.print(node.data);
newNodes.add(node.left);
newNodes.add(node.right);
} else {
newNodes.add(null);
newNodes.add(null);
System.out.print(" ");
}
BTreePrinter.printWhitespaces(betweenSpaces);
}
System.out.println("");
for (int i = 1; i <= endgeLines; i++) {
for (int j = 0; j < nodes.size(); j++) {
BTreePrinter.printWhitespaces(firstSpaces - i);
if (nodes.get(j) == null) {
BTreePrinter.printWhitespaces(endgeLines + endgeLines + i
+ 1);
continue;
}
if (nodes.get(j).left != null)
System.out.print("/");
else
BTreePrinter.printWhitespaces(1);
BTreePrinter.printWhitespaces(i + i - 1);
if (nodes.get(j).right != null)
System.out.print("\\");
else
BTreePrinter.printWhitespaces(1);
BTreePrinter.printWhitespaces(endgeLines + endgeLines - i);
}
System.out.println("");
}
printNodeInternal(newNodes, level + 1, maxLevel);
}
/**
*
* @param count
*/
private static void printWhitespaces(int count) {
for (int i = 0; i < count; i++)
System.out.print(" ");
}
/**
*
* @param node
* @return
*/
private static <T> int maxLevel(Node<T> node) {
if (node == null)
return 0;
return Math.max(BTreePrinter.maxLevel(node.left),
BTreePrinter.maxLevel(node.right)) + 1;
}
/**
*
* @param list
* @return
*/
private static <T> boolean isAllElementsNull(List<T> list) {
for (Object object : list) {
if (object != null)
return false;
}
return true;
}
public static <T> Node<T> drawTree(T[] input) {
Node<T> node = null;
Map<String, Node<T>> mapNode = new HashMap<String, Node<T>>();
int i = 0, c = 0;
int count = 0;
for (i = 0; i < input.length; i++) {
if (i > 0) {
for (c = 1; c <= square(i); c++) {
String stringKey = "n" + i + c;
if (count < input.length) {
node = new Node<T>(input[count]);
mapNode.put(stringKey, node);
}
count++;
}
} else {
String stringKey = "n" + i + c;
node = new Node<T>(input[count]);
mapNode.put(stringKey, node);
count++;
}
}
Map<String, Node<T>> sortMapNode = new TreeMap<String, Node<T>>(mapNode);
Node<T> root = null;
Node<T> prent = null;
int countVal = 1;
int pointerCount = 0;
for (Map.Entry<String, Node<T>> entry : sortMapNode.entrySet()) {
String key = entry.getKey();
char p = key.charAt(1);
char q = key.charAt(2);
int k = Integer.parseInt(String.valueOf(p));
int k1 = Integer.parseInt(String.valueOf(p));
int k2 = Integer.parseInt(String.valueOf(q));
if (k > 0) {
String newKey = null;
if (k1 % 2 == 0) {
if ((k1 + k2) % 2 == 0) {
newKey = "n" + (k1 - 1) + (k2 / 2);
prent = (Node<T>) mapNode.get(newKey);
prent.right = entry.getValue();
countVal++;
pointerCount++;
} else {
if (k2 % 2 == 0) {
newKey = "n" + (k1 - 1) + (k2 / 2);
prent = (Node<T>) mapNode.get(newKey);
prent.right = entry.getValue();
countVal++;
pointerCount++;
} else {
newKey = "n" + (k1 - 1) + countVal;
prent = (Node<T>) mapNode.get(newKey);
prent.left = entry.getValue();
pointerCount++;
}
}
} else {
if ((k1 + k2) % 2 == 0) {
if (k1 == 1) {
newKey = "n00";
prent = (Node<T>) mapNode.get(newKey);
if (k2 == 1) {
prent.left = entry.getValue();
}
pointerCount++;
} else {
newKey = "n" + (k1 - 1) + countVal;
prent = (Node<T>) mapNode.get(newKey);
prent.left = entry.getValue();
pointerCount++;
}
} else {
if (k1 == 1) {
newKey = "n00";
prent = (Node<T>) mapNode.get(newKey);
if (k2 == 2) {
prent.right = entry.getValue();
countVal++;
}
pointerCount++;
} else {
if (k2 % 2 == 0) {
newKey = "n" + (k1 - 1) + (k2 / 2);
prent = (Node<T>) mapNode.get(newKey);
prent.right = entry.getValue();
countVal++;
pointerCount++;
} else {
newKey = "n" + (k1 - 1) + countVal;
prent = (Node<T>) mapNode.get(newKey);
prent.left = entry.getValue();
pointerCount++;
}
}
}
}
if (pointerCount == square(k1)) {
countVal = 1;
pointerCount = 0;
}
} else {
root = entry.getValue();
}
}
return root;
}
/**
*
* @param a
* @return
*/
public static int square(int a) {
int n = 1;
for (int i = 0; i < a; i++) {
n = n * 2;
}
return n;
}
public static void main(String... args) {
Integer[] array = { 45, 23, 11, 89, 77, 98, 4, 28, 65, 43 };
BTreePrinter.printNode(BTreePrinter.drawTree(array));
Character[] arrayChar = { 'K', 'A', 'R', 'T', 'I', 'K', 'M', 'A', 'N',
'D', 'A', 'L' };
BTreePrinter.printNode(BTreePrinter.drawTree(arrayChar));
}
}
Out put:
45
/ \
/ \
/ \
/ \
23 11
/ \ / \
/ \ / \
89 77 98 4
/ \ /
28 65 43
K
/ \
/ \
/ \
/ \
A R
/ \ / \
/ \ / \
T I K M
/ \ / \ /
A N D A L