Преобразование файла с разделителями в дерево

Я работаю над преобразованием файла с разделителями в упорядоченную древовидную структуру. Ниже приведен пример ввода...

1.2.3.4.5
1.3.2.4.5
1.2.4.5.6

Мне нужно иметь возможность преобразовать это в вывод, подобный следующему (в древовидной структуре с возможностью поиска)...

1
-2
--3
---4
----5
--4
---5
----6
-3
--2
---4
----5

Мои мысли о решении этой проблемы заключались в том, чтобы...

  1. Повторите текстовый файл и создайте список массивов, представляющий каждую строку.
  2. Используйте Collections.sort(), чтобы отсортировать список массивов.
  3. Используйте TreeMap для хранения «базовой» записи в качестве ключа (в данном случае 1) и arrayList строк, чтобы содержать все записи.
  4. Повторите ключи TreeMap и преобразуйте его arrayList в LinkedHashSet, который содержит узлы, представляющие каждую запись.
  5. Повторите ключи Дерева и распечатайте каждый узел, купив его значение индекса.

Я думал, что все работает хорошо, но когда я начал тестировать этот подход, я обнаружил, что мой вывод выглядит следующим образом...

1
-2
--3
---4
----5
--4
---5
----6
-3
--2

Как видно, узлы под 3/2/xx отсутствуют, это связано с логикой, которую я использую для построения LinkedHashSet для значений моего узла (узел (3, 4)) будет просто проигнорирован, потому что это дубликат Узел. Я думал, что иду в правильном направлении, но теперь вижу, что моя логика явно ошибочна. Есть ли у кого-нибудь предложения по подходу к чему-то подобному? Мой текущий код ниже...

TreeBuilder.java

public class TreeBuilder {

 public static void main(String[] args) {

    // Get a list of records
    List<String> data = new ArrayList<String>();
    data.add("1.2.3.4.5");
    data.add("1.3.2.4.5");
    data.add("1.2.4.5.6");

    Collections.sort(data);

    // Build the "Base" tree
    TreeMap<String, List<String>> tree = buildBaseTree(data);

    // Build the target tree structure
    TreeMap<String, LinkedHashSet<Node>> finalTree = convertListToSet(tree);

    printRecords(finalTree);

 }

 public static void printRecords(
        TreeMap<String, LinkedHashSet<Node>> recordTree) {

    System.out.println("---------Records---------");

    for (Map.Entry<String, LinkedHashSet<Node>> entry : recordTree
            .entrySet()) {

        System.out.println(entry.getKey());

        // Print out the structured data
        StringBuilder stringBuilder = new StringBuilder();
        Iterator<Node> iterator = entry.getValue().iterator();
        while (iterator.hasNext()) {

            Node node = iterator.next();
            for (int i = 0; i < node.index; i++) {
                stringBuilder.append("-");
            }

            System.out.println(stringBuilder.toString() + node.value);

            // "reset" the builder
            stringBuilder.setLength(0);
        }
    }

 }

 private static TreeMap<String, LinkedHashSet<Node>> convertListToSet(
        TreeMap<String, List<String>> tree) {

    TreeMap<String, LinkedHashSet<Node>> finalMap = new TreeMap<String, LinkedHashSet<Node>>();
    LinkedHashSet<Node> linkedHashSet = new LinkedHashSet<Node>();

    // Iterate the entry set
    for (Map.Entry<String, List<String>> entry : tree.entrySet()) {

        List<String> records = entry.getValue();
        for (String record : records) {
            String[] recordArray = record.split("\\.");

            for (int i = 1; i < recordArray.length; i++) {
                Node node = new Node(i, Integer.parseInt(recordArray[i]));
                linkedHashSet.add(node);
            }
        }

        finalMap.put(entry.getKey(), linkedHashSet);

        // reset our linkedHashSet
        linkedHashSet = new LinkedHashSet<Node>();

    }

    System.out.println("Final map " + finalMap);

    return finalMap;
 }

 /**
  * Builds a tree with base record keys and a list of records for each key.
  * 
  * @param data
  * @return
  */
 private static TreeMap<String, List<String>> buildBaseTree(List<String> data) {

    TreeMap<String, List<String>> tree = new TreeMap<String, List<String>>();
    List<String> recordList = null;

    // First find all base records
    for (String record : data) {

        String[] baseEntry = record.split("\\.");
        if (!tree.containsKey(baseEntry[0])) {
            recordList = new ArrayList<String>();
            tree.put(baseEntry[0], recordList);
        }
    }

    // Now place all sub-records in each base record
    for (String record : data) {

        String[] baseEntry = record.split("\\.");
        tree.get(baseEntry[0]).add(record);
    }

    System.out.println("------------------Base Tree---------------");
    System.out.println(tree);
    System.out.println("------------------------------------------");

    return tree;
 }

