清晰高效的3D范围树实现

5
我正在进行一个项目,需要在三维空间中查找物体,效率是一个重要的问题。我认为Range Tree非常适合我的需求。Interval Tree也可以使用,但是我不会从树中删除任何东西,一旦将3D空间中的每个物体添加进去,我将只使用该结构来执行搜索。
这里是我将如何使用这个结构: 假设我有一个对象数组(我们称之为< strong >queryArr < / strong >),其中包含大约10,000个对象,每个对象都有3个参数(x、y、z)。我还有另一个非常大的对象数组(我们称之为< strong >totalArr< / strong>),其中包含超过5,000,000个对象。
我的目标是在给定< strong >queryArr < / strong >元素的情况下,查找最相似(或相同的)元素在< strong >totalArr< / strong >中。在某些情况下,< strong >totalArr< / strong >中会有具有相同参数的对象,但在大多数情况下,没有具有相同参数的对象。
因此,我将搜索< code > (x + 10,y + 10,z + 10)< / code >和< code > (x-10,y-10,z-10)< / code >之间的所有值。如果没有产生任何结果,我将通过2来乘以x,y和z并再次尝试,直到产生一些结果。
最简单的方法是一种朴素的搜索方法,其复杂度为< code > O(N * M ) (N = queryArr的大小,M = totalArr的大小)< / code >,但该方法非常慢且愚蠢。
我认为Range Tree是最好的选择,但我从未自己实现过它,并且不太理解在大于2个维度上Range Tree如何工作。那么,有人知道一个很好的Range Tree实现吗?如果我能有源代码,我会理解它们的真正工作原理。
顺便说一句,如果您认为还有比Range Tree更好的结构,请告诉我,我很乐意听取建议。(我已经考虑过kd-Tree和Interval Tree)
谢谢,
1个回答

4

我刚刚阅读了维基百科文章。让我们看看我能否编写一个n维范围树。因为在三维中值得做的任何事情都值得在n维中做。

因此,n维范围树的基本部分是可以通过较低维度的范围树进行递归定义的。

一些属性类用于处理相对通用的值类型。将element_properties<T>特化以设置您的n维值的标量类型,并将get<i>(T const&)特化以获取您的n维值的第i维。

#include <memory>
#include <cstddef>
#include <vector>
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>

void Assert(bool test) {
  if (!test)
  {
    std::cout << "Assert failed" << std::endl;
    exit(-1);
  }
}
template<typename... Args>
struct Print {
  static void Do(Args... args) {}
};
template<typename Arg, typename... Tail>
struct Print<Arg, Tail...> {
  static void Do(Arg arg, Tail... args) {
      std::cout << arg;
      Print<Tail...>::Do(args...);
  }
};
template<typename... Args>
void Debug(Args... args) {
    std::cout << "DEBUG:[";
    Print<Args...>::Do(args...);
    std::cout << "]\n";
}

template<typename T>
struct element_properties {
  typedef typename T::value_type value_type;
};
template<>
struct element_properties<int> {
  typedef int value_type;
};
template<size_t d, typename T>
typename element_properties<T>::value_type get( T const & t );

template<size_t d>
typename element_properties<int>::value_type get( int i ) { return i; }

template<size_t d, typename U, typename A>
typename element_properties<std::vector<U,A>>::value_type get( std::vector<U,A> const& v) {
  return v[d];
}

template<typename T, size_t dim, typename Order = std::less< typename element_properties<T>::value_type> >
struct range_tree {
  typedef typename element_properties<T>::value_type value_type;
  struct sorter {
    bool operator()( T const& left, T const& right ) const {
      return Order()( get<dim-1>(left), get<dim-1>(right) );
    }
  };
  struct printer {
    std::string operator()( T const& t ) const {
      std::string retval = "[ ";
      retval += print_elements( t );
      retval += "]";
      return retval;
    }
    std::string print_elements( T const& t ) const {
      std::stringstream ss;
      typedef typename range_tree<T, dim-1, Order>::printer next_printer;
      ss << next_printer().print_elements(t);
      ss << get<dim-1>(t) << " ";
      return ss.str();
    }
  };
  template<typename Iterator>
  range_tree( Iterator begin, Iterator end ) {
    std::sort( begin, end, sorter() );
    root.reset( new tree_node( begin, end ) );
  }

