2
\$\begingroup\$

I implemented a generic Dijkstra algorithm. It's lazy since the vertices with their final distances are request on demand. I used the Fibonacci Heap from this question with a few changes (added a copy and move constructor and the copy and swap idiom among other things).

I'm looking for a general review.

Dijkstra's algorithm:

#ifndef DIJKSTRA_ALGORITHM_DIJKSTRA_ALGORITHM_H
#define DIJKSTRA_ALGORITHM_DIJKSTRA_ALGORITHM_H

#include "Graph.h"
#include <limits>
#include "index_fib_heap.h"
#include <type_traits>
#include <functional>
#include <deque>
#include <climits>

namespace mds {


    struct identity {
        template<typename U>
        constexpr auto operator()(U&& v) const noexcept
            -> decltype(std::forward<U>(v)) {
            return std::forward<U>(v);
        }
    };   

    template<typename W, 
             typename F = identity, 
             typename IPQ = mds::index_fib_heap<
                                typename std::remove_reference<
                                    decltype(F()(std::declval<W>()))
                                >::type
                            >
    >
    class dijkstra_algorithm {

        using Weight = typename std::remove_reference<
            decltype(F()(std::declval<W>()))
        >::type;

       F               map_w;
       IPQ             pq;
       const graph<W>  *g;
       const edge<W>   **edge_to;
       Weight          *distances;

       std::pair<std::size_t, Weight> next() {
           if (pq.empty()) {
               return { SIZE_MAX, { } };
           }
           auto min = pq.top();
           pq.pop();
           for (const auto &e : g->adj(min.first)) {
               relax(&e);
           }
           return min;
       }

       void relax(const edge<W>* edge) {
           auto to    = edge->to();
           auto new_d = distances[edge->from()]
                        + map_w(edge->weight());

           if (new_d < distances[to]) {
               distances[to] = new_d;
               edge_to[to]   = edge;
               if (!pq.insert(to, new_d)) {
                   pq.decrease(to, new_d);
               }    
           }
       }

       void check(std::size_t v) const {
           if (g == nullptr || v >= g->V()) {
               throw std::out_of_range("vertex out of range");
           }
       }

    public:

       dijkstra_algorithm(std::size_t source, const graph<W>* pg, F pmap_w, IPQ ppq) 
           : g(pg), 
             map_w(pmap_w), 
             edge_to(new const edge<W>*[pg->V()]()),
             distances(new Weight[pg->V()]),
             pq(std::move(ppq)) 
       {       
           for (std::size_t i = 0; i < pg->V(); ++i) {
               distances[i] = std::numeric_limits<Weight>::max();
           }
           pq.insert(source, distances[source] = { });   
       }

       dijkstra_algorithm(std::size_t source, const graph<W>* pg, F pmap_w)
           : dijkstra_algorithm(source, pg, pmap_w, { pg->V() }) { }

       dijkstra_algorithm(std::size_t source, const graph<W>* pg)
           : dijkstra_algorithm(source, pg, { }) { }

       dijkstra_algorithm(dijkstra_algorithm<W, F, IPQ> && dijkstra) noexcept
          : map_w(std::move(dijkstra.map_w)), 
           pq(std::move(dijkstra.pq)), 
           g(dijkstra.g), 
           edge_to(dijkstra.edge_to), 
           distances(dijkstra.distances) 
       { 
           dijkstra.g         = nullptr;
           dijkstra.edge_to   = nullptr;
           dijkstra.distances = nullptr;
       }

       dijkstra_algorithm(const dijkstra_algorithm<W, F, IPQ>& dijkstra)
           : map_w(dijkstra.map_w),
             pq(dijkstra.pq),
             g(dijkstra.g) 
       {
           edge_to   = new const edge<W>*[g->V()];
           distances = new Weight[g->V()];
           for (std::size_t i = 0; i < g->V(); ++i) {
               edge_to[i]   = dijkstra.edge_to[i];
               distances[i] = dijkstra.distances[i];
           }
       }

       ~dijkstra_algorithm() {       
           delete[] edge_to;
           delete[] distances;
       }

       Weight distance_to(std::size_t v) const {
           check(v);
           return distances[v];
       }

       bool has_path(std::size_t v) const {
          return distance_to(v) < std::numeric_limits<Weight>::max();
       }

       std::deque<const edge<W>*> path(std::size_t v) const {
           check(v);

           std::deque<const edge<W>*> path;
           auto e = edge_to[v];
           while (e != nullptr) {
               path.push_front(e);
               e = edge_to[e->from()];
           }
           return path;
       }

       friend void swap(dijkstra_algorithm<W, F, IPQ>& first, 
                        dijkstra_algorithm<W, F, IPQ>& second) noexcept {
           using std::swap;
           swap(first.map_w, second.map_w);
           swap(first.pq, second.pq);
           swap(first.g, second.g);
           swap(first.edge_to, second.edge_to);
           swap(first.distances, second.distances);

       }

