import java.util.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; public class BSTVisualization extends JFrame implements ActionListener, KeyListener { // Tree Root Node. private Node root; // private Color color; private JPanel topPanel, treePanel, infoPanel; private JPanel topLeftPanel, topRightPanel; private JButton btnAdd, btnDelete; private JTextField tf; private int X = 300, Y = 75; private Graphics2D g2; private Rectangle size; private JLabel labelInorder, labelPreorder, labelPostorder, labelHeight; private JLabel ansInorder, ansPreorder, ansPostorder, ansHeight; private FontMetrics fontMatrix; //Node Structure private static class Node { static int TEXT_WIDTH = 40; static int TEXT_HEIGHT = 40; JLabel data; Node left; Node right; Points p; Node(int info) { data = new JLabel(info + "", SwingConstants.CENTER); data.setFont(new Font("Arial", Font.BOLD, 20)); data.setBorder(BorderFactory.createLineBorder(Color.black)); data.setOpaque(true); data.setBackground(Color.green); p = null; } } //Points structure private static class Points { int x1 = 0, x2 = 0, y2 = 0, y1 = 0; Points(int x1, int y1, int x2, int y2) { this.x1 = x1; this.x2 = x2; this.y2 = y2; this.y1 = y1; } public String toString() { return "x1 = " + x1 + ", y1 = " + y1 + ", x2 = " + x2 + ", y2 = " + y2; } } // For storing the Height of the root,left and right child height. private static class Height { int root, left, right; Height() { this.root = 0; this.left = 0; this.right = 0; } Height(int left, int right) { this.left = left; this.right = right; } @Override public String toString() { return Integer.toString(this.root); } } public void paint(Graphics g) { super.paintComponents(g); g2 = (Graphics2D) g; g2.setStroke(new BasicStroke(3.0f)); Stack s = new Stack<>(); Node curr = root; Points pts; int offset = topPanel.getBounds().height; while (!s.isEmpty() || curr != null) { while (curr != null) { s.push(curr); curr = curr.left; } if (!s.isEmpty()) curr = s.pop(); pts = curr.p; g2.drawLine(pts.x1 + 7, pts.y1 + 30 + offset, pts.x2 + 3, pts.y2 + 10 + offset); curr = curr.right; } // x1 = label.getX()+7 // y1 = label.getY()+30 } public BSTVisualization() { // Initialize the frame. initialize(); } private void initialize() { // setLayout(null); // layout setSize(1200, 700); //frame size size = getBounds(); X = size.width / 2; topPanel = new JPanel(new BorderLayout()); Rectangle top = topPanel.getBounds(); topLeftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10)); topPanel.add(topLeftPanel, BorderLayout.WEST); topRightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); topPanel.add(topRightPanel, BorderLayout.EAST); treePanel = new JPanel(null); treePanel.setPreferredSize(new Dimension(size.width, size.height - 300)); infoPanel = new JPanel(); infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS)); infoPanel.setPreferredSize(new Dimension(size.width, 200)); // Height of BST label labelHeight = new JLabel("BST Height : "); labelHeight.setFont(new Font("Calibri", Font.BOLD, 20)); topLeftPanel.add(labelHeight); // Height of BST answer ansHeight = new JLabel("0"); ansHeight.setFont(new Font("Calibri", Font.BOLD, 20)); ansHeight.setPreferredSize(new Dimension(50, 30)); topLeftPanel.add(ansHeight); //For geting data. tf = new JTextField(""); tf.setFont(new Font("Arial", Font.BOLD, 20)); tf.setPreferredSize(new Dimension(150, 30)); tf.addKeyListener(this); topRightPanel.add(tf); //Add Button btnAdd = new JButton("Add"); btnAdd.setFont(new Font("Arial", Font.BOLD, 20)); // btnAdd.setBounds(size.width - 130, 20, 100, 30); btnAdd.addActionListener(this); topRightPanel.add(btnAdd); //Delete Button btnDelete = new JButton("Delete"); btnDelete.setFont(new Font("Arial", Font.BOLD, 20)); // btnDelete.setBounds(size.width - 130, 60, 100, 30); btnDelete.addActionListener(this); topRightPanel.add(btnDelete); // Inorder label labelInorder = new JLabel("Inorder :"); labelInorder.setFont(new Font("Times New Roman", Font.BOLD, 20)); infoPanel.add(labelInorder); infoPanel.add(Box.createRigidArea(new Dimension(7, 5))); // Inorder traversal answer ansInorder = new JLabel("BST is empty."); ansInorder.setFont(new Font("Arial", Font.PLAIN, 18)); infoPanel.add(ansInorder); infoPanel.add(Box.createRigidArea(new Dimension(7, 15))); // Preorder label labelPreorder = new JLabel("Preorder :"); labelPreorder.setFont(new Font("Times New Roman", Font.BOLD, 20)); infoPanel.add(labelPreorder); infoPanel.add(Box.createRigidArea(new Dimension(7, 5))); // Preorder traversal answer ansPreorder = new JLabel("BST is empty."); ansPreorder.setFont(new Font("Arial", Font.PLAIN, 18)); infoPanel.add(ansPreorder); infoPanel.add(Box.createRigidArea(new Dimension(7, 15))); // Postorder label labelPostorder = new JLabel("Postorder :"); labelPostorder.setFont(new Font("Times New Roman", Font.BOLD, 20)); infoPanel.add(labelPostorder); // Postorder traversal answer ansPostorder = new JLabel("BST is empty."); ansPostorder.setFont(new Font("Arial", Font.PLAIN, 18)); infoPanel.add(ansPostorder); tf.requestFocusInWindow(); add(topPanel, BorderLayout.NORTH); add(treePanel, BorderLayout.CENTER); add(infoPanel, BorderLayout.SOUTH); setTitle("Binary Search Tree Visualization"); //Title Frame setResizable(false); setLocationRelativeTo(null); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @Override public void actionPerformed(ActionEvent evt) { if (tf.isEnabled()) { try { int data = Integer.parseInt(tf.getText()); if (evt.getSource() == btnAdd) { add(data); } else { delete(data); } tf.setText(""); tf.requestFocusInWindow(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Please Enter Integer."); } } } @Override public void keyTyped(KeyEvent evt) { char c = evt.getKeyChar(); if (!tf.isEnabled()) { return; } else if (c == 'a' || c == 'A' || c == '\n') { try { String data = tf.getText(); evt.consume(); // Not type 'a' or 'A' character in textfield if (!data.isEmpty()) { add(Integer.parseInt(data)); } else { throw new Exception(); } tf.requestFocusInWindow(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Please Enter Integer."); } tf.setText(""); } else if (c == 'd' || c == 'D') { try { String data = tf.getText(); evt.consume(); // Not type 'd' or 'D' character in textfield if (!data.isEmpty()) { delete(Integer.parseInt(data)); } tf.requestFocusInWindow(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Please Enter Integer."); } tf.setText(""); } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') evt.consume(); } @Override public void keyPressed(KeyEvent evt) { } @Override public void keyReleased(KeyEvent evt) { } //Add element in BST. public void add(int info) { Node newNode = new Node(info); int width = getWidth(newNode); if (root == null) { root = newNode; newNode.data.setBounds(treePanel.getBounds().width / 2, 10, width, 40); newNode.p = new Points(0, 0, 0, 0); } else { Node curr = root, pre = root; int currData; X = treePanel.getBounds().width / 2; while (curr != null) { pre = curr; currData = Integer.parseInt(curr.data.getText()); if (info == currData) { JOptionPane.showMessageDialog(null, info + " is already exist."); return; } else if (currData > info) { curr = curr.left; } else { curr = curr.right; } X /= 2; } currData = Integer.parseInt(pre.data.getText()); int x = pre.data.getX(); int y = pre.data.getY(); Dimension preDimension = pre.data.getSize(); Dimension currDimension = new Dimension(width, Node.TEXT_HEIGHT); if (currData > info) { pre.left = newNode; newNode.data.setBounds(x - X, y + Y, width, 40); // x1=x;y1=y+20;x2=x-X+20;y2=y+Y+20; newNode.p = new Points(x, y + preDimension.height / 2, x - X + currDimension.width / 2, y + Y + currDimension.height / 2); } else { pre.right = newNode; newNode.data.setBounds(x + X, y + Y, width, 40); // x1=x+40;y1=y+20;x2=x+X+20;y2=y+Y+20; newNode.p = new Points(x + preDimension.width, y + preDimension.height / 2, x + X + currDimension.width / 2, y + Y + currDimension.height / 2); } } // Set all traversal and height of BST setInfo(); // paint(treePanel.getGraphics()); treePanel.add(newNode.data); treePanel.validate(); treePanel.repaint(); validate(); repaint(); } // Delete Node from BST public void delete(int data) { if (root == null) { JOptionPane.showMessageDialog(null, "BST is empty."); } else { Node curr = root, pre = root; while (curr != null) { int info = Integer.parseInt(curr.data.getText()); if (info == data) { break; } else if (info > data) { pre = curr; curr = curr.left; } else { pre = curr; curr = curr.right; } } if (curr == null) { // data is not find. JOptionPane.showMessageDialog(null, data + " is not available."); return; } else if (curr.left == null || curr.right == null) { // data has 0 or 1 child treePanel.remove(curr.data); treePanel.validate(); treePanel.repaint(); validate(); repaint(); if (curr != root) { Node address = curr.left != null ? curr.left : curr.right; // curr.data>pre.data int preData = Integer.parseInt(pre.data.getText()); int currData = Integer.parseInt(curr.data.getText()); if (currData > preData) { pre.right = address; } else { pre.left = address; } } else { if (curr.left != null) { root = curr.left; } else { root = curr.right; } } } else { // data has 2 child. treePanel.remove(curr.data); treePanel.validate(); treePanel.repaint(); validate(); repaint(); /* It set another node depending upon the height of left and right sub tree. */ Node nextRoot = null, preRoot = curr; Height height = calculateHeight(curr); /* For taking maximum element from the left Side. */ if (height.left > height.right) { nextRoot = curr.left; while (nextRoot.right != null) { preRoot = nextRoot; nextRoot = nextRoot.right; } if (preRoot != curr) { preRoot.right = nextRoot.left; } else { preRoot.left = nextRoot.left; } } else { /* For taking minimum element from the right Side.*/ nextRoot = curr.right; while (nextRoot.left != null) { preRoot = nextRoot; nextRoot = nextRoot.left; } if (preRoot != curr) { preRoot.left = nextRoot.right; } else { preRoot.right = nextRoot.right; } } curr.data = nextRoot.data; } reArrangeNode(root, root, treePanel.getBounds().width / 2); } // Set all traversal and height of BST setInfo(); } // Set all traversal and height of BST private void setInfo() { Height height = calculateHeight(root); if (height.root == 0) { ansInorder.setText("BST is empty."); ansPostorder.setText("BST is empty."); ansPreorder.setText("BST is empty."); } else { ansInorder.setText(inorder(root)); ansPostorder.setText(postorder(root)); ansPreorder.setText(preorder(root)); } ansHeight.setText(height.root + ""); } private int getWidth(Node node) { fontMatrix = getFontMetrics(node.data.getFont()); int width = fontMatrix.stringWidth(node.data.getText()); return width < Node.TEXT_WIDTH ? Node.TEXT_WIDTH : (width + 5); } //Inorder logic private String inorder(Node root) { if (root == null) return ""; return inorder(root.left) + root.data.getText() + " " + inorder(root.right); } //Preorder logic public String preorder(Node root) { if (root == null) return ""; return root.data.getText() + " " + preorder(root.left) + preorder(root.right); } //Postorder logic public String postorder(Node root) { if (root == null) return ""; return postorder(root.left) + postorder(root.right) + root.data.getText() + " "; } // Calculate Height of BST using recursive method. private Height calculateHeight(Node root) { if (root == null) { return new Height(); } Height leftChild = calculateHeight(root.left); Height rightChild = calculateHeight(root.right); Height current = new Height(leftChild.root, rightChild.root); current.root = 1 + Math.max(leftChild.root, rightChild.root); return current; } // Rearrange nodes private void reArrangeNode(Node node, Node pre, int X) { if (node == null) return; int width = getWidth(node); if (root == node) { node.data.setBounds(X, 10, width, Node.TEXT_HEIGHT); } else { int x = pre.data.getX(); int y = pre.data.getY(); Dimension preDimension = pre.data.getSize(); Dimension currDimension = new Dimension(width, Node.TEXT_HEIGHT); int preData = Integer.parseInt(pre.data.getText()); int nodeData = Integer.parseInt(node.data.getText()); if (nodeData < preData) { node.data.setBounds(x - X, y + Y, width, Node.TEXT_HEIGHT); node.p = new Points(x, y + preDimension.height / 2, x - X + currDimension.width / 2, y + Y + currDimension.height / 2); } else { node.data.setBounds(x + X, y + Y, width, Node.TEXT_HEIGHT); // node.p = new Points(x + 40, y + 20, x + X + 20, y + Y + 20); node.p = new Points(x + preDimension.width, y + preDimension.height / 2, x + X + currDimension.width / 2, y + Y + currDimension.height / 2); } } reArrangeNode(node.left, node, X / 2); reArrangeNode(node.right, node, X / 2); } public static void main(String arg[]) { BSTVisualization bst = new BSTVisualization(); bst.add(500); bst.add(250); bst.add(350); bst.add(200); bst.add(750); bst.add(1000); bst.add(700); bst.add(740); } }