3
\$\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\$
1
  • \$\begingroup\$ Please remember to tag these questions with c++ as well. \$\endgroup\$
    – Jamal
    Commented Aug 14, 2016 at 0:02

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.