Трие структура реализации данных в C++11 с помощью смарт-указатели


Эта реализация является частью моего проекта с открытым исходным кодом лес.

Я написал следующий заголовочный файл для реализации бор структура данных, которая поддерживает вставку и поиск строки, состоящие из 26 строчных букв английского алфавита. Я с нетерпением жду, чтобы услышать Ваше мнение об этой реализации и предложения/исправления вы можете иметь.

/**
 * @file trie.h
 */

#ifndef TRIE_H
#define TRIE_H

#include <iostream>
#include <string>
#include <memory>

#define ALPHABET_SIZE 26

/**
 * @brief The forest library namespace
 */
namespace forest {
  namespace trie {
    /**
     * @brief trie node struct
     */
    struct node {
      std::shared_ptr<node> children[ALPHABET_SIZE];
      bool end;
      /**
       * @brief Constructor of a trie node
       */
      node() {
        this->end = false; 
        for (int i = 0; i < ALPHABET_SIZE; i++) {
          this->children[i] = NULL;
        }
      }
    };
    /**
     * @brief trie class
     */
    class tree {
    private:
      std::shared_ptr<node> root;
    public:
      tree() {
              root = std::make_shared<node>();
      }
      /**
       * @brief Inserts the given key into the trie
       * @param key The key to be inserted
       * @return void
       */
      void insert(const std::string &key) {
        std::shared_ptr<node> n = root;
        for (int i = 0; i < key.length(); i++) {
          int index = key[i] - 'a';
          if (n->children[index] == nullptr) {
            n->children[index] = std::make_shared<node>();
          }
          n = n->children[index];
        }
        n->end = true;
      }
      /**
       * @brief Searches for the given key in the trie
       * @param key The key to be inserted
       * @return true if key exists in the trie and false otherwise
       */
      const bool search(const std::string &key) {
        std::shared_ptr<node> n = root;
        for (int i = 0; i < key.length(); i++) {
          int index = key[i] - 'a';
          if (n->children[index] == nullptr) {
            return false;
          }
          n = n->children[index];
        }
        return (n != nullptr && n->end == true);
      }
    };
  }
}

#endif


1870
5
задан 3 апреля 2018 в 10:04 Источник Поделиться
Комментарии
2 ответа

Вашего включить предохранитель, возможно столкновение с другим файлом. В конце концов, вы использовали внешнее пространство имен, потому что имя не уникально; то же справедливо и для заголовка. Рекомендуем вам использовать UUID для того, а также #pragma once . (Лично, я решил использовать ПРАГМА только с мыслью, что если я когда-нибудь понадобится охранники, они могут быть механически генерируется, все правильно. Но для всех основных платформ взять ПРАГМА поэтому я никогда не нуждался в ГВ.)


#define ALPHABET_SIZE 26

Ноооо... не использовать #define для простых констант, особенно в заголовке файла!


Давайте посмотрим на ваш node конструктор:

(и что со всем этим this-> на каждого члена доступ?)

this->end = false;

Вы не должны быть присвоение начальных значений в конструкторе. Вы должны инициализировать вещи. Здесь, вы можете просто использовать встроенную инициализатора в объявлении данных, и покончим с этим.

// data member
bool end = false;

Продолжая узла конструктора:

