252 lines
8.5 KiB
C
252 lines
8.5 KiB
C
|
// Copyright (c) 1997-2000
|
||
|
// Utrecht University (The Netherlands),
|
||
|
// ETH Zurich (Switzerland),
|
||
|
// INRIA Sophia-Antipolis (France),
|
||
|
// Max-Planck-Institute Saarbruecken (Germany),
|
||
|
// and Tel-Aviv University (Israel). All rights reserved.
|
||
|
//
|
||
|
// This file is part of CGAL (www.cgal.org)
|
||
|
//
|
||
|
// $URL: https://github.com/CGAL/cgal/blob/v5.1/Union_find/include/CGAL/Union_find.h $
|
||
|
// $Id: Union_find.h fcd4e57 2020-05-22T11:29:03+02:00 Marc Glisse
|
||
|
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
||
|
//
|
||
|
//
|
||
|
// Author(s) : Michael Seel <seel@mpi-sb.mpg.de>,
|
||
|
// Lutz Kettner <kettner@mpi-sb.mpg.de>
|
||
|
|
||
|
|
||
|
#ifndef CGAL_UNION_FIND_H
|
||
|
#define CGAL_UNION_FIND_H
|
||
|
|
||
|
#include <CGAL/basic.h>
|
||
|
#include <CGAL/memory.h>
|
||
|
#include <cstddef>
|
||
|
|
||
|
namespace CGAL {
|
||
|
|
||
|
namespace internal {
|
||
|
|
||
|
template <class PTR_, class V_, class R_, class P_>
|
||
|
class UF_forward_iterator {
|
||
|
PTR_ m_p;
|
||
|
public:
|
||
|
// should be private and Union_find<...> a friend.
|
||
|
PTR_ ptr() const { return m_p; }
|
||
|
|
||
|
typedef UF_forward_iterator<PTR_, V_, R_, P_> Self;
|
||
|
typedef V_ value_type;
|
||
|
typedef R_ reference;
|
||
|
typedef P_ pointer;
|
||
|
typedef std::forward_iterator_tag iterator_category;
|
||
|
|
||
|
UF_forward_iterator() : m_p(0) {}
|
||
|
UF_forward_iterator(PTR_ p) : m_p(p) {}
|
||
|
|
||
|
// Allows construction of const_iterator from iterator
|
||
|
template <class PPTR, class VV, class RR, class PP>
|
||
|
UF_forward_iterator(const UF_forward_iterator<PPTR, VV, RR, PP>& i)
|
||
|
: m_p(i.ptr()) {}
|
||
|
|
||
|
bool operator==( const Self& x) const { return m_p == x.m_p; }
|
||
|
bool operator!=( const Self& x) const { return !(*this == x); }
|
||
|
bool operator<(const Self& other) const{ return m_p<other.m_p; }
|
||
|
bool operator>(const Self& other) const{ return m_p>other.m_p; }
|
||
|
bool operator<=(const Self& other) const{ return m_p<=other.m_p; }
|
||
|
bool operator>=(const Self& other) const{ return m_p>=other.m_p; }
|
||
|
|
||
|
reference operator*() const { return m_p->value; }
|
||
|
pointer operator->() const { return &(m_p->value); }
|
||
|
Self& operator++() {
|
||
|
CGAL_assertion(m_p != 0);
|
||
|
m_p = m_p->next;
|
||
|
return *this;
|
||
|
}
|
||
|
Self operator++(int) {
|
||
|
Self tmp = *this;
|
||
|
++*this;
|
||
|
return tmp;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // internal
|
||
|
|
||
|
|
||
|
// Union-Find with path compression.
|
||
|
// --------------------------------------------------------------------
|
||
|
// An instance of the data type Union_find is a partition of values of
|
||
|
// type |T| into disjoint sets. The type |A| has to be a model of the
|
||
|
// allocator concept as defined in the C++ standard.
|
||
|
|
||
|
// Union_find is implemented with union by rank and path compression.
|
||
|
// The running time for $m$ set operations on $n$ elements is
|
||
|
// $O(n \alpha(m,n))$ where $\alpha(m,n)$ is the extremely slow
|
||
|
// growing inverse of Ackermann's function.
|
||
|
|
||
|
template <typename T, typename A = CGAL_ALLOCATOR(T) >
|
||
|
class Union_find {
|
||
|
struct Union_find_struct {
|
||
|
typedef Union_find_struct* pointer;
|
||
|
// friend class Union_find<T,A>;
|
||
|
mutable pointer up;
|
||
|
pointer next;
|
||
|
std::size_t size;
|
||
|
T value;
|
||
|
Union_find_struct( pointer n, const T& x)
|
||
|
: up(0), next(n), size(1), value(x) {}
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
typedef Union_find<T,A> Self;
|
||
|
typedef Union_find_struct* pointer;
|
||
|
typedef const Union_find_struct* const_pointer;
|
||
|
|
||
|
typedef T value_type;
|
||
|
typedef T& reference;
|
||
|
typedef const T& const_reference;
|
||
|
|
||
|
typedef internal::UF_forward_iterator< pointer, T, T&, T*> iterator;
|
||
|
typedef iterator handle;
|
||
|
typedef internal::UF_forward_iterator< const_pointer, T, const T&, const T*>
|
||
|
const_iterator;
|
||
|
typedef const_iterator const_handle;
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
typedef CGAL_ALLOCATOR(Union_find_struct) allocator;
|
||
|
#else
|
||
|
typedef typename std::allocator_traits<A>::template rebind_alloc<Union_find_struct> allocator;
|
||
|
#endif
|
||
|
|
||
|
private:
|
||
|
pointer m_first;
|
||
|
std::size_t sets;
|
||
|
std::size_t values;
|
||
|
allocator alloc;
|
||
|
|
||
|
// Private decl. and no definition, thus no copy constructor
|
||
|
// and no assignment for this class.
|
||
|
Union_find(const Self&);
|
||
|
Self& operator=(const Self&);
|
||
|
|
||
|
pointer find( pointer p) const {
|
||
|
CGAL_assertion(p != 0);
|
||
|
pointer r = p;
|
||
|
while (r->up)
|
||
|
r = r->up; // now r is the root;
|
||
|
while (p->up) {
|
||
|
pointer u = p->up;
|
||
|
p->up = r; // path compression: assign root r as new parent
|
||
|
p = u; // this would need the 'mutable' for the up field
|
||
|
} // if we would have a const_pointer, see the cast
|
||
|
return r; // in the fct. below. We keep the mutable as reminder.
|
||
|
}
|
||
|
const_pointer find( const_pointer p ) const {
|
||
|
return find( const_cast<pointer>(p));
|
||
|
}
|
||
|
bool is_valid(const_handle v) const { return v != const_handle(0); }
|
||
|
|
||
|
public:
|
||
|
Union_find() : m_first(0), sets(0), values(0) {}
|
||
|
~Union_find() { clear(); }
|
||
|
|
||
|
allocator get_allocator() const { return alloc; }
|
||
|
|
||
|
std::size_t number_of_sets() const { return sets; }
|
||
|
// returns the number of disjoint sets
|
||
|
|
||
|
std::size_t size() const { return values; }
|
||
|
// returns the number of values
|
||
|
|
||
|
std::size_t bytes() const {
|
||
|
// returns the memory consumed
|
||
|
return values * sizeof(Union_find_struct) + sizeof( Self);
|
||
|
}
|
||
|
|
||
|
std::size_t size( const_handle p) const { return find(p).ptr()->size; }
|
||
|
// returns the size of the set containing $p$
|
||
|
|
||
|
void clear();
|
||
|
// reinitializes to an empty partition
|
||
|
|
||
|
handle make_set(const T& x);
|
||
|
// creates a new singleton set containing |x| and returns a handle to it
|
||
|
|
||
|
handle push_back(const T& x) { return make_set(x); }
|
||
|
// same as |make_set(x)|
|
||
|
|
||
|
template <class Forward_iterator>
|
||
|
void insert( Forward_iterator first, Forward_iterator beyond) {
|
||
|
// insert the range of values referenced by |[first,beyond)|.
|
||
|
// Precond: value type of |Forward_iterator| is |T|.
|
||
|
while (first != beyond)
|
||
|
push_back(*first++);
|
||
|
}
|
||
|
|
||
|
handle find( handle p) const { return find(p.ptr()); }
|
||
|
|
||
|
const_handle find( const_handle p ) const {
|
||
|
// returns a canonical handle of the set that contains |p|,
|
||
|
// i.e., |P.same_set(p,q)| iff |P.find(p)| and |P.find(q)| return
|
||
|
// the same handle. Precond: |p| is a handle in the union find structure.
|
||
|
return find(p.ptr());
|
||
|
}
|
||
|
|
||
|
void unify_sets(handle p, handle q);
|
||
|
// unites the sets of partition containing $p$ and $q$.
|
||
|
// Precond: $p$ and $q$ are in the partition.
|
||
|
|
||
|
bool same_set( const_handle p, const_handle q) const {
|
||
|
// returns true iff $p$ and $q$ belong to the same set.
|
||
|
// Precond: $p$ and $q$ are in the partition.
|
||
|
return find(p) == find(q);
|
||
|
}
|
||
|
|
||
|
iterator begin() { return iterator(m_first); }
|
||
|
iterator end() { return iterator(0); }
|
||
|
|
||
|
const_iterator begin() const { return const_iterator(m_first); }
|
||
|
const_iterator end() const { return const_iterator(0); }
|
||
|
};
|
||
|
|
||
|
template <typename T, typename A>
|
||
|
typename Union_find<T,A>::handle Union_find<T,A>::make_set(const T& x) {
|
||
|
pointer tmp = m_first;
|
||
|
m_first = alloc.allocate(1);
|
||
|
std::allocator_traits<allocator>::construct(alloc, m_first, tmp, x);
|
||
|
++sets;
|
||
|
++values;
|
||
|
return handle( m_first);
|
||
|
}
|
||
|
|
||
|
template <typename T, typename A>
|
||
|
void Union_find<T,A>::clear() {
|
||
|
while (m_first) {
|
||
|
pointer tmp = m_first->next;
|
||
|
std::allocator_traits<allocator>::destroy(alloc, m_first);
|
||
|
alloc.deallocate(m_first,1);
|
||
|
m_first = tmp;
|
||
|
}
|
||
|
sets = 0;
|
||
|
values = 0;
|
||
|
}
|
||
|
|
||
|
template <typename T, typename A>
|
||
|
void Union_find<T,A>::unify_sets( handle p, handle q) {
|
||
|
CGAL_assertion( is_valid(p) && is_valid(q));
|
||
|
pointer pit = find( p.ptr());
|
||
|
pointer qit = find( q.ptr());
|
||
|
if (pit == qit)
|
||
|
return;
|
||
|
std::size_t sp = pit->size;
|
||
|
std::size_t sq = qit->size;
|
||
|
if (sp > sq)
|
||
|
std::swap(pit,qit); // now sp <= sq
|
||
|
pit->up = qit; // linking roots
|
||
|
qit->size += pit->size; // updating size
|
||
|
--sets;
|
||
|
}
|
||
|
|
||
|
} //namespace CGAL
|
||
|
|
||
|
#endif // CGAL_UNION_FIND_H
|