【问题标题】:Is there a suitable data structure for solving this question?是否有合适的数据结构来解决这个问题?
【发布时间】:2011-08-19 15:21:18
【问题描述】:

我有四组数据:

//group 1
2 2 6
2 2 7
2 3 5
2 3 6
2 3 7

3 2 5
3 2 6
3 2 7
3 3 4
3 3 5
3 3 6
3 3 7
...
...
7 2 2
7 2 3
7 2 5
7 2 7
7 3 2
7 5 2
7 6 2

//group 2
2 2 2
2 2 3
2 2 4
2 2 5
2 3 2
2 3 3

3 3 2
3 3 3
3 4 2
...
...

5 2 2

//group 3
2 4
2 5

3 3
3 4
3 5
3 6
...
...
7 2

//group 4
6
7
8

而我想要做的是对于给定的输入数字,给出所有可能的结果。 一个例子可能有助于解释我想要做什么: 假设输入为 7,那么输出应该如下:

from group 1
7 2 2
7 2 3
7 2 5
7 2 7
7 3 2
7 5 2
7 6 2

from group 2
//nothing

from group 3
7 2

from group 4
7

然后我添加第二个输入2(所以总输入是7 2),那么结果应该是

from group 1
7 2 2
7 2 3
7 2 5
7 2 7

from group 2
//nothing

from group 3
7 2

from group 4
//nothing

然后我添加第三个输入5(所以总输入是7 2 5),那么结果应该是

from group 1
7 2 5

from group 2
//nothing

from group 3
//nothing

from group 4
//nothing

看来我需要一片森林(几棵树),对吗? 如果是这样,对于这项任务是否有任何好的森林 C++ 树实现,或者我自己最好手工制作一个?

非常感谢

【问题讨论】:

  • 您需要实际描述您尝试使用的算法
  • 如果我理解正确,你想输出所有包含你的输出作为前缀的行,对吧?
  • 看起来像一棵前缀树。 C++ 标准库中没有一个,但我相信您会在 Internet 上找到一个很好的实现。
  • 对于数据库来说听起来不错。
  • @DeadMG:我认为算法取决于我选择使用的数据结构。如果我的例子不够清楚,请告诉我。

标签: c++ data-structures


【解决方案1】:

类似于保存数据的东西

std::set<std::vector<int> > data;

现在,如果不能保证每个组中的项目数相同,或者如果您知道每个组是特定数量的项目,那么您可以为每个组创建其中一个,然后将它们全部放在同一套。

然后将std::find_if 与带有上述data 的自定义谓词一起使用。在这个谓词中有一个std::vector,这是您要查找的序列。

struct search_sequence
{
  bool operator()(std::vector<int> const& cVal) const
  {
    if (sequence.size() <= cVal.size())
      return std::equal(sequence.begin(), sequence.end(), cVal.begin());
    return false;
  }

  std::vector<int> sequence;
};

现在将其与std::find_if 一起应用将找到data 中以搜索序列开头的所有序列。

编辑:要存储在单个实例中,请包装矢量,例如

struct group_entry
{
  int id;
  std::vector<int> data;

  friend bool operator<(group_entry const& lhs, group_entry const& rhs)
  {
    return lhs.id < rhs.id && lhs.data < rhs.data;
  }
};

现在你的集合包含

std::set<group_entry> data;

添加所有组中的所有数据

修改谓词:

struct search_sequence
{
  bool operator()(group_entry const& cVal) const
  {
    if (sequence.size() <= cVal.data.size())
      return std::equal(sequence.begin(), sequence.end(), cVal.data.begin());
    return false;
  }

  std::vector<int> sequence;
};

【讨论】:

  • 这4组数据不一样,例如第一组和第二组总是有 3 个元素,第三组总是有 2 个元素,第四组总是有 1 个元素。
  • @Gob00st,很好,在这种情况下,每个组都有一个 data 实例。并在每个组上执行find_if...
  • 我知道在我问这个问题之前我可以搜索 4 个组。我只是想知道每次进行 4 次搜索,我可以使用一种数据结构来保存整体数据并且每次只进行一次搜索吗?
  • @Gob00st:好的,那么我认为你需要一个前缀树。在每个节点上,您可以放置​​该节点的结果列表。
  • @Giorgio:到目前为止,前缀树听起来是正确的方法,但我有很多不同的输入,输入可以从 4 或 5 开始。所以我可能需要一个前缀树森林?
【解决方案2】:

C++ 术语中的“森林”是:

vector<set<string> >

其中集合中的字符串是“2 2 6”、“2 2 7”等。

假设您只想使用前缀,并且所有数字都是一位数(或零对齐到相同的宽度),您可以在您想要的前缀上使用 set::lower_boundset::upper_bound 来实现算法(在示例中,首先是“7”,然后是“7 2”,等等)。

例子:

void PrintResults(const vector<set<string> >& input, const string& prefix) {
  for (int i = 0, end(input.size()); i < end; ++i) {
    cout << "from group " << i + 1 << endl;
    const set<string>& group_set = input[i];
    set<string>::const_iterator low(group_set.lower_bound(prefix)), high(group_set.upper_bound(prefix));
    if (low == high) {
      cout << "//nothing" << endl;
    } else {
      for (; low != high; ++low) {
        cout << *low << endl;
      }
    }
  }
}