       dijkstra_algorithm<W, F, IPQ>& operator=(dijkstra_algorithm<W, F, IPQ> other)  noexcept {
           swap(*this, other);
           return *this;
       }

       class dijkstra_iterator
           : public std::iterator<std::forward_iterator_tag,
           std::remove_cv_t<std::size_t>,
           std::ptrdiff_t,
           std::size_t*,
           std::size_t& >
       {

          dijkstra_algorithm<W, F, IPQ>*  dijkstra;
          std::pair<std::size_t, Weight> current;

       public:

           explicit dijkstra_iterator(dijkstra_algorithm<W, F, IPQ>*  pdijkstra)
               : dijkstra(pdijkstra) { 
               current = pdijkstra->next();

           }

           explicit dijkstra_iterator(std::size_t pcurrent)
               : current({ pcurrent, { } }) { }

           dijkstra_iterator& operator++ () {
               current = dijkstra->next();
               return *this;
           }
           dijkstra_iterator operator++ (int) {
               dijkstra_iterator tmp(*this);
               current = dijkstra->next();
               return tmp;
           }

           bool operator == (const dijkstra_iterator& rhs) const {
               return current.first == rhs.current.first;
           }

           bool operator != (const dijkstra_iterator& rhs) const {
               return current.first != rhs.current.first;
           }

           const std::pair<std::size_t, Weight>& operator* () const {
               return current;
           }

           const std::pair<std::size_t, Weight>&  operator-> () const {
               return current;
           }
       };

       using iterator = dijkstra_iterator;
       iterator begin() {
           return iterator(this);
       }

       iterator end()  {
           return iterator(SIZE_MAX);
       }
    };
}

#endif //DIJKSTRA_ALGORITHM_DIJKSTRA_ALGORITHM_H

Fibonacci Heap:

#ifndef INDEX_FIB_HEAP_FIB_HEAP_H
#define INDEX_FIB_HEAP_FIB_HEAP_H

#include <algorithm>
#include <stdexcept>
#include <functional>

namespace mds {

    template<typename P, typename  Func = std::less<P>>
    class index_fib_heap {

        struct node {

            unsigned index;
            P prty;

            unsigned degree = 0;
            bool mark       = false;
            node* parent    = nullptr;
            node* childs    = nullptr;
            node* left      = nullptr;
            node* right     = nullptr;


            node(node* n) :
                prty(n->prty),
                index(n->index),
                degree(n->degree),
                mark(n->mark) {

            }

            node(unsigned pindex, const P& priority)
                : index(pindex),
                prty(priority) { }

            node(unsigned pindex, P&& priority)
                : index(pindex),
                prty(std::move(priority)) { }

            node* add_brother(node* n) {
                n->parent   = parent;
                n->left     = this;
                n->right    = right;
                right->left = n;
                right       = n;
                return this;
            }
            node* remove_self() {
                left->right = right;
                right->left = left;
                return this;
            }

            node* add_child(node* n) {
                if (childs == nullptr) {
                    childs    = n->to_single_list();
                    n->parent = this;
                }
                else {
                    childs->add_brother(n);
                }
                n->mark = false;
                ++degree;
                return this;
            }

            node* to_single_list() {
                left  = this;
                right = this;
                return this;
            }
            void destroy() {
                auto n = childs;
                for (auto i = 0u; i < degree; ++i) {
                    auto next = n->right;
                    n->destroy();
                    n = next;
                }
                delete this;
            }

        };

        node* copy_node(node* n) {
            auto new_n = new node(n);
            index_table[n->index] = new_n;
            return new_n;
        }

        node* deep_copy(node* root) {
            auto copy = copy_node(root);
            if (root->childs == nullptr) {
                return copy;
            }

            auto n = root->childs->right;
            auto child_copy = deep_copy(root->childs)
                              ->to_single_list();

            copy->childs = child_copy;
            child_copy->parent = copy;
            for (auto i = 1u; i < root->degree; ++i) {
                child_copy->add_brother(deep_copy(n));
                n = n->right;
            }
            return copy;
        }

        void consolidate() {
            unsigned bound = static_cast<unsigned>(
                        std::log(N) / LOG_OF_GOLDEN) + 1;
            auto degree_array = new node*[bound]();

            for (auto n = min; root_size > 0; --root_size) {
                auto parent = n;
                auto d = parent->degree;
                n = n->right;

                while (degree_array[d] != nullptr) {
                    auto child = degree_array[d];
                    if (cmp(child->prty, parent->prty)) {
                        std::swap(child, parent);
                    }
                    parent->add_child(child->remove_self());
                    degree_array[d++] = nullptr;
                }
                degree_array[d] = parent;
            }

            auto d = 0u;
            while (degree_array[d++] == nullptr);

            min = degree_array[d - 1]->to_single_list();
            for (; d < bound; ++d) {
                if (degree_array[d] != nullptr) {
                    add_to_root(degree_array[d]);
                }
            }
            delete[] degree_array;
            ++root_size;
        }