  template<size_t n, typename Func>
  void walk(Func f) const {
      if (root) root->walk<n>(f);
  }
  template<size_t n, typename Func>
  void walk(Func f) {
      if (root) root->walk<n>(f);
  }
  struct tree_node {
    std::unique_ptr< range_tree<T, dim-1, Order> > subtree;
    T value;
    template<size_t n, typename Func>
    void walk(Func f) const {
      if (n==dim && !left && !right)
        f(value);
      if (left)
        left->walk<n>(f);
      if (right)
        right->walk<n>(f);
      if (subtree)
        subtree->walk<n>(f);
    }
    template<size_t n, typename Func>
    void walk(Func f) {
      if (n==dim && !left && !right)
        f(value);
      if (left)
        left->walk<n>(f);
      if (right)
        right->walk<n>(f);
      if (subtree)
        subtree->walk<n>(f);
    }
    void find_path( T const& t, std::vector< tree_node const* >& vec ) {
      vec.push_back(this);
      if ( sorter()(t, value) ) {
        if (left)
          left->find_path(t, vec);
      } else if (sorter()(value, t)) {
        if (right)
          right->find_path(t, vec);
      } else {
        // found it!
        return;
      }
    }
    std::vector< tree_node const* > range_search( T const& left, T const& right )
    {
      std::vector<tree_node const*> left_path;
      std::vector<tree_node const*> right_path;
      find_path( left, left_path );
      find_path( right, right_path );
      // erase common path:
      {
        auto it1 = left_path.begin();
        auto it2 = right_path.begin();
        for( ; it1 != left_path.end() && it2 != right_path.end(); ++it1, ++it2) {
          if (*it1 != *it2)
          {
            Debug( "Different: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
            break;
          }

          Debug( "Identical: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
        }
        // remove identical prefixes:
        if (it2 == right_path.end() && it2 != right_path.begin())
            --it2;
        if (it1 == left_path.end() && it1 != left_path.begin())
            --it1;
        right_path.erase( right_path.begin(), it2 );
        left_path.erase( left_path.begin(), it1 );
      }
      for (auto it = left_path.begin(); it != left_path.end(); ++it) {
        if (*it && (*it)->right) {
          Debug( "Has right child: ", printer()( (*it)->value ) );
          *it = (*it)->right.get();
          Debug( "It is: ", printer()( (*it)->value ) );
        } else {
          Debug( "Has no right child: ", printer()( (*it)->value ) );
          if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
            Debug( printer()( (*it)->value ), "<", printer()( left ), " so erased" );
            *it = 0;
          }
        }
      }
      for (auto it = right_path.begin(); it != right_path.end(); ++it) {
        if (*it && (*it)->left) {
          Debug( "Has left child: ", printer()( (*it)->value ) );
          *it = (*it)->left.get();
          Debug( "It is: ", printer()( (*it)->value ) );
        } else {
          Debug( "Has no left child: ", printer()( (*it)->value ) );
          if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
            Debug( printer()( right ), "<", printer()( (*it)->value ), " so erased" );
            *it = 0;
          }
        }
      }
      left_path.insert( left_path.end(), right_path.begin(), right_path.end() );
      // remove duds and duplicates:
      auto highwater = std::remove_if( left_path.begin(), left_path.end(), []( tree_node const* n) { return n==0; } );
      std::sort( left_path.begin(), highwater );
      left_path.erase( std::unique( left_path.begin(), highwater ), left_path.end() );
      return left_path;
    }

    std::unique_ptr<tree_node> left;
    std::unique_ptr<tree_node> right;
    // rounds down:
    template<typename Iterator>
    static Iterator middle( Iterator begin, Iterator end ) {
      return (end-begin-1)/2 + begin ;
    }
    template<typename Iterator>
    tree_node( Iterator begin, Iterator end ):value(*middle(begin,end)) {
      Debug( "Inserted ", get<dim-1>(value), " at level ", dim );
      Iterator mid = middle(begin,end);
      Assert( begin != end );
      if (begin +1 != end) { // not a leaf
        Debug( "Not a leaf at level ", dim );
        ++mid; // so *mid was the last element in the left sub tree 
        Assert(mid!=begin);
        Assert(mid!=end);
        left.reset( new tree_node( begin, mid ) );
        right.reset( new tree_node( mid, end ) );
      } else {
        Debug( "Leaf at level ", dim );
      }
      if (dim > 0) {
        subtree.reset( new range_tree<T, dim-1, Order>( begin, end ) );
      }
    }
  };
  std::unique_ptr<tree_node> root;
};
// makes the code above a tad easier:
template<typename T, typename Order >
struct range_tree< T, 0, Order > {
  typedef typename element_properties<T>::value_type value_type;
  struct printer { template<typename Unused>std::string print_elements(Unused const&) {return std::string();} };
  range_tree(...) {};
  struct tree_node {}; // maybe some stub functions in here
  template<size_t n, typename Func>
  void walk(Func f) {}
};

int main() {
  typedef std::vector<int> vector_type;
  std::vector<vector_type> test;
  test.push_back( vector_type{5,2} );
  test.push_back( vector_type{2,3} );
  range_tree< vector_type, 2 > tree( test.begin(), test.end() );
  std::cout << "Walking dim 2:";
  auto print_node = [](vector_type const& v){ std::cout << "(" << v[0] << "," << v[1] << ")"; };
  tree.walk<2>( print_node );
  std::cout << "\nWalking dim 1:";
  tree.walk<1>( print_node );
  std::cout << "\n";

  std::cout << "Range search from {3,3} to {10,10}\n";
  auto nodes = tree.root->range_search( vector_type{3,3}, vector_type{10,10} );
  for (auto it = nodes.begin(); it != nodes.end(); ++it)
  {
    (*it)->walk<2>( print_node );
  }
}

这非常接近于一个n维范围树。 0维树自然不包含任何内容。

现在已经添加了搜索基本功能(一次只能搜索一个维度)。您可以手动递归到较低的维度,或将其设置为range_search始终返回第1级tree_node*


1
嗨Yakk,谢谢你的回答,我正在尝试理解你的代码,但我甚至无法编译它:( 我尝试使用VC++和MinGW编译它,但没有结果。这段代码是用C++0x写的吗?有几件事情让我感到困惑,首先,为什么要删除相同的前缀?还有,当您调用**range_tree(...) {}**时,它会做些什么呢?如果可以的话,您能不能稍微解释一下您的代码?非常感谢您的时间和努力:) - user1880907
这里的...只是用来丢弃参数的。你可能需要用一个什么也不做的构造函数替换它,该构造函数接受两个迭代器作为参数。打印和调试代码是C++11的,无法在Visual Studio中编译。删除相同前缀是查找范围的算法的一部分--维基百科有解释。 - Yakk - Adam Nevraumont

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接