如何加速深度优先搜索方法?

How to speed up Depth First Search method?

我正在尝试对我的图表进行深度优先搜索,但有些东西大大减慢了它的速度,我不确定是什么原因。

这是我的包代码:

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Bag<Item> implements Iterable<Item> {
    private Node<Item> first;    // beginning of bag
    private Node<Item> end;
    private int n;               // number of elements in bag
    public int label;
    public int edges;

    public static class Node<Item> {
        private Item item;                  
        private Node<Item> next;
        public int label;
        public int edges;
    }

    public Bag() {
        first = null;                           // empty bag initialized
        end = null;
        n = 0;
    }
    
    public void add(Item item) {
        if (n==0) {
            Node<Item> head = new Node<Item>();     // if bag is empty
            first = head;
            end = head;
            head.item = item;           // new node both first and end of bag
            edges++;
            n++;
        }
        else {
            Node<Item> oldlast = end;           // old last assigned to end of node
            Node<Item> last = new Node<Item>();
            last.item = item;
            oldlast.next = last;                // new node added after old last
            end = last;
            n++;                                    // size increased
            edges++;
        }
    }

    public Iterator<Item> iterator()  {
        return new LinkedIterator(first);           // returns an iterator that iterates over the items in this bag in arbitrary order
    }


    public class LinkedIterator implements Iterator<Item> {
        private Node<Item> current;

        public LinkedIterator(Node<Item> first) {
            current = first;                                            // iterator starts at head of bag
        }

        public boolean hasNext()  { return current != null;                     }
        public void remove()      { throw new UnsupportedOperationException();  }

        public Item next() {
            if (!hasNext()) throw new NoSuchElementException();             // if there is next item, current is moved to next
            Item item = current.item;
            current = current.next; 
            return item;                                        // item is returned
        }
    }
}

这是我的 driver:

import java.util.ArrayList;
import java.util.Random;

public class Driver {
    
    public static ArrayList<Integer> randomNum(int howMany) {
        ArrayList<Integer> numbers = new ArrayList<Integer>(howMany);   
        Random randomGenerator = new Random();
        while (numbers.size() < howMany) {
            int rand_int = randomGenerator.nextInt(10000);
            if (!numbers.contains(rand_int)) {
                numbers.add(rand_int);
            }
        }
        return numbers;
    }
    
    public static void main(String[] args) {
        ArrayList<Integer> num = randomNum(100);
        Graph G = new Graph(num);
        System.out.println("The length of longest path for this sequence with graph is: " +  G.dfsStart(num));
    }
}

我从 driver 向我的 dfsStart 方法发送一个随机整数的 ArrayList,它会查看图中每个起始节点的所有不同路径。我的 DepthFirstSearch 方法为每个起始节点调用 getAdjList 以使用我的 Bag adj 找到它的邻居,然后在回溯之前沿着每条路径前进。

这是我的 Graph 代码,包含我的最长路径方法:

import java.util.ArrayList;
import java.util.NoSuchElementException;

public class Graph {

    public final int V;                     // initializing variables and data structures
    public Bag<Integer>[] adj;
    public int longestPath;
    
    public Graph(ArrayList<Integer> numbers) {
        
        try {
            longestPath = 0;
            this.V = numbers.size();
            adj = (Bag<Integer>[]) new Bag[V];                      // bag initialized
            for (int v = 0; v < V; v++) {
                adj[v] = new Bag<Integer>();                            
            }
            for (int i = 0; i < V; i++) {
                adj[i].label = numbers.get(i);
                int j = (i + 1);
                while (j < numbers.size()) {
                    if (numbers.get(i) < numbers.get(j)) {
                        addEdge(i, numbers.get(j));
                    }
                    j++;
                }
            }
        }
        catch (NoSuchElementException e) {
            throw new IllegalArgumentException("invalid input format in Graph constructor", e);
        }
    }

    
    public void addEdge(int index, int num) {                                           
        adj[index].add(num);            
    }

    public int getIndex(int num) {
        for (int i = 0; i < adj.length; i++) {
            if (adj[i].label == num) {
                return i;
            }
        }
        return -1;
        
    }
    
    public Bag<Integer> getAdjList(int source) {
        Bag<Integer> adjList = null;
        for (Bag<Integer> list : adj) {
            if (list.label == source) {
                adjList = list;
                break;
            }
        }
        return adjList;
    }
    
    public int dfsStart(ArrayList<Integer> numbers) {
        for (int i=0;i<numbers.size();i++) {
            // Print all paths from current node
            depthFirstSearch(numbers.get(i),new ArrayList<>(300));
        }
        return longestPath;
    }
    
    public void depthFirstSearch(int src, ArrayList<Integer> current) {
        current.add(src);
        Bag<Integer> srcAdj = getAdjList(src);
        if (srcAdj.size() == 0) {
            // Leaf node
            // Print this path
            longestPath = Math.max(longestPath, current.size());
        }
        for (int links : srcAdj) {
            depthFirstSearch(links, current);
        }
        current.remove(current.size()-1);
    }

}

我相信下面的建议有助于消除错误,但在超过 150 个顶点的图中尝试找到最长路径时,它仍然慢得令人难以置信。

即使对于一个小的密集图,也可以有许多来自 src 节点的唯一路径。我测试了这个输入 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]16777216 来自所有节点的唯一路径。所以你可以期待 OOM 更大的输入。一种方法是在找到路径后立即更新 longestPath,而不是将其添加到列表中。

稍后更改。

addtoCount(current.size());

longestPath = Math.max(longestPath, current.size());

确保 longestPathglobal 并在每个测试用例之前初始化为 0

好吧,我不知道 JAVA 但是对于深度优先搜索这样的简单事情来说,代码量实在是太多了。

在 C++ 中,它是这样完成的:

    void cPathFinder::depthFirst(
        int v)
    {
        // initialize visited flag for each node in graph
        myPath.clear();
        myPath.resize(nodeCount(), 0);

        // start recursive search from starting node
        depthRecurse(v, visitor);
    }

    void cPathFinder::depthRecurse(
        int v )
    {

        // remember this node has been visited
        myPath[v] = 1;

        // look for new adjacent nodes
        for (int w : adjacent(v))
            if (!myPath[w])
            {
                // search from new node
                depthRecurse(w);
            }
    }