Java 绘制树状图的递归方法

Java 绘制树状图的递归方法,java,recursion,dendrogram,Java,Recursion,Dendrogram,我必须画一个这样的树状图: 但要大得多。这是一些数据集群表示的一个选项。 所以我坚持用递归的方法来绘制树状图 我确实得到了绘制方法应该是这样的原则 draw(cluster){ if(clusters.hasChildren()){ draw(cluster.child1) draw(cluster.child2) } //draw actual cluster here } 但我在实现它时遇到了很大困难 我目

我必须画一个这样的树状图:

但要大得多。这是一些数据集群表示的一个选项。 所以我坚持用递归的方法来绘制树状图

我确实得到了绘制方法应该是这样的原则

 draw(cluster){
      if(clusters.hasChildren()){
         draw(cluster.child1)
         draw(cluster.child2)
      }
      //draw actual cluster here
    }
但我在实现它时遇到了很大困难

我目前的方法是这样的

drawCluster(cluster, startX, startY){
   if(cluster.hasChildren()){
      drawCluster, cluster.child1(), cluster.child1().getDepth * 30, height - cluster.child2.getWidth * 20)
      drawCluster, cluster.child2(), cluster.child2().getDepth * 30, height - 20)
   }
   if cluster.getDepth() == 0 )
      drawLine(500 - 30), height, 500)
   else
      drawLine(500 - (width * 30), height, 500);
}
我用来画的空间是500px,宽度和高度的总和是 现在我只为每个簇画一条线,只是为了得到正确的距离。 每次我从500px减去簇深度乘以20开始画一条线,直到第500个像素

此外,高度应为最大高度。例如,在绘图时,假设参数中高度为(1,2)的簇为40。等等

但这并不奏效。基本上,每次调用draw方法时,我都会被困在如何更改值的问题上。除了行的x起点和y起点之外,我还需要传递更多的变量吗

任何帮助都将不胜感激,因为我还有最后期限要赶


提前感谢。

因为您不能提供任何特定的数据结构,所以我只能提供伪代码。最重要的部分似乎是自下而上构建树,在构建较高层级时,考虑较低层级的计算宽度

func drawCluster(node:Node, depth:int, top:int) -> int:
    if node has children:
        next_top = top
        for child in node.children:
            drawLine(depth, top, depth + 1, next_top)
            next_top = drawCluster(child, depth + 1, next_top)
        return next_top
    else:
        drawLabel(node.label, depth, top)
        return top + text_height

显然,我无法测试这个,但我曾经为一个图形布局算法做过类似的测试,所以我认为这应该可以工作,除非我误解了你的问题。

既然你不能提供任何特定的数据结构,我只能给出伪代码。最重要的部分似乎是自下而上构建树,在构建较高层级时,考虑较低层级的计算宽度

func drawCluster(node:Node, depth:int, top:int) -> int:
    if node has children:
        next_top = top
        for child in node.children:
            drawLine(depth, top, depth + 1, next_top)
            next_top = drawCluster(child, depth + 1, next_top)
        return next_top
    else:
        drawLabel(node.label, depth, top)
        return top + text_height

显然,我不能测试这个,但我曾经为一个图形布局算法做过类似的事情,所以我认为这应该是可行的,除非我误解了你的问题。

递归地画一个完全像这样的树状图实际上有点棘手

叶节点无法“知道”其y位置。此外,没有节点“直接”知道必须在哪里绘制,以及如何绘制将其连接到其子节点的线:在绘制所有叶子(或每个节点的子节点)之前,所有这些信息都不可用

我认为迭代解决方案可能更容易、更灵活。然而,这里有一个使用递归方法的实现。请注意,这是一个非常简单的实现,它(例如)假设树状图的数据结构是一个二叉树,但这应该与您发布的示例一致

顺便说一句:它填满了可用的空间,我强烈建议避免“魔法常数”和关于节点或绘图区域像素大小的假设,如
绘图线(500-(宽度*30),高度,500)
。即使您不想根据树的大小和叶节点的数量来计算这些,您也应该至少为其引入变量,以便以后可以更轻松地进行更改

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class DendrogramPaintTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        DendrogramPaintPanel panel = new DendrogramPaintPanel();
        f.getContentPane().add(panel);

        f.setSize(1000,800);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class Node<T> 
{
    private final T contents;
    private final List<Node<T>> children;

    Node(T contents)
    {
        this.contents = contents;
        this.children = Collections.emptyList();
    }

    Node(Node<T> child0, Node<T> child1)
    {
        this.contents = null;

        List<Node<T>> list = new ArrayList<Node<T>>();
        list.add(child0);
        list.add(child1);
        this.children = Collections.unmodifiableList(list);
    }

    public T getContents()
    {
        return contents;
    }

    public List<Node<T>> getChildren()
    {
        return Collections.unmodifiableList(children);
    }
}


