Cod sursa(job #2796519)

Utilizator waren4Marius Radu waren4 Data 8 noiembrie 2021 12:43:48
Problema Algoritmul lui Dijkstra Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 35.01 kb
#include<iostream>
#include<fstream>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>

using namespace std;

struct Edge {                                       // Auxiliary struct for holding edge information
        int source, destination, cost;          
        Edge(int source, int dest, int cost = 0): source(source), destination(dest), cost(cost){}
        Edge Flip() {return Edge(destination, source, cost);}   // Returns edge with source and destination swapped
};

template <typename T>
class Heap {  

public:
 
    T Root();
    T Extract();
    void Pop();
    void Insert(T value);
    int Remove(T value);
    void Build(vector<T> values);
    vector<T> ExtractAll();

    bool Empty() {
        return tree.empty();
    }
    void Clear() {
        tree.clear();
    }

    Heap(bool maxHeap = false) {
        this->maxHeap = maxHeap;
    }

    Heap(vector<T> values, bool maxheap = false) {

        for(auto value : values) {
            Insert(value);
        }
    }

    int GetNumberOfNodes() {
        return tree.size();
    }

private:

    bool maxHeap;
    vector<T> tree;

    int Parent(int node);
    int LeftChild(int node);
    int RightChild(int node);
    void Descend(int node);
    void Ascend(int node);
    void Delete(int node);
    void SwapNodes(int node1, int node2);
    
    
    template<typename T0> static T0 Opposite(T0 x) {
        return -x;
    };
    static string Opposite(string x) {
        string newString = x;
        for(int i = 0; i < x.size(); ++i) {
            newString[i] = Opposite<char>(x[i]);
        }
        return newString;
    };
    template<typename T1, class T2> static pair<T1, T2> Opposite(pair<T1, T2> x)  {
        return make_pair(Opposite(x.first), x.second);
    };

};

#pragma region HeapPublicMethods

template<typename T>
T Heap<T>::Root() {  // Returns minimum/maximum element 

    if(maxHeap) {
        return Opposite(tree[0]);
    }
    else {
        return tree[0];
    }
}

template<typename T>
void Heap<T>::Insert(T value) { // Inserts element into heap

    T newValue = value;
    if(maxHeap) newValue = Opposite(value);

    tree.push_back(newValue);
    Ascend(tree.size() - 1);
}

template<typename T>
void Heap<T>::Pop() {  // Removes minimum/maximum element from the heap
    Delete(0);
}

template<typename T>
T Heap<T>::Extract() {  // Removes minimum/maximum element from the heap and returns it
    T root = Root();
    Delete(0);
    return root;
}

template<typename T>
int Heap<T>::Remove(T value) {   // Removes a given element from the heap, returns 0 if element was not found, returns 1 on succesful removal

    bool found = false;

    for(int i = 0; i < tree.size(); ++i) {
        if(tree[i] == value) {
            found = true;
            Delete(i);
            break;
        }
    }

    return found? 1 : 0;
}

template<typename T>
void Heap<T>::Build(vector<T> values) {   // Inserts all elements from values into the heap
    for(auto value : values) {
        Insert(value);
    }
}

template<typename T>
vector<T> Heap<T>::ExtractAll() {      // Returns a sorted vector of all elements

    vector<T> sortedVector;

    while(!Empty()) {
        sortedVector.push_back(Extract());
    }

    return sortedVector;
}

#pragma endregion

#pragma region HeapPrivateMethods

template<typename T>
void Heap<T>::Descend(int node) {

    T minimum = tree[node];
    int left = LeftChild(node);
    int right = RightChild(node);
    int minIndex = node;

    if(left) {
        if(minimum > tree[left]) {
            minIndex = left;
            minimum = tree[left];
        }
    }

    if(right) {
        if(minimum > tree[right]) {
            minIndex = right;
        }
    }

    if(node != minIndex) {
        SwapNodes(node, minIndex);
        Descend(minIndex);
    }
}    

template<typename T>
void Heap<T>::Ascend(int node) {

    int parent = Parent(node);

    if(tree[parent] > tree[node]) {
        SwapNodes(node, parent);
        Ascend(parent);
    }
}

template<typename T>
void Heap<T>::Delete(int node) {
    
    SwapNodes(node, tree.size()-1);
    tree.pop_back();
    Descend(node);
}

template<typename T>
void Heap<T>::SwapNodes(int node1, int node2) {

    T swap = tree[node1];
    tree[node1] = tree[node2];
    tree[node2] = swap;
}

template<typename T>
int Heap<T>::Parent(int node) {

    if(node > 0) {
        return (node - 1) / 2;
    }
    else return 0;
}

template<typename T>
int Heap<T>::LeftChild(int node) {
    int left = node * 2 + 1;

    if( left < tree.size()) {
        return left;
    }
    else return 0;
}

template<typename T>
int Heap<T>::RightChild(int node) {
    int right = node * 2 + 2;

    if( right < tree.size()) {
        return right;
    }
    else return 0;
}

#pragma endregion

struct DisjointSets {    // Struct used as helper for creating and manipulating disjoint sets of nodes

    int Root(int node);
    void Union(int node1, int node2);

    DisjointSets(int numberOfNodes): numberOfNodes(numberOfNodes) {

        for(int node = 0; node < numberOfNodes; ++node) {
            rank.push_back(1);
            parent.push_back(node);
        }
    }

    private:

        int numberOfNodes;

        vector<int> rank;           // Holds the rank of the set of each node, for optimizing unions
        vector<int> parent;         // Either the direct parent's index or the root node index 

};

#pragma region DisjointSetsPublicMethods

int DisjointSets::Root(int node) {        // Returns the root of the node's subtree

    int root = node;

    while(parent[root] != root) {
        root = parent[root];        // Go up through the set until its root is found
    }


    while(node != parent[node]) {   // Update parent[node] to the set's root, for all nodes up to the root
        int swap = parent[node];
        parent[node] = root;
        node = swap; 
    }

    return root;
}

void DisjointSets::Union(int node1, int node2) {  // Merges the two subtrees by connecting one's root to the other's
    node1 = Root(node1);            // Get the roots 
    node2 = Root(node2);

    if (rank[node1] > rank[node2]) {    // Link roots, smaller rank gets attached to higher rank 
        parent[node2] = node1;
    }
    else {
        parent[node1] = node2;
    }
    if(rank[node1] == rank[node2]) {
        ++rank[node2];              // Increase rank of new set if sets were equal 
    }
}

#pragma endregion

class Graph {
public:

        int TotalCost();
        int NumberOfComponents();
        vector<Edge> GetAllEdges();
        vector<int> UnweightedDistances(int startIndex = 0);
        vector<int> WeightedDistances(int startIndex = 0);
        vector<int> TopologicalSort();
        vector<vector <int>> StronglyConnectedComponents();
        vector<vector <int>> BiconnectedComponents();
        vector<Edge> CriticalConnections();

        Graph MinimumSpanningTree();
        Graph DFSTree(int startIndex);
        Graph BFSTree(int startIndex);
        Graph DFSTrees();
        
        void BuildFromAdjacencyList(istream& inputStream);
        void BuildFromAdjacencyMatrix(istream& inputStream);

        void AddEdge(int source, int dest, int cost = 0);
        void AddEdge(Edge newEdge);
        void AddNode(int value = 0);

        static bool CheckHavelHakimi(vector<int>& degrees);

        

#pragma region GraphConstructors

        Graph(bool directed = false, bool weighted = false) {

            numberOfNodes = 0;
            numberOfEdges = 0;
            this->directed = directed;
            this->weighted = weighted; 
        }

        Graph(int numberOfNodes, int numberOfEdges, bool directed = false, bool weighted = false) {
            
            this->numberOfEdges = numberOfEdges;
            this->directed = directed;
            this->weighted = weighted; 

            for(int i = 0; i < numberOfNodes; ++i) {
                
                AddNode();
            }

        }

#pragma endregion

#pragma region GraphGetSet

        void SetValue(int node, int value) {

            try {
                values[node] = value;
            }
            catch(...) {
                throw "Node doesn't exist";
            }
        }

        int GetValue(int node) {
            
            try {
                return values[node];
            }
            catch(...) {
                throw "Node doesn't exist";
            }
            
        }

        void SetDirected(bool directed) {
            this->directed = directed;
        }

        bool IsDirected() {
            return directed;
        }

        void SetWeighted(bool weighted) {
            this->weighted = weighted;
        }

        bool IsWeighted() {
            return weighted;
        }

        void SetNumberOfNodes(int number) {
            numberOfNodes = number;
        }
        
        int GetNumberOfNodes() {
            return numberOfNodes;
        }

        void SetNumberOfEdges(int number) {
            numberOfEdges = number;
        }

        int GetNumberOfEdges() {
            return numberOfEdges;
        }

#pragma endregion

    private:

        bool directed;
        bool weighted;

        int numberOfNodes;
        int numberOfEdges;

        vector < int > values;                              // The value of each node 
        vector < vector < Edge > > adjacencyList;           // adjacencyList[X] holds the information of each edge that X has

        
        void Dijkstra(vector<bool>& visitedNodes, vector<int>& distances, Heap< pair<int, int>>& heap, int startIndex = 0);        
        void BFS(vector<int>& visitedNodes, vector<int>& distances, int startIndex = 0);
        void TreeBuilderBFS(int startIndex, Graph& treeGraph, vector<bool>& visitedNodes);
        void DFS(vector<int>& visitedNodes, int marker = 1, int nodeIndex = 0);
        void StronglyConnectedDFS(int currentNode, vector<vector<int>>& scc, vector<int>& order, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& onStack, int& counter);
        void BiconnectedDFS(int currentNode, int parent, int currentDepth, vector< vector <int>>& bcc, vector<int>& depth, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& visited);
        void CriticalEdgesDFS(int currentNode, int parent, vector <Edge>& cc, vector<int>& depth, vector<int>& lowest, int& counter);
        void TopologicalDFS(int currentNode, stack<int>& orderStack, vector<bool>& visitedNodes);
        void TreeBuilderDFS(int currentNode, int treeCurrentNode, Graph& treeGraph, vector<bool>& visitedNodes);

        static bool CompareEdges(Edge x, Edge y);

    
};

#pragma region GraphPublicMethods

void Graph::AddEdge(int source, int dest, int cost /*= 0*/) {

    Edge newEdge(source, dest, cost);

    adjacencyList[source].push_back(newEdge);

    if(!this->directed) {

        adjacencyList[dest].push_back(newEdge.Flip());
    }

    ++numberOfEdges;
}

void Graph::AddEdge(Edge newEdge) {

    adjacencyList[newEdge.source].push_back(newEdge);

    if(!this->directed) {

        adjacencyList[newEdge.destination].push_back(newEdge.Flip());
    }

    ++numberOfEdges;
}

void Graph::AddNode(int value /*= 0*/) {

    vector<Edge> tempVector;
    adjacencyList.push_back(tempVector);

    values.push_back(value);

    ++numberOfNodes;
}

vector<Edge> Graph::GetAllEdges() {

    vector<Edge> edges;

    if (directed) {
        for(int node = 0; node < numberOfNodes; ++node) {
            for(auto edge : adjacencyList[node]) {
                edges.push_back(edge);            
            }
        }
    }
    else {
    
        vector<bool> visited;                   // Used for not returning duplicate edges in undirected graphs

        for(int i = 0; i < numberOfNodes; ++i) {
            visited.push_back(false);
        }

        for(int node = 0; node < numberOfNodes; ++node) {
            for(auto edge : adjacencyList[node]) {
                if (!visited[edge.destination]) {
                    edges.push_back(edge);
                }
            }
        
        visited[node] = true;

        }
    }

    return edges;
}

Graph Graph::MinimumSpanningTree() {
                                                // Returns a graph that is a minimum spanning tree of this graph, using Kruskal's algorithm and disjoint sets

    Graph treeGraph(numberOfNodes, 0, directed, weighted);  // Initialize graph to be returned
                                                            // Has 0 edges currently because numberOfEdges will be incremented by AddEdge()
    vector<Edge> sortedEdges = GetAllEdges();
    sort(sortedEdges.begin(), sortedEdges.end(), CompareEdges);

    DisjointSets sets(numberOfNodes);           // Used for keeping track of the new trees components

    for(int i = 0; i < numberOfEdges; ++i) {

        if( sets.Root(sortedEdges[i].source) != sets.Root(sortedEdges[i].destination) )  {       // Check if nodes are in separate components

            sets.Union(sortedEdges[i].source, sortedEdges[i].destination);
            treeGraph.AddEdge(sortedEdges[i]);
        }
    }

    return treeGraph;
}

int Graph::TotalCost() {
                            // Returns the summed cost of all edges in graph
    int totalCost = 0;

    vector<Edge> edges = GetAllEdges();

    for(auto edge : edges) {
        
        totalCost += edge.cost;
    }

    return totalCost;
}

Graph Graph::DFSTree(int startIndex) {
                                            // Returns a graph object of the DFS tree of node with startIndex index
                                            // Node of index startIndex will be the node of index 0 in the new graph
    Graph treeGraph(1, 0, directed, weighted);        // Initialize empty graph with one node
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    TreeBuilderDFS(startIndex, 0, treeGraph, visitedNodes);
    
    return treeGraph;
}

Graph Graph::DFSTrees() {
                                            // Returns a graph object of the DFS tree of node with startIndex index
                                            // Node of index startIndex will be the node of index 0 in the new graph
    Graph treeGraph(0, 0, directed, weighted);        // Initialize empty graph
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    for(int node = 0; node < numberOfNodes; ++node) {
        
        if(!visitedNodes[node]) {

            treeGraph.AddNode();
            TreeBuilderDFS(node, treeGraph.numberOfNodes-1, treeGraph, visitedNodes);
        }
    }

    return treeGraph;
}

Graph Graph::BFSTree(int startIndex) {

    Graph treeGraph(0, 0, directed, weighted);
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    TreeBuilderBFS(startIndex, treeGraph, visitedNodes);

    return treeGraph;
}

vector<int> Graph::TopologicalSort() {
                                            // Returns the list of nodes in topologically sorted order
                                            // Should only be applied on directed graphs
    vector<int> sortedNodes;

    stack<int> orderStack;                  // By using a stack, the elements in pop order will form the topologically sorted list
    vector<int> depth;
    vector<bool> visited;

    for(int i = 0; i < numberOfNodes; ++i) {

        visited.push_back(false);    
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        if(!visited[i]) { 
            TopologicalDFS(i, orderStack, visited);
        }
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        sortedNodes.push_back(orderStack.top());
        orderStack.pop();
    }

    return sortedNodes;

}

bool Graph::CheckHavelHakimi(vector<int>& degrees) {
                                                                            // Receives a list of node degrees and returns true if a corelated graph can exist through the Havel-Hakimi algorithm
    int numberOfNodes = degrees.size();

    int sum = 0; 

    for(int degree : degrees) {

        sum += degree;

        if (degree >= numberOfNodes) {
                                    // If degree is > n-1 then there are not enough nodes, therefore a graph doesn't exist
            return false; 
        }
    }

    if(sum % 2) {
                            // If sum of degrees is odd then a graph doesn't exist
        return false; 
    }

    sort(degrees.begin(), degrees.end());           // Sort vector of degrees, then iterate through it in descending order

    --numberOfNodes; 

    while(sum > 0) {

        int currentNode = degrees[numberOfNodes];

        for(int i = numberOfNodes; i > numberOfNodes - currentNode - 1; --i ) {
            
            --degrees[i];                       // Decrement the previous max(degrees) values
            if(degrees[i] < 0) {
                                               // If any number in the vector falls below 0, then a graph doesn't exist
                return false;
            }
        }

        --numberOfNodes;        // "Eliminating" the node we resolved the degrees of
        sum -= 2*currentNode;
       
        degrees.pop_back();
        sort(degrees.begin(), degrees.end());   // Bubble sort might be faster here
    }
    
    return true;            // If this point is reached then the vector is now [0, 0, .. 0] and a graph exists for the given set of degrees          
}

vector<Edge> Graph::CriticalConnections() {
                                                         // Tested on leetcode(Critical Connections problem)  
    vector<Edge> cc;                    // The list of critical edges

    vector<int> order;                  // Node X is the order[x]-th node to be found during DFS
    vector<int> lowest;                 // lowest[X] is the minimum order[Y], where node Y is connected to node X
     
    int counter = 0;

    for(int i = 0; i < numberOfNodes; ++i) {

        order.push_back(-1);            // -1 order means node hasn't been visited yet
        lowest.push_back(-1);
    }

    CriticalEdgesDFS(0, -1, cc, order, lowest, counter);  // Root node has no parent

    return cc;
} 

vector<int> Graph::UnweightedDistances(int startIndex /*= 0*/) {
                                                                        // Wrapper Method for calculating unweighted distances to each node form startIndex through BFS
                                                                        // Saves distances in distances parameter(distances vector should be empty when calling this method)
    
    vector<int> visited;
    vector<int> distances;

    
    for(int i = 0; i < numberOfNodes; ++i) {
        visited.push_back(0);
        distances.push_back(-1);                // -1 means there is no path to a certain node
    }

    BFS(visited, distances, startIndex);

    return distances;
}

vector<int> Graph::WeightedDistances(int startIndex /*= 0*/) {
                                                // Computes the weighted distances from node of index startIndex to all other nodes
                                                // Wrapper method for Dijkstra's algorithm
    vector<int> distances;
    vector<bool> visited;

    Heap< pair<int, int> > heap; 
    // The heap will contain pairs of (distance, node), where distance is the current total distance to node
    

    int infinity = 2147483647;      // Used for initializing distance vector

    for(int i = 0; i < numberOfNodes; ++i) {

        if(i == startIndex) {
            distances.push_back(0);     
        }
        else {
            distances.push_back(infinity);
        }

        visited.push_back(false);
    }

    Dijkstra(visited, distances, heap, startIndex);

    return distances;
}

int Graph::NumberOfComponents() {
                                        // Computes number of components in undirected graph 
    int numberOfComponents = 0;

    vector<int> visited;
    for(int i = 0; i < numberOfNodes; ++i) {
        visited.push_back(0);
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        if (!visited[i]) {
            ++numberOfComponents;
            DFS(visited, numberOfComponents, i);
        }
    }

    return numberOfComponents;
}

vector< vector <int> > Graph::BiconnectedComponents() {
                                                                    // Returns the list of biconnected components in graph

    vector< vector<int> > bcc;      // The list of BCCs to be returned

    stack<int> nodeStack;           // The stack of visited nodes
    
    vector<int> depth;              // Holds the depth of each node in the DFS tree
    vector<int> lowest;             // Holds the lowest depth a node can reach through its descendants
    vector<bool> visited;           // For marking visited nodes

    for(int i = 0; i < numberOfNodes; ++i) {
        depth.push_back(-1);        // Initialization
        lowest.push_back(-1);   
        visited.push_back(false);
    }

    BiconnectedDFS(0, -1, 0, bcc, depth, lowest, nodeStack, visited);       // Root node (0) has no parent

    return bcc;
}

vector< vector <int> > Graph::StronglyConnectedComponents() {
                                                                    // Computes the number of strongly connected components in directed graph through Tarjan's algorithm
    vector< vector <int> > scc;         // The list of strongly connected components to be returned

    stack<int> nodeStack;               // Stack to be used in tarjan's algorithm

    vector<int> order;                  // Node X is the order[x]-th node to be found during DFS
    vector<int> lowest;                 // lowest[X] is the minimum order[Y], where node Y is connected to node X
    vector<bool> onStack;               // onStack[X] is true if node X is currently on the stack 
    
    int counter = 0;                    // Counter for the order vector

    for(int i = 0; i < numberOfNodes; ++i) {
        order.push_back(-1);            // -1 means node hasn't been visited
        lowest.push_back(-1);           
        onStack.push_back(false);
    }


    for(int node = 0; node < numberOfNodes; ++node) {

        if (order[node] < 0) {

            StronglyConnectedDFS(node, scc, order, lowest, nodeStack, onStack, counter);
        }
    }

    return scc;
}

void Graph::BuildFromAdjacencyMatrix(istream& inputStream) {        // Sets edges between nodes by reading an adjacency matrix from inputStream
                                                                    // Should only be used if numberOfEdges has already been set 

    int matrixValue;             // If matrixValue is 0, there is no edge, if matrixValue != 0, then there is an edge of cost matrixValue

    for(int i = 0; i < numberOfNodes; ++i) {
        for (int j = 0; j < numberOfNodes; ++j) {

            inputStream >> matrixValue;

            if(matrixValue) {
                
                if(!weighted) {
                    matrixValue = 0;
                }

                Edge newEdge(i, j, matrixValue);

                adjacencyList[i].push_back( newEdge );

                if(!directed) {    
                    adjacencyList[j].push_back( newEdge.Flip() );
                }
               
            }
        } 
    }
}

void Graph::BuildFromAdjacencyList(istream& inputStream) {          // Sets edges between nodes by reading adjancency list pairs from inputStream
                                                                    // Should only be used if numberOfEdges has already been set 
                                                                    
    int node1, node2, cost;

    for(int i = 0; i < numberOfEdges; ++i) {

        inputStream >> node1 >> node2;
 
        if(weighted) {
            inputStream >> cost;
        }
        else {
            cost = 0;
        }
        
        Edge newEdge(node1, node2, cost);

        adjacencyList[node1].push_back( newEdge );
      
        if(!directed) {
                adjacencyList[node2].push_back( newEdge.Flip() );
        }
    }   
}


#pragma endregion

#pragma region GraphPrivateMethods

bool Graph::CompareEdges(Edge x, Edge y) {
    return (x.cost < y.cost); 
}

void Graph::Dijkstra(vector<bool>& visitedNodes, vector<int>& distances, Heap < pair<int, int> >& heap, int startIndex /*= 0*/) {

    heap.Insert(make_pair(0, startIndex));    // Push starting node

    while(!heap.Empty()) {

        int currentDistance = heap.Root().first;
        int currentNode = heap.Root().second;

        visitedNodes[currentNode] = true;
        heap.Pop();

        if(distances[currentNode] < currentDistance) {  // If the current minimum distance is smaller than the one in the current pair, 
                                                        // then there is no point in processing the pair further, 
            continue;                                   // because it can't lead to shorter paths
        }

        for(auto edge : adjacencyList[currentNode]) {

            if(!visitedNodes[edge.destination]) {       // If we have already processed a node, then the minimum distance to it has already been calculated

                if(distances[edge.destination] > currentDistance + edge.cost) {
                    
                    distances[edge.destination] = currentDistance + edge.cost;
                    heap.Insert(make_pair(distances[edge.destination], edge.destination));
                }
            }
        }
    }
}   

void Graph::TreeBuilderBFS(int startIndex, Graph& treeGraph, vector<bool>& visitedNodes) {
                                                                        // BFS that also builds a new graph(the BFS tree)
    queue<int> nodeQueue;
    treeGraph.AddNode();
    int treeCurrentNode = 0;        // Index of the current node in the new graph

    nodeQueue.push(startIndex);

    while(!nodeQueue.empty()) {

        int topNode = nodeQueue.front();

        visitedNodes[topNode] = true;

        for (auto edge : adjacencyList[topNode]) {

            int neighbor = edge.destination;
             
            if(!visitedNodes[neighbor]) {

                treeGraph.AddNode();
                treeGraph.AddEdge(treeCurrentNode, treeGraph.numberOfNodes-1);

                nodeQueue.push(neighbor);
                visitedNodes[neighbor] = true;
            }
        }

        treeCurrentNode += 1;       // The next node the search will process has this index in the tree
        nodeQueue.pop();
    }

}

void Graph::TreeBuilderDFS(int currentNode, int treeCurrentNode, Graph& treeGraph, vector<bool>& visitedNodes) {
                                                                        // DFS that also builds a new graph(the DFS tree)
    visitedNodes[currentNode] = true;

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if(!visitedNodes[child]) {

            treeGraph.AddNode();
            treeGraph.AddEdge(treeCurrentNode, treeGraph.numberOfNodes-1);

            TreeBuilderDFS(child, treeGraph.numberOfNodes-1, treeGraph, visitedNodes);
        }
    }
}

