有没有合适的数据结构来解决这个问题?

时间:2011-08-19 15:21:19

标签: c++ data-structures

我有四组数据:

//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

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

from group 1
7 2 5

from group 2
//nothing

from group 3
//nothing

from group 4
//nothing

这似乎是我需要森林(几棵树),对吗? 如果是这样,是否有任何良好的c ++树实现这个任务,或者我最好亲自制作一个?

非常感谢

6 个答案:

答案 0 :(得分:3)

喜欢拿着数据的东西

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;
};

答案 1 :(得分:3)

用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;
      }
    }
  }
}

你可以使用一个尝试向量,但是没有std库版本。

答案 2 :(得分:2)

由于深度似乎是固定的

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

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

答案 3 :(得分:2)

以下是可能解决方案的草图:

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的边。当您到达时在路径的最后一个节点,你有结果向量的结果。

<强>更新 当然,你也有构建这样一棵树的问题。您可以使用递归算法来完成它。

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

您为每个子节点重复上述步骤,这次查看列表中的第二个元素。等等,直到你再也不能扩展树了。

答案 4 :(得分:2)

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

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;
            }
        } 
    }
}

答案 5 :(得分:1)

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

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