Algorithmen und Datenstrukturen Übung 1b: Praktische Nutzung von Bäumen 1. Sets mit binären Suchbäumen Interface Set interface Set { void add(Comparable o); boolean contains(Comparable o); Set unite(Set s); } Für die Implementierung dieses Interface ist Voraussetzung: - effizienten Test auf Enthaltensein - Implementierung: binärer Suchbaum (Klassen TreeNode, BinaryTree) Klasse TreeNode public class TreeNode { protected Object element; protected TreeNode left, right; int x = 0; int y = 0; public TreeNode(Object o) { left = right = null; element = o; } public TreeNode getLeft() { return left; } public TreeNode getRight() { return right; } public Object getElement() { return element; } public void setLeft(TreeNode n) { left = n; } public void setRight(TreeNode n) { right = n; } public String toString() { return element.toString(); } public void accept(NodeVisitor v, int depth) { if (left != null) left.accept(v,depth+1); v.visitNode(this,depth); if (right != null) right.accept(v,depth+1); } public void setX(int wert) { x = wert; } public void setY(int wert) { y = wert; } public int getX() { return x; } public int getY() { return y; } } Klasse BinaryTree public class BinaryTree { protected TreeNode root; 1 Algorithmen und Datenstrukturen protected int size; public BinaryTree() { root = null; size = 0; } public int size() { return this.size; } public boolean isEmpty() { return this.size == 0; } public TreeNode root() { return this.root; } public boolean contains(Comparable o) { TreeNode node = findNode(root,o); return (o.compareTo(node.getElement()) == 0); } protected TreeNode findNode(TreeNode n, Comparable o) { int cmp = o.compareTo(n.getElement()); if (cmp < 0 && n.getLeft() != null) return findNode(n.getLeft(),o); else if (cmp > 0 && n.getRight() != null) return findNode(n.getRight(),o); else return n; } public void insert(Comparable o) { if (isEmpty()) root = new TreeNode(o); else { TreeNode node = findNode(root,o); int cmp = o.compareTo(node.getElement()); if (cmp == 0) return; else if (cmp < 0) node.setLeft(new TreeNode(o)); else node.setRight(new TreeNode(o)); } size++; } } Klasse TreeSet public class TreeSet implements Set { protected BinaryTree tree; public TreeSet() { tree = new BinaryTree(); } public int size() { return tree.size(); } public void add(Comparable o) { tree.insert(o); } public boolean contains(Comparable o) { return tree.contains(o); } public TreeSetIterator iterator() { return new TreeSetIterator(tree); } public TreeSet unite(TreeSet set) { // Vereinigung TreeSet newSet = new TreeSet(); java.util.Iterator i = this.iterator(); while (i.hasNext()) newSet.add((Comparable) i.next()); i = set.iterator(); while (i.hasNext()) newSet.add((Comparable) i.next()); return newSet; 2 Algorithmen und Datenstrukturen } } Implementierung Baum-Iterator import java.util.*; public class TreeSetIterator implements java.util.Iterator { private BinaryTree tree; private Stack stack; public TreeSetIterator(BinaryTree tree) { this.tree = tree; this.stack = new Stack(); TreeNode node = tree.root(); while (node != null) { stack.push(node); node = node.getLeft(); } } public boolean hasNext() { return !stack.empty(); } public Object next() { Object o; TreeNode node = (TreeNode) stack.peek(); o = node.getElement(); stack.pop(); if (node.getRight() != null) { node = node.getRight(); while (node != null) { stack.push(node); node = node.getLeft(); } } return o; } public void remove() { System.out.println("Nicht implementiert"); } } 3 Algorithmen und Datenstrukturen 2. Zeichnen von Bäumen Regeln zum Zeichnen von Bäumen - x-Position: Anzahl der Knoten, die zuvor "inorder" besichtigt werden - y-Position: Tiefe des Knotens im Baum. Mit dieser Funktionalität ist der abstrake Datentyp eines Binärbaumknotens (Klasse TreeNode) auszustatten: - Bestimmen der Position des Knotens - Zeichnen des Knotens Alternativen sind: - Modifizieren der Klasse - Spezialisierung - Visitor Konzept eines Visitor - Ganz allgemein kann man von einer Collection verlangen, dass einem Besucher (Visitor) alle Insassen (Elemente) genau einmal vorgestellt werden. - Ein beliebiges Objekt ist ein Visitor, wenn ihm Objekte vorgeführt werden können - Interface Visitor: Methode mit der die Kollektion (Collection) dem Visitor ihre Insassen vorstellt. - Beim Suchbaum hat man die Möglichkeit, Einträge sortiert auszugeben. - Ein Suchbaum sollte Visitable sein, um die Sortierung auszunutzen. - Implementiert man einen Visitor, der die Insassen in einem Stringbuffer abbildet, kann man den Baum sehr elegant darstellen lassen. Die Schnittstelle Visitor public interface NodeVisitor { public void visitNode(TreeNode n, int depth); } Anwendung der Schnittstelle - Erzeugen eines Visitors: - Start beim Wurzelknoten: - Aufruf der Methode visitNode() für jeden besuchten Knoten. Visitor-Unterstützung in TreeNode public void accept(NodeVisitor v, int depth) { if (left != null) left.accept(v,depth+1); v.visitNode(this,depth); if (right != null) right.accept(v,depth+1); } 4 Algorithmen und Datenstrukturen Bestimmen der Position: ArrangeVisitor public class ArrangeVisitor implements NodeVisitor { private int rank = 0; public ArrangeVisitor(){ } public void visitNode(TreeNode node,int depth) { node.setX(rank++ * 30); node.setY(depth * 30); } } Zeichnen der Knoten: DrawVisitor public class DrawVisitor implements NodeVisitor { private java.awt.Graphics g; public DrawVisitor(java.awt.Graphics g) { this.g = g; } public void visitNode(TreeNode node, int depth) { g.drawString(node.getElement().toString(), node.getX() + 15, node.getY()); g.fillOval(node.getX(),node.getY(),20,20); if (node.getLeft() != null) { g.drawLine(node.getX() + 10, node.getY() + 10, node.getLeft().getX() + 10, node.getLeft().getY() + 10); } if (node.getRight() != null) { g.drawLine(node.getX() + 10, node.getY() + 10, node.getRight().getX() + 10, node.getRight().getY() + 10); } } } Darstellung eines binären Suchbaums 5 Algorithmen und Datenstrukturen 3. Darstellung eines binären Suchbaums mit den Swing-Komponenten für binäre Bäume Zur grafischen Darstellung von Bäumen werden mit Swing neue Interfaces und Klassen eingeführt, die im Paket javax.swing.tree zusammengefasst sind. BinaryTreeModel Die Klasse BinaryTreeModel javax.swing.tree.TreeModel. implementiert import javax.swing.event.*; import javax.swing.tree.*; public class BinaryTreeModel implements javax.swing.tree.TreeModel { BinaryTree tree; public BinaryTreeModel(BinaryTree tree) { this.tree = tree; } public Object getRoot() { return tree.root(); } public Object getChild(Object parent,int index) { if (index == 0) { if (((TreeNode)parent).getLeft() == null ) return ((TreeNode)parent).getRight(); return ((TreeNode)parent).getLeft(); } else if (index == 1) return ((TreeNode)parent).getRight(); else return null; } public boolean isLeaf(Object node) { if ( ((TreeNode)node).getLeft() == null && ((TreeNode)node).getRight() == null) return true; else return false; } public int getChildCount(Object parent) { int i = 0; if (((TreeNode)parent).getLeft()!= null) i++; if (((TreeNode)parent).getRight() != null) i++; return i; } public int getIndexOfChild(Object parent,Object child) { // liefert den Index von child bzgl. parent if (parent == null || child == null) { return -1; } if ( ((TreeNode)parent).getLeft().equals(child)) { 6 die Schnittstelle Algorithmen und Datenstrukturen return 0; } if ( ((TreeNode)parent).getRight().equals(child)) { return 1; } return -1; } public void valueForPathChanged(TreePath path, Object newValue) { } public void addTreeModelListener(TreeModelListener l) { } public void removeTreeModelListener(TreeModelListener l) { } } BinaryTreeTest import import import import java.awt.*; javax.swing.*; javax.swing.tree.*; javax.swing.event.*; public class BinaryTreeTest extends JFrame { public BinaryTreeTest() { super("Erzeugen eines binaeren Baums"); BinaryTree tree = new BinaryTree(); for (int i=0; i< 20; i++) { String s = "Zufallszahl " + (int)(Math.random() * 100); tree.insert(s); } BorderLayout borderLayout = new BorderLayout(); JTree jTree = new JTree(); jTree.setModel(new BinaryTreeModel(tree)); JScrollPane scrollPane = new JScrollPane(jTree); Container contentPane = (JPanel) this.getContentPane(); contentPane.setLayout(borderLayout); contentPane.add(scrollPane,BorderLayout.CENTER); // scrollPane.getViewport().add(jTree,null); setSize(300,300); setVisible(true); } public static void main(String args[]) { new BinaryTreeTest(); } } Darstellung 7 Algorithmen und Datenstrukturen 8