void Graph::BFS(vector<int>& visitedNodes, vector<int>& distances, int startIndex /*= 0*/) {
                                                                        // Breadth-first search that sets visited nodes and distance to each node from node with index startIndex
    queue<int> nodeQueue;           // Queue of nodes that haven't had their edges processed yet

    nodeQueue.push(startIndex);
    distances[startIndex] = 0;      // Distance to starting node

    while(!nodeQueue.empty()) {

        int topNode = nodeQueue.front();

        visitedNodes[topNode] = 1;

        for(auto edge : adjacencyList[topNode]) {

            int neighbor = edge.destination;

            if(!visitedNodes[neighbor]) {
                
                nodeQueue.push(neighbor);
                distances[neighbor] = 1 + distances[topNode];
                visitedNodes[neighbor] = 1;
            }
        }

        nodeQueue.pop();
    }

}

void Graph::DFS(vector<int>& visitedNodes, int marker /*= 1*/, int nodeIndex /*= 0*/) {
                                                                                        // Recursive depth-first search, sets visited positions in visitedNodes with marker for counting components
    visitedNodes[nodeIndex] = marker;

    for(auto edge : adjacencyList[nodeIndex]) {

        int neighbor = edge.destination;

        if(!visitedNodes[neighbor]) {
            DFS(visitedNodes, marker, neighbor);
        }
    }

}

