如何整理我的 Java 代码,因为它有太多循环

How to tidying my Java code because it has too many looping

我有一个节点class用来表示一个树结构。在这个 class 中,我定义了一个应该产生以下输出的打印方法:

data
--a (1024)
--b (256)
----ba (100)
------baa (500)
------bac (25)
----bc (150)
----bd (125)
----be (75)
--c (35)
----cb (30)
----ce (50)

这是我编写打印方法的方式:

public void print(String name) {
        Node node = this.find(name);
        System.out.println(node.name);
        if (node.childrensCount != 0) {
            for (int i = 0; i < node.childrensCount; i++) {
                Node children = node.childrens[i];
                System.out.println("--" + children.name + " (" + children.value + ")");
                if (children.childrensCount != 0) {
                    for (int j = 0; j < children.childrensCount; j++) {
                        Node grandChildren = children.childrens[j];
                        System.out.println("----" + grandChildren.name + " (" + grandChildren.value + ")");
                        if (grandChildren.childrensCount != 0) {
                            for (int k = 0; k < grandChildren.childrensCount; k++) {
                                Node greatGrandChildren = grandChildren.childrens[k];
                                System.out.println("------" + greatGrandChildren.name + " (" + greatGrandChildren.value + ")");
                            }
                        }
                    }
                }
            }
        }
    }

这也是节点 class 实现,可以更好地帮助您理解场景:

public class Node {

    int value;
    String name;
    Node parent;
    int childrensCount;
    Node[] childrens = new Node[100];

    public Node(int value, String name) {
        this.value = value;
        this.name = name;
        this.childrensCount = 0;
    }

    public Node(String name) {
        this.name = name;
    }
    
    public void addChildren(Node node)
    {
        this.childrens[childrensCount] = node;
        childrensCount++;
    }
    
    public void setParent(Node parent)
    {
        this.parent = parent;
    }
    
    public boolean hasParent(){
        return this.parent != null;
    }
    
    public int sumValue(){
        int sum = 0;
        sum += this.value;
        for (int i = 0; i < childrensCount; i++) {
            sum += childrens[i].value;
        }
        return sum;
    }
}

我认为我的代码很脏,可以改进。我想定义一个递归方法,但我仍然不明白递归是如何工作的。有人可以帮我吗?

您可以使用递归来完成。我没有尝试重建所有复杂的数据,而是使用我自己的节点 class.

创建了一个简单的示例
class MyNode {
    String name;
    List<MyNode> nodeList;
    
    public MyNode(String name) {
        this.name = name;
    }
    
    public MyNode setNodeList(List<MyNode> list) {
        nodeList = list;
        return this;
    }
    @Override
    public String toString() {
        return name;
    }
    public List<MyNode> getNodeList() {
        return nodeList;
    }
}

    
    
MyNode m = new MyNode("a");
List<MyNode> list1 =
        List.of(new MyNode("aaa"), new MyNode("aab"),
                new MyNode("aac"), new MyNode("aad"));
List<MyNode> list2 = List.of(new MyNode("aba"),
        new MyNode("abb"), new MyNode("abc"));
List<MyNode> list3 = List.of(new MyNode("aca"),
        new MyNode("acb"), new MyNode("acc"));
List<MyNode> list4 = List.of(new MyNode("ada"),
        new MyNode("adb"), new MyNode("adc"));

List<MyNode> mainlist = List.of(new MyNode("aa").setNodeList(list1),
        new MyNode("ab").setNodeList(list2), new MyNode("ac").setNodeList(list3), new MyNode("ad").setNodeList(list4));
        

m.setNodeList(mainlist);
print(m,1);

打印

--a
----aa
------aaa
------aab
------aac
------aad
----ab
------aba
------abb
------abc
----ac
------aca
------acb
------acc
----ad
------ada
------adb
------adc
  • 这是通过让 print 方法调用自身并调整缩进级别来实现的。
  • 打印当前节点和级别。
  • 如果该节点的列表是 non-null,则为列表中的每个节点调用 print 方法并重复该过程,更新级别。
  • 在每个 return 之后,该方法从它停止的地方开始,因为它继续调用自身和 return.
  • 这是遍历 hie-rarchical 数据集的常见过程,适用于不确定数量的级别。
