如何迭代Route(?)数据结构(Java)

时间:2019-03-13 17:41:56

标签: java data-structures iterator

我已经建立了一个数据结构,这是我之前从未见过的。该结构有点像一个图形,但是却像一个图形,它具有一个或多个起点,您可以使用键从中起点到路线中的下一个终点。在我正在开发的应用程序(插件)中,我需要迭代路径中的所有值以为路径中的每个点生成转换配置。

简体: 链接节点 每个LinkedNode都有一个值和下一个可能前进的节点的哈希图。 (我称它为“步进”)

This is one way in which the structure may look like. The nodes with keys BEGIN, START, ALOITA are the starting nodes of the route, whilst in the other nodes their key is not shown in the picture. There may be nodes which have same key, but the a node cannot point to two nodes with same keys (same with starting nodes)

路线: Route具有一个可从其开始的LinkedNodes哈希表,以及两个堆栈,可用于取消(撤消)和重新执行(重做)路由步骤。该路由可能包含具有相同键值的环路和节点,但是一个LinkedNode只能指向具有不同键的节点(例如,节点A可以指向具有键a,b,c,d的节点,但不能指向节点a, a,b,c,因为有相似的键)。第一个堆栈可保持您在路线中的前进位置,也可用于确定您在路线中的当前位置(在堆栈顶部)。您可能永远都不会退出堆栈(如果A指向a,b,c,它将无法尝试使用键d转到节点)。

由于多个节点可能具有相同的密钥,因此无法仅将它们“存储到地图中”,并且由于生成UUID的结构非常困难,因为我一直在尝试使其工作一段时间。使用类似DFS的常规算法也是有问题的,因为它将导致无限循环,除非检查一个密钥没有重复两次。我尝试将路由中的所有节点收集到一个列表中,但没有成功,非常感谢您的帮助。

LinkedNode:

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * A node that is linked forward to next nodes. Used for example by
 * Web data structure
 * @author Nuubles
 *
 * @param <K> Key
 * @param <V> Value
 */
public class LinkedNode <K extends Comparable<K>,V> implements Comparable<LinkedNode<K,V>>, Iterable<LinkedNode<K,V>>{
    private K key;
    private V value;
    private Map<K,LinkedNode<K,V>> nextNodes = new HashMap<K,LinkedNode<K,V>>();

    /**
     * Initializes a new linkedNode with a key and a value
     * @param key Key of this node
     * @param value Value of this node
     */
    public LinkedNode(final K key, final V value) throws IllegalArgumentException
    {
        if(key == null)
            throw new IllegalArgumentException("Given key was null!");

        this.key = key;
        this.value = value;
    }

    /**
     * Returns the next node using a key 
     * @param key Key which the next node needs to have
     * @return Next node after this or null if there's no next node
     */
    public LinkedNode<K,V> getNextNode(final K key)
    { return nextNodes.get(key); }

    /**
     * Returns the all the next nodes after this node
     * @return Collection of next nodes after this
     */
    public Collection<LinkedNode<K,V>> getNextNodes()
    { return nextNodes.values(); }

    /**
     * Returns the next possible routes after this node
     * @return Routes after this node
     */
    public Collection<K> getNextKeys()
    { return nextNodes.keySet(); }

    /**
     * Return the next node which was removed or null
     * if no node was found
     * @param key Key of the node to be removed
     * @return The removed node
     */
    public LinkedNode<K,V> removeNextNode(final K key)
    { return this.nextNodes.remove(key); }

    /**
     * Adds a next node to this node and overrides old if there
     * is some node with same key
     * @param key Key of the next node to add
     * @param value Value of the next node to add
     */
    public void putNextNode(final K key, final V value)
    {
        LinkedNode<K,V> node = new LinkedNode<K,V>(key,value);
        this.nextNodes.put(key, node);
    }

    /**
     * Adds a next node to this node and overrides old if there
     * is some node with same key
     * @param node Node to add
     */
    public void putNextNode(LinkedNode<K,V> node)
    { this.nextNodes.put(node.key, node); }

    /**
     * Like putNextNode, but if key already exists,
     * does not add.
     * @param key Key of the node
     * @param value Value of the node
     * @return Was the addition successful
     */
    public boolean addNextNode(final K key, final V value)
    {
        if(this.nextNodes.containsKey(key))
            return false;
        LinkedNode<K,V> node = new LinkedNode<K,V>(key,value);
        this.nextNodes.put(key, node);
        return true;
    }