void Graph::StronglyConnectedDFS(int currentNode, vector<vector<int>>& scc, vector<int>& order, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& onStack, int& counter) {

    order[currentNode] = counter;
    lowest[currentNode] = counter;

    ++counter;

    nodeStack.push(currentNode);
    onStack[currentNode] = true;


    for(auto edge : adjacencyList[currentNode]) {

        int neighbor = edge.destination;

        if(order[neighbor] < 0) {           // If node hasn't been visited yet

            StronglyConnectedDFS(neighbor, scc, order, lowest, nodeStack, onStack, counter);

            lowest[currentNode] = min(lowest[currentNode], lowest[neighbor]);               // Set the SCC index of each node on the recursion return path
        }
        else {
            if (onStack[neighbor]) {
                                                                                            // If neighbor isn't on the stack, then neighbor is part of a different, previously discovered SCC
                lowest[currentNode] = min(lowest[currentNode], order[neighbor]);
            }
        }
    }

    if(lowest[currentNode] == order[currentNode]) {                                         // If lowest[X] = order[X] then X has no back-edge and is the root of its SCC
                                                                                            // The stack must be popped up to said root, as we have found a complete SCC                                                                                      
        vector<int> currentScc;

        int stackTop;

        do {

            stackTop = nodeStack.top();
            
            nodeStack.pop();
            onStack[stackTop] = false;

            currentScc.push_back(stackTop);

        } while (currentNode != stackTop);

        scc.push_back(currentScc);
    }
}