for (int i = 0; i < ALPHABET_SIZE; i++) {
this->children[i] = NULL;

Вау! Много ходить сюда.

Во-первых, не использовать NULL. Не всегда! Если вы пишете на C++11, это мертвые и должны быть забыты.

Во-вторых, не пишите наследие for петли перейти в коллекцию. Используйте диапазон-для формы.

for (auto el : children)
el.reset();

В-третьих, не оставить петли на всех, если вы можете использовать алгоритмы.

std::fill (std::begin(children), std::end(children), nullptr);

но мне не нравится должны дать две отдельные точки, когда я просто хочу все это. Алгоритмы std отстают в этом отношении. Но, мы имеем импульс как полигон для библиотечных вещи, которые мы хотим добавить в стандартный или просто часто используемые: (см. док страницы)

boost::fill (children, nullptr);

Но... просто нет! А shared_ptr автоматически инициализируется пустой, это означает, что конструктор уже делал это до того, как тело ваш код выполняется!

Так, добавить inline инициализатора endи затем удалить весь конструктор.


Теперь посмотрим на конструктор для tree.

То же самое: не назначать в организме, но инициализации членов. Член рядный init будет прекрасно работать здесь.

// data member of tree
std::shared_ptr<node> root = std::make_shared<node>();

а потом удалить все tree() конструктор function.


дерево::вставить

В C++ (в отличие от c) стиль поставить * или типа с типом, а не именем определяется. Так что пишите string& name не string &name.

Использовать auto (почти везде).

auto n = root;

Для for петли, те же замечания, как и раньше. Перестать думать цифр индекса в массив или другую коллекцию, которую затем необходимо индексного. Скорее, думаю о итератора позиции в любой коллекции, или конструкции, которые делают обход и просто дать вам ссылку на рабочий элемент.

Так что нет i.

for (auto ch : key) {

Не сравнивайте указатели (или смарт-указатели) со значением null. Они имеют значение истина, которая работает на этом напрямую.

Помните, что вы можете сделать ссылку (псевдоним) на любой части вашего сведения, чтобы не повторить children[index] три раза.

auto& slot = n->children[index];
if (!slot) slot = make_shared<node>();
n= slot;


Похожие на search.
Возвращая const bool не делайте ничего; просто вернуть bool.
В конце этой функции, тестирование true - это просто глупо. Это уже bool! Какое значение несет? true.

Кроме того, явно не испытывая против nullptr здесь поможет вам увидеть, как идиома хорошо работает для сторожил реальный тест:

return n && n->end;

Да, и не ставить избыточные скобки вокруг возвращаемых значений.


Кстати, Ваш алгоритм и подход вполне уместен. Это только по языку C++ беглости и идиомы, которые я прокомментировал.

4
ответ дан 4 апреля 2018 в 09:04 Источник Поделиться

Использование shared_ptr

Будьте осторожны std::shared_ptr Если вы не разделяя данные. Общие указатели не свободны, по сравнению с std::unique_ptr и сырые указатели копирование общий указатель довольно дорого. Обычно вы хотите использовать shared_ptr когда вы выражаете долевой собственности данных. Узлы никогда не подвергаются за пределами вашего дерева, используя unique_ptr должно быть достаточно здесь.

Учитывая std::unique_ptr<Node> root и используя std::vector<std::unique_ptr<Node>> children в узле вы бы переписать свой цикл что-то вроде этого:

    node* n = root.get();
for (int i = 0; i < key.length(); i++) {
int index = key[i] - 'a';
if (n->children[index] == nullptr) {
n->children[index] = std::make_unique<node>();
}
n = n->children[index].get();
}

Предупреждения

Компиляция с -Wall на показаны два предупреждения в отношении key.length() что возвращает size_t и не int.

ALPHABET_SIZE

Зад @JDługosz сказал, не #define что вы помещаете это в глобальной области видимости, можно использовать локальную переменную-член в trie структуры, где он используется. Если вы нуждаетесь в его сторону, сделать ее публичной, вы можете также сделать его статическим, то оно будет доступно как Trie::Alphabet_Size.

В зависимости от вашего целевого языка, ограничивая себя символы ASCII не может быть очень перспективное способ, чтобы реализовать это структура данных.

Комментарии

Это хорошо, чтобы попытаться документации, но смотреть на то, что некоторые из вашей документации написано:

  ...
/**
* @brief trie node struct
*/
struct node {
...

Это в основном повторяет то, что код уже говорит, единственное слово в этой документации блок, который добавляет любую информацию trie и это действительно подразумевается пространство имен. Ничего не говоря, вероятно, было лучше здесь. Попробуйте сказать что-то, что код не выразить.

Именования

Ваш trie называется tree ну технически он называется trie::tree, в то время как вы alread имеют пространства имен forest почему бы просто не назвать это trie, это позволит сэкономить поколений программистов от необходимости вводить другое пространство имен меток, и это позволит людям писать using forest::trie и не путать ее с tree вы должны поставить под forest::redblack::tree если вы держите схему именования происходит.

2
ответ дан 6 апреля 2018 в 01:04 Источник Поделиться