SetNoneScaleMutableGraph

WebGraph中的图,基本都会在删除结点后,进行向前移动的操作,其实在很多是侯,这是没有必要的,甚至是有副作用的。SetNoneScaleMutableGraph就是我实现的一个没有进行前移操作的类,还没有做深入的测试,理论上来说,基本没有什么问题。

 

废话少说,直接上码:

package cn.edu.dlut.wisdom;
/*		 
 * Copyright (C) 2006-2007 Sebastiano Vigna 
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntAVLTreeSet;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.lang.MutableString;
import it.unimi.dsi.webgraph.ImmutableGraph;
import it.unimi.dsi.webgraph.LazyIntIterator;
import it.unimi.dsi.webgraph.LazyIntIterators;
import it.unimi.dsi.webgraph.NodeIterator;
import it.unimi.dsi.webgraph.Transform;
import java.io.*;
/** A very simple mutable graph class based on {@link it.unimi.dsi.fastutil.ints.IntArrayList}s.
 * 
 * <p>When creating examples for test cases or everyday usage, this class offers practical constructors.
 * For instance, a 3-cycle is easily built as
 * <pre>
 *     new ArrayListMutableGraph( 3, new int[][] { { 0, 1 }, { 1, 2 }, { 2, 0 } } )
 * </pre>
 * 
 * <p>Moreover, methods like {@link #addNodes(int)} and {@link #addArc(int, int)} allow to change
 * the graph structure after construction, and several static factory methods provides ready-made
 * common graphs (see, e.g., {@link #newCompleteBinaryIntree(int)}).
 * 
 * <p>A mutable graph is <em>not</em> an {@link it.unimi.dsi.webgraph.ImmutableGraph}. However,
 * it is possible to obtain an {@linkplain #immutableView() immutable view} of a mutable graph.
 * The view is valid until the exposed mutable graph is modified. A modification counter is used
 * to cause a <em>fail-fast</em> behaviour in case the immutable view is used after modifications.  
 * 
 * <p><strong>Warning</strong>: obtaining a {@link it.unimi.dsi.webgraph.NodeIterator} and using it
 * while modifying the graph will lead to unpredictable results.
 */