 private static List<String> readData(String file) {

    BufferedReader bufferedReader = null;
    try {
        bufferedReader = new BufferedReader(new FileReader(new File(file)));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    List<String> data = new ArrayList<String>();

    // Get a list of all the records
    String line = null;
    try {
        while ((line = bufferedReader.readLine()) != null) {
            data.add(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    // Sort the list so its ordered
    System.out.println("-------------Sorted Data Set-----------");
    Collections.sort(data);
    for (String record : data) {
        System.out.println(record);
    }
    System.out.println("---------------------------------------");

    return data;
 }
}

Node.java

public class Node implements Comparable<Node> {

 int index;
 int value;

 public Node(int index, int value) {
    this.index = index;
    this.value = value;
 }

 public int getIndex() {
    return index;
 }

 @Override
 public String toString() {
    return "Node [index=" + index + ", value=" + value + "]";
 }

 public void setIndex(int index) {
    this.index = index;
 }

 public int getValue() {
    return value;
 }

 public void setValue(int value) {
    this.value = value;
 }

 @Override
 public int compareTo(Node o) {

    Node otherNode = (Node) o;

    if (this.index > otherNode.index)
        return 1;

    if (this.index < otherNode.index) {
        return -1;
    }

    return 0;
 }

 @Override
 public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + index;
    result = prime * result + value;
    return result;
 }

 @Override
 public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Node other = (Node) obj;
    if (index != other.index)
        return false;
    if (value != other.value)
        return false;
    return true;
 }

}

person Jason    schedule 15.02.2016    source источник
comment
Я не могу понять ваши требования. Предполагается ли, что дерево рассматривает список как упорядоченный и использует общую структуру узлов для списков, начинающихся с общих атрибутов? Если это так, вы не можете сортировать списки перед вставкой, так как существующий порядок имеет большое значение. Я добавлю ответ, исходя из того, что это ваши требования.   -  person sprinter    schedule 16.02.2016


Ответы (2)


Это не должно быть сложно. Все, что вам нужно, это SortedMap из SortedMap экземпляров, и есть только одна хитрость: рекурсивная параметризация для безопасности типов (при желании).

package com.acme;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

public class Main {

    public static void main(String[] args) {
        List<String> rows = new ArrayList<>();
        rows.add("1.2.3.4.5");
        rows.add("1.3.2.4.5");
        rows.add("1.2.4.5.6");

        MyTreeMap root = new MyTreeMap();
        for (String row : rows) {
            MyTreeMap n = root;
            String[] cells = row.split("\\.");
            for (String cell : cells) {
                MyTreeMap child = n.get(cell);
                if (child == null) {
                    n.put(cell, child = new MyTreeMap());
                }
                n = child;
            }
        }

        print(root, "", "-");
    }

    static void print(MyTreeMap m, String indentationStr, String indentationStrAddition) {
        for (Entry<String, MyTreeMap> o : m.entrySet()) {
            System.out.println(indentationStr + o.getKey());
            print(o.getValue(), indentationStr + indentationStrAddition, indentationStrAddition);
        }
    }

    /**
     * This is just a construct that helps us to parameterize recursively.
     */
    static class MyTreeMap extends TreeMap<String, MyTreeMap> {private static final long serialVersionUID = 1L;}
}
person Brian    schedule 15.02.2016
comment
Извините за задержку с ответом, это именно то, что я искал. Мне потребовалось несколько прочтений, чтобы понять, что здесь происходит, но для всех, кто находит это запутанным, подход, предложенный Брайаном, очень похож на построение бинарного дерева. Концепция в основном начинается с корня для каждого столбца входных данных, проходит по текущему дереву, если ветка не имеет дочернего элемента, добавляет его. Спасибо Брайан! - person Jason; 24.02.2016

Насколько я понимаю вашу проблему, вы хотите создать деревья поиска из списка целых чисел, которые имеют общие префиксы. Если это так, то я думаю, вы можете добиться этого с гораздо меньшим количеством кода:

private static class TreeNode {

    private final Map<Integer, TreeNode> children = new HashMap<>();

    public void insert(List<Integer> values) {
        if (!values.isEmpty()) {
            children.putIfAbsent(values.get(0), new TreeNode());
            children.get(values.get(0)).insert(values.subList(1, values.size()));
        }
    }

    public void print(int level) {
        for (Map.Entry<Integer, TreeNode> entry : children.entrySet()) {
            System.out.print(String.join("", Collections.nCopies(level, "-")));
            System.out.println(entry.getKey());
            entry.getValue().print(level + 1);
        }
    }
}

Я не уверен, собираетесь ли вы сортировать список целых чисел. Если это так, вы можете добавить Collections.sort в соответствующее место кода.

person sprinter    schedule 16.02.2016