class DendrogramPaintPanel extends JPanel
{
    private static <T> Node<T> create(T contents)
    {
        return new Node<T>(contents);
    }
    private static <T> Node<T> create(Node<T> child0, Node<T> child1)
    {
        return new Node<T>(child0, child1);
    }


    private Node<String> root;
    private int leaves;
    private int levels;
    private int heightPerLeaf;
    private int widthPerLevel;
    private int currentY;
    private final int margin = 25;

    DendrogramPaintPanel()
    {
        root =
            create(
                create(
                    create("10"),
                    create(
                        create("9"),
                        create(
                            create("8"), 
                            create("7")
                        )
                    )
                ),
                create(
                    create(
                        create("6"),
                        create("5")
                    ),
                    create(
                        create("4"),
                        create(
                            create("3"),
                            create(
                                create("2"),
                                create("1")
                            )
                        )
                    )
                )
            );
    }

    private static <T> int countLeaves(Node<T> node)
    {
        List<Node<T>> children = node.getChildren();
        if (children.size() == 0)
        {
            return 1;
        }
        Node<T> child0 = children.get(0);
        Node<T> child1 = children.get(1);
        return countLeaves(child0) + countLeaves(child1);
    }

    private static <T> int countLevels(Node<T> node)
    {
        List<Node<T>> children = node.getChildren();
        if (children.size() == 0)
        {
            return 1;
        }
        Node<T> child0 = children.get(0);
        Node<T> child1 = children.get(1);
        return 1+Math.max(countLevels(child0), countLevels(child1));
    }


    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;

        leaves = countLeaves(root);
        levels = countLevels(root);
        heightPerLeaf = (getHeight() - margin - margin) / leaves;
        widthPerLevel = (getWidth() - margin - margin)/ levels;
        currentY = 0;

        g.translate(margin, margin);
        draw(g, root, 0);
    }


    private <T> Point draw(Graphics g, Node<T> node, int y)
    {
        List<Node<T>> children = node.getChildren();
        if (children.size() == 0)
        {
            int x = getWidth() - widthPerLevel - 2 * margin;
            g.drawString(String.valueOf(node.getContents()), x+8, currentY+8);
            int resultX = x;
            int resultY = currentY;
            currentY += heightPerLeaf;
            return new Point(resultX, resultY);
        }
        if (children.size() >= 2)
        {
            Node<T> child0 = children.get(0);
            Node<T> child1 = children.get(1);
            Point p0 = draw(g, child0, y);
            Point p1 = draw(g, child1, y+heightPerLeaf);

            g.fillRect(p0.x-2, p0.y-2, 4, 4);
            g.fillRect(p1.x-2, p1.y-2, 4, 4);
            int dx = widthPerLevel;
            int vx = Math.min(p0.x-dx, p1.x-dx);
            g.drawLine(vx, p0.y, p0.x, p0.y);
            g.drawLine(vx, p1.y, p1.x, p1.y);
            g.drawLine(vx, p0.y, vx, p1.y);
            Point p = new Point(vx, p0.y+(p1.y - p0.y)/2);
            return p;
        }
        // Should never happen
        return new Point();
    }
}
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.Point;
导入java.util.ArrayList;
导入java.util.Collections;
导入java.util.List;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.SwingUtilities;
公共类树状图测试
{
公共静态void main(字符串[]args)
{
SwingUtilities.invokeLater(新的Runnable()
{
@凌驾
公开募捐
{
createAndShowGUI();
}
});
}
私有静态void createAndShowGUI()
{
JFrame f=新的JFrame();
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
树状图PaintPanel panel=新树状图PaintPanel();
f、 getContentPane().add(面板);
f、 设置大小(1000800);
f、 setLocationRelativeTo(空);
f、 setVisible(真);
}
}
类节点
{
私人最终考试内容;
私人最终名单儿童;
节点(T内容)
{
this.contents=目录;
this.children=Collections.emptyList();
}
节点(节点child0、节点child1)
{
this.contents=null;
列表=新的ArrayList();
列表。添加(0);
添加(child1);
this.children=Collections.unmodifiableList(列表);
}
公共T getContents()
{
返回内容;
}
公共列表getChildren()
{
返回集合。不可修改列表(子项);
}
}
类树状图PaintPanel扩展了JPanel
{
私有静态节点创建(T内容)
{
返回新节点(内容);
}
私有静态节点创建(节点child0,节点child1)
{
返回新节点(child0,child1);
}
私有节点根;
私密的叶子;
私人智力水平;
私人海特佩利夫酒店;
每级私有整数;
私营企业;
私人最终整数保证金=25;
树状图面板()
{
根=
创造(
创造(
创建(“10”),
创造(
创建(“9”),
创造(
创建(“8”),
创建(“7”)
)
)
),
创造(
创造(
创建(“6”),
创建(“5”)
),
创造(
创建(“4”),
创造(
创建(“3”),
创造(
创建(“2”),
创建(“1”)
)
)