计算基于树结构的行和列以生成excel文件

我有一个class的部门:

class Department{ string des; string name; string id; List<Department> subDeps; } 

如所示的代码,一个部门可能有几个子部门。

现在,我已经从数据库中读取了信息,并得到了根部门对象。

然后我想要像这样生成一个excel工作表:

在这里输入图像说明

事实上,根“部门”对象是一个树形结构。

当我尝试将项目写入Excel表格时,我必须决定项目的行和列,但是我无法做到。

而且有人可以帮我一个忙吗?

这样看:

 0 00 000 | (0,0) (0,1) (0,2) 001 | (1,2) 1 10 100 | (2,0) (2,1) (2,2) 101 | (3,2) 11 | (4,1) 

我用同样的语法将节点(左侧)和相应的位置“(row,column)”命名为网格(右侧)。 这里有两个顶级部门,分别是0和1.您可以用“(x,y)”坐标来标记节点,并为每个树/顶级部门进行深度优先访问。 每当你从父亲到孩子下降,你必须增加列号1.每次你访问一个新的兄弟姐妹,你的行索引必须指向一个新的,空的(在这个例子中,兄弟10是11,但你可以(3,1),因为第3行已经被101部门占用)。

我会按照这些准则编写algorithm。 使用两个作为recursion深度优先访问parameter passing的variables,以及要访问的当前节点/部门,跟踪当前(x,y)。 确保函数根据需要编辑excel表示,但返回使用的最大行索引(以便在访问新同级时可以使用它来知道下一行是哪一行 – 如前例所示)。 每次recursion调用时,都必须用coords(x,y + 1)来调用它,因为它是一个新列。 以类似的方式,当你访问一个新的兄弟,你可以用(coords prev_call_retn + 1,y)来调用它,因为它是一个新行(其中coords_prev_call_retn是前一个兄弟上次recursion调用的值)。 辅助函数会调用根节点上的recursion函数,并用作基础坐标(0,0),或者你喜欢的任何东西。

 #include <iostream> #include <list> #include <string> using namespace std; class Dept { public: string id; list<Dept*> *subDepts; Dept(string id) { this->id = id; this->subDepts = new list<Dept*>(); } Dept *addChild(Dept *d) { this->subDepts->push_back(d); return d; } Dept *addChild(string id) { Dept *d = new Dept(id); return this->addChild(d); } }; void visit(Dept *d, int row, int col) { cout << "Dept " << d->id << ": (" << row << ", " << col << ")\n"; } int label(Dept *d, int row, int col) { if (d == 0) return row; visit(d, row, col); list<Dept*> *lst = d->subDepts; for (list<Dept*>::iterator it = lst->begin() ; it != lst->end(); it++) { row = label(*it, row, col+1) + 1; } if (lst->size() > 0) row--; return row; } int label(Dept *d) { label(d, 0, 0); } 

在这个C ++代码中,label()是你感兴趣的函数。

参数row和col应该是部门* d的正确坐标,以便我们可以立即访问节点。

循环recursion调用每个孩子的标签()(如果有的话)。 请注意,我使用variables行来跟踪最后使用的行+1。

最后,我们必须返回该函数使用的最后一行,但如果d-> subDepts不为空,则要小心减去一行。 这是因为在上一次迭代中增加了1行的for循环。

这里是一个主例子:

 int main(int argc, char **argv) { Dept *d0 = new Dept("0"); Dept *d00 = d0->addChild("00"); Dept *d01 = d0->addChild("01"); Dept *d000 = d00->addChild("000"); Dept *d001 = d00->addChild("001"); Dept *d002 = d00->addChild("002"); Dept *d010 = d01->addChild("010"); Dept *d02 = d0->addChild("02"); label(d0); return 0; } 

这里的结果是:

 bash-4.2$ g++ dept.cpp && ./a.out Dept 0: (0, 0) Dept 00: (0, 1) Dept 000: (0, 2) Dept 001: (1, 2) Dept 002: (2, 2) Dept 01: (3, 1) Dept 010: (3, 2) Dept 02: (4, 1) 

我希望这有帮助。

如果你想按照你的例子做布局,使用colspans来alignment单元格,那么如果没有先发现,或者事先知道(硬编码)树的最大深度,我看不到任何方法,您将需要此来确定用于部门的总列数。

一些表格属性的假设(将有助于理解下面的代码):

  • 只有最右边的单元格跨越多个列,其他所有其他单元格的列宽为1
  • 最右边的单元格不会跨越多行,总是有colHeight 1
  • 存在一些函数来计算表的“部门”部分的最大宽度或者最大列索引(在代码中设置variablesblockWidthmaxY

伪代码来布局表格:

 displayDeptTree(Department root) { startX, startY: beginning coord of the root dept blockWidth: total no. of columns (tree depth + 1, or hardcoded to value > possible depth) maxY = startY + blockWidth - 1 // global var? addDeptIntoCell(root, startX, startY) } // returns height of the cell int addDeptIntoCell(Department d, x, y) { colHeight = 1 if (d->subDeps is empty) { colWidth = maxY - y addDepartment(d, x, y, colHeight, colWidth) // colHeight = 1 always } else { currY = y for (Dept child : d->subDeps) { childHeight = addDeptIntoCell(child, x + 1, currY); // increment x pos for children currY += childHeight } colHeight = currY - y addDepartment(d, x, colHeight, 1) } return colHeight } addDepartment(Department d, x, y, height, width) { // Actually adds the department info into table, parameters should be self-explanatory, implementation not shown } 

这可能有一个错误等,我使用d-> subDeps的Java意义上,而不是C + +指针尊重,但你应该得到的一般想法:)