Skip to main content
added 2 characters in body
Source Link
bipll
  • 998
  • 4
  • 7

Same goes for branches: there's no point in keeping child Nodes on heap, std::unordered_map<char, Node> is quite sufficient for a tree, with no additional overhead.

Same goes for branches: there's no point in keeping child Nodes on heap, std::unordered_map<char, Node> is quite sufficient for a tree, with no additional overhead.

added 2 characters in body
Source Link
bipll
  • 998
  • 4
  • 7
    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic simply written as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists:

Trie::Trie(): root(new Node) {}

And if you upgrade to C++14, recall that make_unique is the new new new

Trie::Trie(std::vector<std::string> words)
  : root(std::make_unique<Node>())
{

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic simply written as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists:

Trie::Trie(): root(new Node) {}

And if you upgrade to C++14, recall that make_unique is the new new

Trie::Trie(std::vector<std::string> words)
  : root(std::make_unique<Node>())
{

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic simply written as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists:

Trie::Trie(): root(new Node) {}

And if you upgrade to C++14, recall that make_unique is the new new

Trie::Trie(std::vector<std::string> words)
  : root(std::make_unique<Node>())
{

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

added 105 characters in body
Source Link
bipll
  • 998
  • 4
  • 7
    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic to simply writewritten as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists.:

Trie::Trie(): root(new Node) {}

And rememberif you upgrade to C++14, recall that make_unique is the new new:

Trie::Trie(std::vector<std::string> words)
  : root(std::make_unique<Node>())  
{}

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic to simply write as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists. And remember that make_unique is the new new:

Trie::Trie(): root(std::make_unique<Node>()) {}

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

    bool end_of_word;
    Node(): end_of_word(false) {}

This would be probably more idiomatic simply written as

    bool end_of_word = false;

Next,

    explicit Trie(std::vector<std::string>);

is probably more idiomatic written as

    template<typename Itr> Trie(Itr begin, Itr end);

Next,

    std::unique_ptr<Node> root;

Why is it allocated on heap? It's a private member owned by this object, by putting it on heap you simply get some (noticeable) dereference overhead, and that's all.

Trie::Trie() {
  root = std::unique_ptr<Node>(new Node());
}

Trie::Trie(std::vector<std::string> words) {
  root = std::unique_ptr<Node>(new Node());

Irrespective of what's said above about root's type, such initializations are better put in initialization lists:

Trie::Trie(): root(new Node) {}

And if you upgrade to C++14, recall that make_unique is the new new

Trie::Trie(std::vector<std::string> words)
  : root(std::make_unique<Node>()) 
{

Next,

  for (const char & key : word) {

char type is small enough to be bound by value. Scalar and small POD types, when passed by reference, can create unnecessary overhead so it is better written as

  for (char key : word) {

(or even

  for (auto key : word) {

which makes perfect sense if you ever decide to make character type a parameter to your Trie.)

    auto found = it->children.count(key);
    if (!found)
      return false;
    it = it->children[key].get();

In C++, even with unordered containers, it is better done as

    auto where = it->children.find(key);
    if (where == it->children.end()) return false;
    it = where->second.get();

And similarly for Insert().

Which, by the way, I would declare as

  Trie &Insert(const std::string &);

But that is more like a personal preference.

How about adding a method that searches for all the elements whose keys start with a given prefix, and returns them either as a subnode, or a fresh container, like list or vector?

And the final boss: define an iterator type for your trie.

added 454 characters in body
Source Link
bipll
  • 998
  • 4
  • 7
Loading
added 454 characters in body
Source Link
bipll
  • 998
  • 4
  • 7
Loading
added 454 characters in body
Source Link
bipll
  • 998
  • 4
  • 7
Loading
Source Link
bipll
  • 998
  • 4
  • 7
Loading