void Graph::CriticalEdgesDFS(int currentNode, int parent, vector <Edge>& cc, vector<int>& order, vector<int>& lowest, int& counter) {

    order[currentNode] = counter;
    lowest[currentNode] = counter;

    ++counter;

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if(order[child] < 0) {

            CriticalEdgesDFS(child, currentNode, cc, order, lowest, counter);

            lowest[currentNode] = min(lowest[currentNode], lowest[child]);      // Update lowest after subtree is finished

            if (lowest[child] > order[currentNode]) {
                                                            // We can't reach the current node through any path that doesn't include the current edge
                                                            // So current edge is critical
                cc.push_back(edge);            
            }
        }
        else if (child != parent || directed) {                         // Found visited node, so a back-edge
                                                            // The if condition is necessary so we don't go back through the edge we just used
            lowest[currentNode] = min(lowest[currentNode], order[child]);
        }
    }
}

void Graph::BiconnectedDFS(int currentNode, int parent, int currentDepth, vector< vector <int>>& bcc, vector<int>& depth, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& visited) {

    depth[currentNode] =  currentDepth;
    lowest[currentNode] = currentDepth;
    visited[currentNode] = true;
    nodeStack.push(currentNode);

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if (child != parent) {          // Check is required to prevent loops
                                    
            if(visited[child]) {                    

                lowest[currentNode] = min(lowest[currentNode], depth[child]);               // Back-edge is found
            }

            else {

                BiconnectedDFS(child, currentNode, currentDepth +1, bcc, depth, lowest, nodeStack, visited);

                lowest[currentNode] = min(lowest[currentNode], lowest[child]);              // Update with lowest depth descendants can reach

                if(depth[currentNode] <= lowest[child]) {                                    // If true, then child can't reach above current depth, so current node separates two BCCs

                    
                    vector < int > currentBcc;
                    currentBcc.push_back(currentNode);                  // Current node is part of both BCCs that it separates

                    int stackTop;

                    do {

                        stackTop = nodeStack.top();
                        nodeStack.pop();
                        currentBcc.push_back(stackTop);
                    } while ( stackTop != child );

                    bcc.push_back(currentBcc);
                }
            }
        }
    }
}

void Graph::TopologicalDFS(int currentNode, stack<int>& orderStack, vector<bool>& visitedNodes) {

    visitedNodes[currentNode] = true;

    for(auto edge : adjacencyList[currentNode]) {

        int node = edge.destination;

        if(!visitedNodes[node]) {

            TopologicalDFS(node, orderStack, visitedNodes);
        }
    }
    
    orderStack.push(currentNode);           // The stack is formed in the return order of the DFS
}

#pragma endregion


int main() {
    // trimis pentru a testa noua clasa heap
    ifstream inputFile("dijkstra.in"); 
    ofstream outputFile("dijkstra.out");

    int numberOfNodes, numberOfEdges;

    inputFile >> numberOfNodes >> numberOfEdges;

    Graph graph(numberOfNodes + 1, numberOfEdges, true, true);  // +1 deoarece pe infoarena sunt indexate de la 1 nodurile

    graph.BuildFromAdjacencyList(inputFile);

    vector<int> distances = graph.WeightedDistances(1);

    for(int i = 2; i < numberOfNodes +1; ++i) {
        if(distances[i] == 2147483647) outputFile<<0<<' '; else outputFile << distances[i]<<' '; 
    }

    return 0;
}