public class SetNoneScaleMutableGraph extends ImmutableGraph implements Serializable {
    /** Current number of nodes. */
    protected int n;
    /** Current number of arcs. */
    protected long m;
    /** Current list of successor lists. The backing array might be longer than {@link #n}. */
    protected Int2ObjectOpenHashMap<IntAVLTreeSet> successors;
    /** Guarantees that a node index is valid.
     *
     * @param x a node index.
     */
    protected void ensureNode(final int x) {
        if (!successors.containsKey(x)) {
            throw new IllegalArgumentException("No node " + x);
        }
    }
    /** Creates a new empty mutable graph. */
    public SetNoneScaleMutableGraph() {
        successors = new Int2ObjectOpenHashMap<IntAVLTreeSet>();
    }
    /** Creates a new mutable graph using a given number of nodes and a given list of arcs.
     *
     * @param numNodes the number of nodes in the graph.
     * @param arc an array of arrays of length 2, specifying the arcs; no sanity checks are performed..
     */
    public SetNoneScaleMutableGraph(final int numNodes, final int[][] arc) {
        n = numNodes;
        m = arc.length;
        // Sanitize
        for (int i = arc.length; i-- != 0;) {
            if (arc[i].length != 2) {
                throw new IllegalArgumentException("The arc of index " + i + " has length " + arc[i].length);
            }
            if (arc[i][ 0] < 0 || arc[i][ 1] < 0 || arc[i][ 0] >= numNodes || arc[i][ 1] >= numNodes) {
                throw new IllegalArgumentException("The arc of index " + i + " (" + arc[i][ 0] + ", " + arc[i][ 1] + ") is illegal");
            }
        }
        successors = new Int2ObjectOpenHashMap<IntAVLTreeSet>();
        for (int i = 0; i < n; i++) {
            successors.put(i, new IntAVLTreeSet());
        }
        for (int i = 0; i < arc.length; i++) {
            successors.get(arc[i][ 0]).add(arc[i][ 1]);
        }
    }
    public SetNoneScaleMutableGraph(final int numNodes) {
        this(numNodes, new int[][]{});
    }
    /** Creates a new mutable graph copying a given immutable graph.
     *
     * @param g an immutable graph.
     */
    public SetNoneScaleMutableGraph(final ImmutableGraph g) {
        successors = new Int2ObjectOpenHashMap<IntAVLTreeSet>();
        int d, s = -1;
        long numArcs = 0;
        for (NodeIterator nodeIterator = g.nodeIterator(); nodeIterator.hasNext();) {
            s = nodeIterator.nextInt();
            d = nodeIterator.outdegree();
            numArcs += d;
            successors.put(s, new IntAVLTreeSet(nodeIterator.successorArray(), 0, d));
        }
        n = s + 1;
        m = numArcs;
    }
    /** Creates a new mutable graph using a given number of nodes and a given arc filter.
     *
     * @param numNodes the number of nodes in the graph.
     * @param arcFilter an arc filter which will specify which arcs go into the graph.
     */
    public SetNoneScaleMutableGraph(final int numNodes, final Transform.ArcFilter arcFilter) {
        n = numNodes;
        successors = new Int2ObjectOpenHashMap<IntAVLTreeSet>();
        for (int i = n; i-- != 0;) {
            successors.put(i, new IntAVLTreeSet());
            for (int j = 0; j < n; j++) {
                if (arcFilter.accept(i, j)) {
                    successors.get(i).add(j);
                    m++;
                }
            }
        }
    }
    /** Returns a new mutable graph containing a directed cycle.
     *
     * @param numNodes the number of nodes in the cycle.
     */
    public static SetNoneScaleMutableGraph newDirectedCycle(final int numNodes) {
        return new SetNoneScaleMutableGraph(numNodes, new Transform.ArcFilter() {
            public boolean accept(final int i, final int j) {
                return (i + 1) % numNodes == j;
            }
        });
    }
    /** Returns a new mutable graph containing a bidirectional cycle.
     *
     * @param numNodes the number of nodes in the cycle.
     */
    public static SetNoneScaleMutableGraph newBidirectionalCycle(final int numNodes) {
        return new SetNoneScaleMutableGraph(numNodes, new Transform.ArcFilter() {
            public boolean accept(final int i, final int j) {
                return (i + 1) % numNodes == j || (j + 1) % numNodes == i;
            }
        });
    }
    /** Returns a new mutable graph containing a complete graph.
     *
     * @param numNodes the number of nodes in the graph.
     * @param loops true if you want loops, too.
     */
    public static SetNoneScaleMutableGraph newCompleteGraph(final int numNodes, final boolean loops) {
        return new SetNoneScaleMutableGraph(numNodes, new Transform.ArcFilter() {
            public boolean accept(final int i, final int j) {
                return i != j || loops;
            }
        });
    }
    /** Returns a new mutable graph containing a complete binary in-tree of given height.
     *
     * <strong>Warning</strong>: starting from version 1.7, the spurious loop
     * at the root has been removed.
     *
     * @param height the height of the tree (0 for the root only).
     */
    public static SetNoneScaleMutableGraph newCompleteBinaryIntree(final int height) {
        return new SetNoneScaleMutableGraph((1 << (height + 1)) - 1, new Transform.ArcFilter() {
            public boolean accept(final int i, final int j) {
                return i != j && i / 2 == j;
            }
        });
    }
    /** Returns a new mutable graph containing a complete binary out-tree of given height.
     *
     * <strong>Warning</strong>: starting from version 1.7, the spurious loop
     * at the root has been removed.
     *
     * @param height the height of the tree (0 for the root only).
     */
    public static SetNoneScaleMutableGraph newCompleteBinaryOuttree(final int height) {
        return new SetNoneScaleMutableGraph((1 << (height + 1)) - 1, new Transform.ArcFilter() {
            public boolean accept(final int i, final int j) {
                return i != j && j / 2 == i;
            }
        });
    }
    @Override
    public int numNodes() {
        return n;
    }
    @Override
    public int outdegree(final int x) {
        ensureNode(x);
        return successors.get(x).size();
    }
    @Override
    public long numArcs() {
        return m;
    }
    @Override
    public int[] successorArray(final int x) {
        ensureNode(x);
        return successors.get(x).toIntArray();
    }
    @Override
    public LazyIntIterator successors(final int x) {
        ensureNode(x);
        return LazyIntIterators.lazy(successors.get(x).iterator());
    }
    @Override
    public NodeIterator nodeIterator() {
        final IntIterator it = successors.keySet().iterator();
        return new NodeIterator() {
            int x;
            @Override
            public int outdegree() {
                throw new UnsupportedOperationException("Not supported yet.");
            }
            @Override
            public LazyIntIterator successors() {
                return LazyIntIterators.lazy(successors.get(x).iterator());
            }
            @Override
            public int[] successorArray() {
                return successors.get(x).toIntArray();
            }
            @Override
            public boolean hasNext() {
                return it.hasNext();
            }
            @Override
            public Integer next() {
                return x = it.next();
            }
            @Override
            public int nextInt() {
                return x = it.nextInt();
            }
            @Override
            @SuppressWarnings("empty-statement")
            public int skip(int n) {
                int i;
                for (i = 0; i < n && hasNext(); i++, next());
                return i;
            }
            @Override
            public void remove() throws UnsupportedOperationException {
                SetNoneScaleMutableGraph.this.removeNode(x);
            }
        };
    }
    /** Adds the given number of nodes, numbering them from {@link #numNodes()} onwards. The new nodes have no successors.
     *
     * @param numNewNodes the number of new nodes.
     */
    public void addNodes(final int x) {
        ensureNoneNode(x);
        successors.put(x, new IntAVLTreeSet());
        n++;
    }
    public void ensureNoneNode(final int x) {
        if (successors.containsKey(x)) {
            throw new IllegalArgumentException("Node " + x + "is already in the Graph");
        }
    }
    /** Removes the given node. All arcs incident on the node are removed, too.
     *
     * @param x the node to be removed.
     */
    public void removeNode(final int x) {
        ensureNode(x);
        m -= successors.get(x).size();
        successors.remove(x);
        n--;
        for (IntAVLTreeSet i : successors.values()) {
            if (i.contains(x)) {
                i.remove(x);
                m--;
            }
        }
    }
    /** Adds the given arc.
     *
     * @param x the start of the arc.
     * @param y the end of the arc.
     */
    public void addArc(final int x, final int y) {
        ensureNode(x);
        ensureNode(y);
        if (successors.get(x).contains(y)) {
            return;
        }
        successors.get(x).add(y);
        m++;
    }
    /** Removes the given arc.
     *
     * @param x the start of the arc.
     * @param y the end of the arc.
     */
    public void removeArc(final int x, final int y) {
        ensureNode(x);
        ensureNode(y);
        if (!successors.get(x).contains(y)) {
            return;
        }
        successors.get(x).remove(y);
        m--;
    }
    /** Compare this mutable graph to another object.
     *
     * @return true iff the given object is a mutable graph the same size, and
     * the successor list of every node of this graph is equal to the successor list of the corresponding node of <code>o</code>.
     */
    @Override
    public boolean equals(final Object o) {
        if (!(o instanceof SetNoneScaleMutableGraph)) {
            return false;
        }
        final SetNoneScaleMutableGraph g = (SetNoneScaleMutableGraph) o;
        int n = numNodes();
        if (n != g.numNodes()) {
            return false;
        }
        int[] s, t;
        int d;
        while (n-- != 0) {
            if ((d = outdegree(n)) != g.outdegree(n)) {
                return false;
            }
            s = successorArray(n);
            t = g.successorArray(n);
            while (d-- != 0) {
                if (s[d] != t[d]) {
                    return false;
                }
            }
        }
        return true;
    }
    /** Returns a hash code for this mutable graph.
     *
     * @return a hash code for this mutable graph.
     */
    @Override
    public int hashCode() {
        int n = numNodes(), h = -1;
        int[] s;
        int d;
        for (int i = 0; i < n; i++) {
            h = h * 31 + i;
            s = successorArray(i);
            d = outdegree(i);
            while (d-- != 0) {
                h = h * 31 + s[d];
            }
        }
        return h;
    }
    @Override
    public String toString() {
        MutableString ms = new MutableString();
        IntIterator ii;
        ms.append("Nodes: " + numNodes() + "
Arcs: " + numArcs() + "
");
        for (int i = 0; i < numNodes(); i++) {
            ms.append("Successors of " + i + " (degree " + outdegree(i) + "):");
            ii = successors.get(i).iterator();
            while (ii.hasNext()) {
                ms.append(" " + ii.nextInt());
            }
            ms.append("
");
        }
        return ms.toString();
    }
    @Override
    public ImmutableGraph copy() {
        return this;
    }
    @Override
    public boolean randomAccess() {
        return true;
    }
    /**
     * @param basename end with .sg
     * @return
     * @throws IOException
     */
    public static SetNoneScaleMutableGraph load(CharSequence basename)
            throws IOException {
        ObjectInputStream oi = new ObjectInputStream(new FileInputStream(basename + ".sg"));
        SetNoneScaleMutableGraph sg;
        try {
            sg = (SetNoneScaleMutableGraph) oi.readObject();
        } catch (ClassNotFoundException ex) {
            sg = null;
        }
        oi.close();
        return sg;
    }
    public static void store(SetNoneScaleMutableGraph sg, CharSequence basename) throws IOException {
        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(basename + ".sg"));
        oo.writeObject(sg);
        oo.flush();
        oo.close();
    }
}

posted on 2010-04-06 06:20  小橋流水  阅读(253)  评论(0编辑  收藏  举报

导航