        node* remove_min() {
            if (empty()) {
                throw std::underflow_error("underflow");
            }

            auto deleted = min;
            add_childs_to_root(deleted);
            deleted->remove_self();
            --root_size;

            if (deleted == deleted->right) {
                min = nullptr;
            }
            else {
                min = min->right;
                consolidate();
            }
            --N;
            return deleted;
        }

        void add_to_root(node* n) {
            min->add_brother(n);
            if (cmp(n->prty, min->prty)) {
                min = n;
            }
            ++root_size;
        }

        void add_childs_to_root(node* n) {
            auto c = n->childs;
            auto d = n->degree;
            for (auto i = 0u; i < d; ++i) {
                auto next = c->right;
                add_to_root(c);
                c = next;
            }
        }

        template<typename PP>
        void decrease_priority(node *n, PP&& new_p) {
            if (cmp(n->prty, new_p)) {
                throw std::runtime_error("key is greater");
            }

            n->prty = std::forward<PP>(new_p);
            auto p = n->parent;

            if (p != nullptr 
                  && cmp(n->prty, p->prty)) {
                cut(n, p);
                cascading_cut(p);
            }
            if (cmp(n->prty, min->prty)) {
                min = n;
            }
        }
        void cut(node* child, node* parent) {
            min->add_brother(child->remove_self());
            child->mark = false;
            --parent->degree;
            if (parent->degree == 0) {
                parent->childs = nullptr;
            }
            else if (parent->childs == child) {
                parent->childs = child->right;
            }
            ++root_size;
        }

        void cascading_cut(node* n) {
            while (n->parent != nullptr && n->mark) {
                cut(n, n->parent);
                n = n->parent;
            }
            n->mark = n->mark || n->parent;
        }

        void check_index(unsigned index) const {
            if (index >= max_size) {
                std::out_of_range("index out of range");
            }
        }

        static constexpr  double LOG_OF_GOLDEN = 0.4812118;

        unsigned max_size;
        node**   index_table;
        node*    min;
        unsigned N;
        unsigned root_size;
        Func     cmp;

    public:

        index_fib_heap(unsigned pmax_size, Func pcmp)
            : max_size(pmax_size),
            index_table(new node*[pmax_size]()),
            min(nullptr),
            N(0),
            root_size(0),
            cmp(pcmp) { }

        index_fib_heap(unsigned pmax_size)
            : index_fib_heap(pmax_size, Func()) { }


        index_fib_heap(index_fib_heap<P, Func> && fib) noexcept :
            min(fib.min),
            N(fib.N),
            root_size(fib.root_size),
            cmp(fib.cmp),
            index_table(fib.index_table),
            max_size(fib.max_size)
        {  
            fib.N = fib.max_size = fib.root_size = 0;
            fib.index_table = nullptr;
            fib.min = nullptr;
        }

        index_fib_heap(const index_fib_heap<P, Func> & fib) noexcept 
            : N(fib.N),
              root_size(fib.root_size),
              cmp(fib.cmp),
              max_size(fib.max_size),
              index_table(new node*[fib.max_size]()) 
        {
            if (fib.empty()) { 
                return; 
            }
            min    = deep_copy(fib.min)->to_single_list();
            auto n = fib.min->right;
            for (auto i = 1u; i < root_size; ++i) {
                min->add_brother(deep_copy(n));
                n = n->right;
            }
        }


        ~index_fib_heap() {
            auto n = min;
            for (auto i = 0u; i < root_size; ++i) {
                auto next = n->right;
                n->destroy();
                n = next;
            }
            delete[] index_table;
        }

        void pop() {
            auto min_node = remove_min();
            index_table[min_node->index] = nullptr;
            delete min_node;
        }

        std::pair<unsigned, P> top() const {
            return { min->index, min->prty };
        }

        template<typename PP>
        bool insert(unsigned index, PP&& prty) {
            check_index(index);
            auto &new_node = index_table[index];
            if (new_node != nullptr) {
                return false;
            }
            new_node = new node(index,
                std::forward<PP>(prty));
            if (min == nullptr) {
                min = new_node->to_single_list();
                ++root_size;
            }
            else {
                add_to_root(new_node);
            }
            ++N;
            return true;
        }

        template<typename PP>
        void decrease(unsigned index, PP&& prty) {
            check_index(index);
            auto node = index_table[index];
            if (node != nullptr) {
                decrease_priority(node,
                    std::forward<PP>(prty));
            }
        }