public static void print(MyNode node, int level) {
    System.out.println("-".repeat(level*2) + node);
    if (node.getNodeList() != null) {
       for (MyNode n : node.getNodeList()) {
                print(n, level+1);
        }
    }
}

我建议您阅读递归过程。在某些情况下,您还可以在遍历层次结构时 return 值。

要定义递归方法,您首先需要确定基本案例和递归案例。在这种情况下,基本情况是传递给打印的节点为空,而递归情况是仍有子节点要打印。

因为我知道您的程序可能是学校练习,所以我将避免讨论什么可以或不可以更好地实现您的 Node class。毫无疑问,使用 Collections 框架中的 List 数据结构而不是 100 个元素的固定数组是更好的选择,但我认为你的老师希望在开始时保持简单,而你很可能没有涵盖 Collections框架,但自从您说您仍在为递归而苦苦挣扎(这完全没问题,我们都必须从某个地方开始!)。因此,我将保留您的 class 实现的原样。我只会添加一些方法和一些调整。

在这里,我实施了一个解决方案来向您展示递归打印的工作原理。请记住,我将打印实现拆分为两种方法只是为了让客户端更容易调用它。事实上,客户不应该知道也不应该为使其工作的内部实现细节而烦恼。


//Test class
public class Test {
    public static void main(String[] args) {
        Node root = new Node(1, "test1", new Node[]{
                new Node(2, "test2", new Node[]{
                        new Node(5, "test6", new Node[]{})
                }),
                new Node(3, "test3", new Node[]{
                        new Node(6, "test6", new Node[]{}),
                        new Node(7, "test7", new Node[]{
                                new Node(8, "test8", new Node[]{})
                        })
                }),
                new Node(4, "test4", new Node[]{})
        });

        root.print();
    }
}

class Node {

    int value;
    String name;
    Node parent;
    int childrenCount;
    Node[] children = new Node[100];

    public Node(int value, String name) {
        this.value = value;
        this.name = name;
        this.childrenCount = 0;
    }

    //Added second constructor only to ease the test
    public Node(int value, String name, Node[] children) {
        this.value = value;
        this.name = name;
        this.children = children;
        this.childrenCount = getNumChildren(children);
    }

    public Node(String name) {
        this.name = name;
    }

    //Fixed possible exception when added more than 100 elements
    public boolean addChildren(Node node) {
        if (childrenCount == this.children.length) {
            return false;
        }
        this.children[childrenCount++] = node;
        return true;
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    public boolean hasParent() {
        return this.parent != null;
    }

    public int sumValue() {
        int sum = 0;
        sum += this.value;
        for (int i = 0; i < childrenCount; i++) {
            sum += children[i].value;
        }
        return sum;
    }

    //small utility method only to compute the effective number of children when an array is passed within the new constructor
    public static int getNumChildren(Node[] children) {
        int num = 0;
        while (num < children.length && children[num] != null) {
            num++;
        }
        return num;
    }

    //print method invoked by the client of the class
    public void print() {
        printRec(this, 0);
    }

    //recursive print
    private void printRec(Node n, int numCall) {
        //identifying the base case
        if (n == null) {
            return;
        }

        //Printing as many dahses as the depth of the current child node
        for (int i = 1; i <= numCall; i++) {
            System.out.print("--");
        }

        //printing the node info
        System.out.println(n.name + " (" + n.value + ")");

        //recursively invoking the print method for each child
        for (int i = 0; i < n.childrenCount; i++) {
            printRec(n.children[i], numCall + 1);
        }
    }
}

这里我只想补充几点:

  • children 已经是 child 的复数形式。你不需要调用你的数组 childrens。

  • 如果您添加的元素超过 100 个,您之前执行的 add 方法可能会引发 ArraIndexOutOfBoundsException。通常,当操作可能失败时,该方法应该 return 一个布尔值来告诉客户端操作是否成功。