CHAPTER_9 提高篇(3)——数据结构(2)
9.3.1 树的静态写法
本节讨论一般意义的树,即子节点个数不限且子节点不分先后顺序。一般的树我们可以采取静态的写法。我们不采用链地址的方式来记录子节点位置,而采用数组下标的形式。结构体node定义如下:
struct Node {
typedef data; //数据域
vector<int> child; //指针域,存放所有孩子结点的下标
}node[maxn];
当需要新建一个结点时,就按顺序从数组中取出一个下标即可:
int index=0;
int newNode(int v) {
node[index].data=v; //数据域为v
node[index].child.clear(); //清空子节点
return index++; //返回index,然后index自增
}
有两点需要特别指出:
(1)在考试中涉及树(非二叉树)的考察时,一般给出结点编号,如:0、1、... 、n-1。这种情况下,就不需要newNode函数了。题目中给的结点编号可以直接用作数组下标,不需要我们创建index来统计结点了。
(2)如果题目中不涉及数据域,即只需要树的结构。那么结构体可以简化成vector数组,即vector<int> child[maxn]。这种写法其实就是下一章中图的邻接表的内容。
9.3.2 树的先根遍历
类似于二叉树的先序遍历,树的先根遍历也是同样的思路。我们总是先访问树的根节点,再去访问所有子树,这也同样是一个递归的过程,访问某一颗子树时,我们也是先访问它的根再访问它的子树。
代码如下:
void preOrder(int root) {
cout<<node[root].data;
for(int i=0;i<node[root].child.size();i++)
preOrder(node[root].child[i]);
}
需要注意的是,代码中没有明显的体现递归边界,并不是不存在递归边界。而是递归边界隐含在循环中的判断条件i<node[root].child.size()里面,当前根节点没有孩子时,这个循环不会执行,即成为了递归边界。
9.3.3 树的层序遍历
树的层序遍历与二叉树的层序遍历思路同样一致。 使用一个队列存放节点的数组中的下标(二叉树中是存放节点的地址),然后同样套用BFS的模板。代码如下:
void layerOrder(int root) {
queue<int> q; //存放节点下标的队列
q.push(root); //根节点下标入队
while(!q.empty()) {
int tmp=q.front();
q.pop(); //队首出队
cout<<node[tmp].data; //访问节点数据
for(int i=0;i<node[tmp].child.size();i++) {
q.push(node[tmp].child[i]); //所有子节点入队
}
}
}