    /**
     * Like putNextNode, but if key already exists,
     * does not add.
     * @param node Node to add
     * @return Was the addition successful
     */
    public boolean addNextNode(final LinkedNode<K,V> node)
    {
        if(this.nextNodes.containsKey(node.key))
            return false;
        this.nextNodes.put(key, node);
        return true;
    }

    /**
     * Removes all the nodes after this node
     */
    public void clearNextNodes()
    { this.nextNodes.clear(); }

    /**
     * Compares the values of the nodes, but not the
     * next nodes. If nodes contain null then return false
     */
    @Override
    public boolean equals(Object node)
    {
        if(!(node instanceof LinkedNode<?,?>))
            return false;

        LinkedNode<?,?> converted = (LinkedNode<?,?>)node;

        //Verify there are no null values
        if(this.key == null || this.value == null || converted.key == null || converted.value == null)
            return false;

        return (this.key.equals(converted.key) && this.value.equals(converted.value));
    }

    /**
     * Does this node have next nodes to traverse to
     * @return True if can travel further, false otherwise
     */
    public boolean isEmpty()
    { return this.nextNodes.isEmpty(); }

    /**
     * Compare the keys of this and a given node
     */
    @Override
    public int compareTo(LinkedNode<K,V> node)
    { return this.key.compareTo(node.key); }

    /**
     * Checks if there is a next node that has given key
     * @param key Key to look for
     * @return Was there a next node with given key
     */
    public boolean hasNextNode(final K key)
    { return this.nextNodes.containsKey(key); }

    /*==========[ ACCESSORS ]=========*/

    /**
     * Get this nodes key
     * @return The key of this node
     */
    public K getKey()
    { return this.key; }

    /**
     * Set the key of this node
     * @param key the new node for this key
     * @return Old key of this node
     */
    public K setKey(final K key)
    {
        K k = this.key;
        this.key = key;
        return k;
    }

    /**
     * Get the value of this node
     * @return The value of this node
     */
    public V getValue()
    { return this.value; }