您可以为此使用尝试向量,但没有标准库版本。

【讨论】:

    【解决方案3】:

    因为深度似乎是固定的

    std::map<int, std::map<int, std::set<int> > >
    

    会做这项工作。不过考虑到数据项的数量,不确定是否值得。

    【讨论】:

      【解决方案4】:

      这是一个可能的解决方案的草图:

      class Group
      {
          int id;
      
          std::vector<int> values;
      }
      

      您使用这个类来存储整个组(初始数据)和查询结果(应用某些过滤器后组的内容)。

      一棵树是由节点和边构成的;每个节点使用 Group 的向量来存储该节点的结果。

      class Node;
      
      typedef Node *NodePtr;
      
      class Edge
      {
          NodePtr target;
      
          int value;
      };
      
      class Node
      {
          // Results for each group. Maybe empty for certain groups.
          // Contains all elements for all groups in the root node.
          std::vector<Group> results;
      
          std::vector<Edge> children;
      };
      

      搜索时,从根开始。匹配,例如7 2,您查找通过遍历值 == 7 的边到达的根的子节点。然后查看该边的目标节点,并查找值 == 2 的边。当您到达路径的最后一个节点,结果向量中有结果。

      更新 当然你也有构建这样一棵树的问题。您可以使用递归算法来做到这一点。

      您从包含所有组和所有列表的根节点开始。然后,对于列表的每个第一个元素,添加一条带有相应节点和相应结果集的边。

      您对每个子节点重复上述步骤,这次查看列表中的第二个元素。以此类推,直到您无法再扩展树为止。

      【讨论】:

        【解决方案5】:

        正如其他发帖者所说,您需要一个前缀树。这是一个让您入门的简单示例,假设唯一的字符是 0-7。请注意,我设置的安全性非常低,并且 number 假定给它的字符串都后跟空格(即使是最后一个),并且结果以相同的方式返回(更容易)。对于真实的代码,应该涉及更多的安全性。此外,代码未编译/未经测试,因此可能有错误。

        class number { //create a prefix node type
            number& operator=(const number& b); //UNDEFINED, NO COPY
            int endgroup;  //if this node is the end of a string, this says which group
            number* next[8];  // pointers to nodes of the next letter
        public:
            number() :group(-1) { //constructor
                for(int i=0; i<8; ++i)
                    next[i] = nullptr;
            }
            ~number() { // destructor
                for(int i=0; i<8; ++i)
                    delete next[i];
            }
            void add(char* numbers, int group) { //add a string to the tree for a group
                if(next[numbers[0] == '\0') //if the string is completely used, this is an end
                    endgroup = group;
                else {
                    int index = numbers[0]-'0'; //otherwise, get next letter's node
                    if (next[index] == nullptr)
                        next[index] = new number; //and go there
                    next[index].add(numbers+2, group); //+2 for the space
                }
            }
            void find(char* numbers, 
                std::vector<std::pair<int, std::string>>& out, 
                std::string sofar="") 
            { //find all strings that match
                if(numbers[0]) { //if there's more letters 
                    sofar.append(numbers[0]).append(' '); //keep track of "result" thus far
                    int index = numbers[0]-'0'; //find next letter's node
                    if (next[index] == nullptr)
                        return; //no strings match
                    next[index].find(numbers+2, out, sofar); //go to next letter's node
                } else { //if there's no more letters, return everything!
                    if (endgroup > -1) //if this is an endpoint, put it in results
                        out.push_back(std::pair<int, std::string>(endgroup, sofar));
                    for(int i=0; i<8; ++i) { //otherwise, try all subsequent letter combinations
                        if (next[i]) {
                            std::string try(sofar);  //keep track of "result" thus far
                            try.append('0'+i).append(' ');
                            next[i].find(numbers, out, try); //try this letter
                        }
                    }
                }
            }
        } root; //this is your handle to the tree
        
        int main() {
            //group one
            root.add("2 2 6", 1);
            root.add("2 2 7", 1);
            //...
            //group two
            root.add("2 2 2", 2);
            //...
        
            std::string pattern;
            char digit;
            while(true) {
                std::cin >> digit;
                if (digit<'0' || digit > '7')
                    break;
                pattern.append(digit).append(' ');
                std::vector<std::pair<int, std::string>> results;
                root.find(pattern.c_str(), results);
                for(int g=1; g<4; ++g) {
                    std::cout << "group " << g << "\n";
                    for(int i=0; i<results.size(); ++i) {
                        if( results.first == g)
                            std::cout << results.second;
                    }
                } 
            }
        }
        

        【讨论】:

          【解决方案6】:

          字符串数组可以解决问题。每一行都是一个字符串。您可以使用分隔符装饰行以使搜索更容易,例如“/7/2/2/”而不是“7 2 2”,以便您可以搜索“/2”。

          我猜你的教授希望你使用更复杂的数据结构。

          【讨论】:

            猜你喜欢
            • 2021-03-17
            • 2023-01-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-08-22
            • 2013-03-02
            相关资源
            最近更新 更多