        bool empty() const {
            return min == nullptr;
        }

        unsigned size() const {
            return N;
        }

        bool contains(unsigned index) const {
            return index_table[index] != nullptr;
        }

        friend void swap(index_fib_heap<P, Func>& first, index_fib_heap<P, Func>& second) noexcept {
            using std::swap;
            swap(first.N, second.N);
            swap(first.max_size, second.max_size);
            swap(first.index_table, second.index_table);
            swap(first.min, second.min);
            swap(first.root_size, second.root_size);
            swap(first.cmp, second.cmp);
        }

        index_fib_heap<P, Func>& operator=(index_fib_heap<P, Func> other)  noexcept {
            swap(*this, other);
            return *this;
        }

    };
}
#endif //INDEX_FIB_HEAP_FIB_HEAP_H

Graph:

#ifndef GRAPH_GRAPH_H
#define GRAPH_GRAPH_H
#include <cstddef>
#include <vector>


namespace mds {

    template <typename W, typename V = std::size_t>
    class edge {
        V v;
        V w;
        W wght;

    public:
        edge(const V& pfrom, const V& pto, const W& pweight)
        : v(pfrom), w(pto), wght(pweight) { }

        edge(V&& pfrom, V&& pto, W&& pweight)
        : v(std::move(pfrom)), 
          w(std::move(pto)), 
          wght(std::move(pweight)) { }

        V from() const { return v; }
        V to() const { return w; }
        const W& weight() const { return wght; }
    };

    template<typename W>
    class graph {

    public:
        using adj_structure = std::vector<edge<W>>;

        graph(std::size_t V)
            : adj_list(std::vector<adj_structure>(V, std::vector<edge<W>>())) { }

        graph(std::size_t V, std::initializer_list<edge<W>> list) 
            : graph(V) 
        {  
            for (const auto &e : list) {
                add_edge(e);
            }
        }

        void add_edge(const edge<W>& e) {
            check(e.from());
            check(e.to());
            adj_list[e.from()].push_back(e);
            ++edges;
        }

        const adj_structure& adj(std::size_t v) const {
            return adj_list[v];
        }

        std::size_t E() const {
            return edges;
        }

        std::size_t V() const {
            return adj_list.size();
        }

    private:
        std::vector<adj_structure>  adj_list;
        std::size_t edges;

        void check(std::size_t v) const {
            if (v >= V()) {
                throw std::out_of_range("vertex out of range");
            }
        }
    };

}
#endif // GRAPH_GRAPH_H 

Client example:

int main() {

    mds::graph<std::pair<int, int>> g(9, 
    {
        { 0, 1, { 4,  3  } },
        { 0, 7, { 8,  7  } },
        { 1, 2, { 8,  7  } },
        { 1, 7, { 11, 10 } },
        { 2, 3, { 7,  6  } },
        { 2, 8, { 2 , 1  } },
        { 2, 5, { 4,  3  } },
        { 3, 4, { 9,  8  } },
        { 3, 5, { 14, 13 } },
        { 4, 5, { 10, 9  } },
        { 5, 6, { 2,  1  } },
        { 6, 7, { 1,  0  } },
        { 6, 8, { 6,  5  } },
        { 7, 8, { 7,  6  } },
        { 1, 0, { 4,  3  } },
        { 7, 0, { 8,  7  } },
        { 2, 1, { 8,  7  } },
        { 7, 1, { 11, 10 } },
        { 3, 2, { 7,  6  } },
        { 8, 2, { 2 , 1  } },
        { 5, 2, { 4,  3  } },
        { 4, 3, { 9,  8  } },
        { 5, 3, { 14, 13 } },
        { 5, 4, { 10, 9  } },
        { 6, 5, { 2,  1  } },
        { 7, 6, { 1,  0  } },
        { 8, 6, { 6,  5  } },
        { 8, 7, { 7,  6  } },
    });

    using SSSP = mds::dijkstra_algorithm<std::pair<int, int>, 
                    std::function<int(const std::pair<int, int>&)>>;

    SSSP da = { 0u, &g, [](const auto& p) { return p.first; } };

     for (auto v : da) {
         std::cout 
             << v.first << " : " 
             << v.second << "\n";
     }
     std::cout << "\n";

     for (auto &e : da.path(4)) {
         std::cout 
             <<"From: "  << e->from() 
             << " To: "  << e->to() 
             << "\n";

         std::cout 
             <<"W1 "   << e->weight().first 
             << " W2 " << e->weight().second 
             << "\n";
     }
}
\$\endgroup\$
  • \$\begingroup\$ Please remember to tag these questions with c++ as well. \$\endgroup\$ – Jamal Aug 14 '16 at 0:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.