    /**
     * Set the value of this node
     * @param value The value of this node
     * @return The old value of this value
     */
    public V setValue(final V value)
    { 
        V v = this.value;
        this.value = value;
        return v;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterator<LinkedNode<K, V>> iterator() {
        return this.nextNodes.values().iterator();
    }
}

路线:

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class Route<K extends Comparable<K>,V> implements Iterable<Entry<K,V>>{
    //Path we've made
    private Deque<LinkedNode<K,V>> path = new ArrayDeque<LinkedNode<K,V>>();
    //Path we've made but stepped back from
    private Deque<LinkedNode<K,V>> futurePath = new ArrayDeque<LinkedNode<K,V>>();
    //Nodes in this web
    private Map<K,LinkedNode<K,V>> nodes = new HashMap<K,LinkedNode<K,V>>();

    /**
     * Initializes the route to start with given path startpoints
     * @param startValues Points which from the path can be started
     */
    public Route(Collection<Entry<K,V>> startValues)
    {
        for(Entry<K,V> entry : startValues)
            nodes.put(entry.getKey(), new LinkedNode<K,V>(entry.getKey(),entry.getValue()));
    }

    /**
     * Initializes an empty route with no startpoints
     * to start traversal
     */
    public Route()
    {}

    /**
     * Initializes a route with one startpoint to start
     * traversal from
     * @param key Key of the startpoint
     * @param value Value of the startpoint
     */
    public Route(final K key, final V value)
    { nodes.put(key, new LinkedNode<K,V>(key,value)); }

    /**
     * Traverses a given path starting from current point.
     * Stops traversal if unsuccessful, but stays at endpoint.
     * Cannot perform restep after this action, traversal is stored in unstep.
     * @param points Keys to travel along
     * @return True if traversal was successful to point, False if unsuccessful and
     * stayed at endpoint 
     */
    public boolean traversePoints(List<K> points)
    {
        //Traverse the points
        for(K point : points)
        {
            if(!this.hasNextNode(point))
                return false;
            this.step(point);
        }

        return true;
    }

    /**
     * Connects current node to another node following the
     * given path. Will not replace existing node if there is one
     * with same key as the node being added.
     * Basically creates a bridge from current node to the node
     * in the given path
     * @param points Path to follow to next node FROM START
     * @return Was the route created to the path
     */
    public boolean addCurrentToPath(List<K> points)
    {
        //If empty list is given, return false
        if(points == null || points.size() == 0)
            return false;

        if(this.path.peek().hasNextNode(points.get(points.size()-1)))
            return false;

        return this.putCurrentToPath(points);
    }

    /**
     * Connects current node to another node following the
     * given path and replaces old node with same key if there is one.
     * Basically creates a bridge from current node to the node
     * in the given path
     * @param points Path to follow to next node FROM START
     * @return Was the route created to the path
     */
    public boolean putCurrentToPath(List<K> points)
    {
        //If empty list is given, return false
        if(points == null || points.size() == 0)
            return false;

        if(this.nodes.isEmpty())
            return false;

        boolean firstStep = true;
        LinkedNode<K,V> currentNode = null;

        //Traverse path along points
        for(K point : points)
        {
            if(firstStep)
            {
                //Node to start the traversal from
                currentNode = this.nodes.get(points);
                if(currentNode == null)
                    return false;
                continue;
            }

            currentNode = currentNode.getNextNode(point);

            if(currentNode == null)
                return false;
        }

        //currentNode will be the node to which current node needs to connect
        this.path.peek().putNextNode(currentNode);
        return true;
    }

    /**
     * Traverses a given path starting from current point.
     * Stops traversal if unsuccessful, but stays at endpoint.
     * Cannot perform restep after this action, traversal is stored in unstep.
     * @param points Keys to travel along
     * @return True if traversal was successful to point, False if unsuccessful and
     * stayed at endpoint 
     */
    public boolean traversePoints(@SuppressWarnings("unchecked") K... points)
    {
        if(points == null)
            return false;

        //Traverse the points
        for(K point : points)
        {
            if(!this.hasNextNode(point))
                return false;
            this.step(point);
        }

        return true;
    }

    /**
     * Returns the possible path choices for next key
     * @return
     */
    public Collection<K> getNextKeys()
    { 
        if(path.isEmpty()) //Begin a new path
            return nodes.keySet();
        else //Continue old path
            return path.peek().getNextKeys();
    }


    /**
     * Traverses forward in path to the next object.
     * Cannot restep after this is ran unless unsteps are made
     * @param key Key to which object should we traverse to
     * @return List of keys possible to continue from the next node
     */
    public Collection<K> step(final K key)
    {
        if(path.isEmpty())
        {
            LinkedNode<K,V> node = nodes.get(key);
            if(node == null) //There is no next node after this, return empty list
                return new ArrayDeque<K>();
            path.push(node); //Add new node to the route traversed
            futurePath.clear();
            return node.getNextKeys();
        }

        //We've already traversed some path, continue on
        LinkedNode<K,V> node = path.peek().getNextNode(key);
        if(node == null) //There is no next node after this, return empty list
            return new ArrayDeque<K>();

        path.push(node);
        futurePath.clear();
        return node.getNextKeys();
    }

    /**
     * Removes the next node if it exists
     * @param key Key of the next node to remove
     * @return Value of the node removed
     */
    public V removeNextNode(final K key)
    {
        //We haven't started traversal yet, remove from start nodes
        if(path.isEmpty())
        {
            LinkedNode<K,V> node = nodes.remove(key);
            if(node == null)
                return null;
            return node.getValue();
        }

        LinkedNode<K,V> node = path.peek().removeNextNode(key);
        if(node == null)
            return null;
        return node.getValue();
    }

    /**
     * Removes all the next nodes after this node, or if
     * we've not traversed at all, clear the start nodes.
     */
    public void clearNextNodes()
    {
        if(path.isEmpty())
        {
            this.nodes.clear();
            return;
        }

        path.peek().clearNextNodes();
    }

    /**
     * Steps back one step in the route.
     * Works kinda like UNDO
     * @return Can we step back more
     */
    public boolean unstep()
    {
        if(path.isEmpty())
            return false;

        futurePath.push(path.pop());
        return !path.isEmpty();
    }

    /**
     * Steps forward in path we've already traversed
     * but backstepped.
     * Works kinda like REDO
     * @return Can we restep further
     */
    public boolean restep()
    {
        if(futurePath.isEmpty())
            return false;

        path.push(futurePath.pop());
        return !futurePath.isEmpty();
    }

    /**
     * Checks if this route has no routes
     * @return Is this route empty
     */
    public boolean isEmpty()
    { return this.nodes.isEmpty(); }

    /**
     * Returns the traversed path from start to current node.
     * MAY LOOP LIKE A RING!
     * @return List of key-value pairs in the traversed order
     */
    public ArrayDeque<Entry<K,V>> getCurrentPath()
    {
        ArrayDeque<Entry<K,V>> traversal = new ArrayDeque<Entry<K,V>>();

        for(LinkedNode<K,V> node : this.path)
            traversal.add(new SimpleEntry<K,V>(node.getKey(),node.getValue()));
        return traversal;
    }

    /**
     * Clears the traversed path, returning to start where
     * no moves were made.
     * Restep and unstep cannot be operated after this unless
     * steps are made.
     */
    public void clearPath()
    {
        this.path.clear();
        this.futurePath.clear();
    }

    /**
     * Are there any nodes further where
     * it is possible to traverse to.
     * @return If there are nodes after this node to traverse to
     */
    public boolean hasNextNodes()
    { 
        if(this.path.isEmpty())
            return this.nodes.isEmpty();
        return path.peek().isEmpty(); 
    }

    /**
     * Checks if it is possible to traverse from current position to a node with
     * given key
     * @param key Key to look for
     * @return Is it possible to traverse with given key
     */
    public boolean hasNextNode(final K key)
    {
        if(this.path.isEmpty())
            return this.nodes.containsKey(key);
        if(this.path.peek().isEmpty())
            return false;
        return this.path.peek().hasNextNode(key);
    }

    /**
     * Checks if it is possible to restep (REDO)
     * @return Is it possible to restep unstepped steps
     */
    public boolean canRestep()
    { return !this.futurePath.isEmpty(); }

    /**
     * Checks if it is possible to unstep (UNDO)
     * @return Is it possible to unstep stepped steps
     */
    public boolean canUnstep()
    { return !this.path.isEmpty(); }

    /**
     * Adds a new node after current node or at the start nodes
     * @param key Key for the new node
     * @param value Value for the new node
     * @return True if node was added, False if node already existed
     * and was not added
     */
    public boolean addNextNode(final K key, final V value)
    {
        if(this.hasNextNode(key))
            return false;

        if(this.path.isEmpty())
            this.nodes.put(key, new LinkedNode<K,V>(key, value));
        else
            this.path.peek().addNextNode(new LinkedNode<K,V>(key, value));
        return true;
    }

    /**
     * Puts a new node after current node or start nodes
     * @param key Key for the new node
     * @param value Value for the new node
     */
    public void putNextNode(final K key, final V value)
    {
        if(this.path.isEmpty())
            this.nodes.put(key, new LinkedNode<K,V>(key, value));
        else
            this.path.peek().addNextNode(key, value);
    }

    /*@Override
    public Iterator<Entry<K,V>> iterator() {
        List<Entry<K,V>> allNodes = new ArrayList<Entry<K,V>>();
        Deque<LinkedNode<K,V>> queue = new ArrayDeque<LinkedNode<K,V>>();

        for(Entry<K, LinkedNode<K, V>> entry : this.nodes.entrySet())
        {
            queue.push(entry.getValue()); //Add current start node to queue
            //Add current start node to list of all Nodes
            allNodes.add(new SimpleEntry<K,V>(entry.getValue().getKey(), entry.getValue().getValue()));

            for(LinkedNode<K,V> node : entry.getValue()) //Iterate all children in queue
            {
                Entry<K,V> pair = new SimpleEntry<K,V>(node.getKey(),node.getValue());

                //If we've already counter this node, don't count it
                if(allNodes.contains(pair))
                    continue;
                //queue.push(p);
            }
        }

        return null;
    }*/
}

1 个答案:

答案 0 :(得分:0)

就像@Dylan在评论中说的那样,在LinkedNode中添加了一个简单的标志,以确定该节点是否已被访问,从而防止两次访问同一节点。要获取所有节点,需要进行深度优先搜索,然后清除节点中的标志。