数据结构之tree
来源:互联网 发布:数据库项目经理的收获 编辑:程序博客网 时间:2024/06/06 12:49
1、基本概念
tree是节点的集合。这个集合可以是空集;若不是空集,则有一个根节点和零个或多个非空子树,每个子树都被根节点使用一条有向边所连接。
由树的定义,我们可以知道,树是由节点和有向边组成的,根据树的结构,我们有以下衍生的定义:
- 整棵树最上层的节点称为根节点(root);
- 连接两个节点的称为有向边(edge);
- 有向边的头称为父节点(parent),尾称为子节点(child);
- 没有子节点的节点称为叶节点(leaf);
- 若子节点的最大个数为n,则该树称为n叉树,即最多有两个子节点的树称为二叉树;
- 拥有相同父节点的子节点之间互为兄弟节点(sibling);
- 根节点至任何节点有唯一的路径(path),路径所经过的边的个数称为路径的长度(length);
- 根节点至任一节点的路径长度,称为该节点的深度(depth),则根节点的深度为0;
- 某一节点到其最深的子节点的路径长度称为该节点的高度(height),则树的高度为根节点的高度;
- 若节点a和节点b之间存在一条路径,则a称为b的祖代(ancestor),b称为a的子代(descendant);
- 某一节点和其子节点的个数之和称为该节点的大小(size)。
2、实现结构
树最直接的实现结构就是有节点数据和指向子节点的链组成,但是子节点的个数是不确定的,如果直接链接,将造成空间的巨大浪费。目前典型的结构定义如下:
struct TreeNode{ TreeNode() { this->data = 0; this->firstChild = NULL; this->nextSibling = NULL; } TreeNode(int data) { this->data = data; this->firstChild = NULL; this->nextSibling = NULL; } int data; TreeNode* firstChild; TreeNode* nextSibling;};
这里通过firstChild访问到子节点链表的表头,通过nextSibling是子节点形成链表。这样通过有限的结构,就可满足树无限的扩展。
3、节点遍历
要访问到树中的每个节点,根据不同的顺序得到的结果是不一样的,目前有一下几种遍历方法:
- 广度优先遍历。这种遍历方法,按照层次去遍历,依次遍历根节点,子节点及其兄弟节点,然后是下一层的子节点和兄弟节点,以下是实现算法:
void breadthFirstTravel(TreeNode* root){ cout<<"bft:"; queue<TreeNode*> q; q.push(root); while (!q.empty()) { TreeNode* node = q.front(); q.pop(); while (node != NULL) { cout <<node->data<<" "; if (node->firstChild) { q.push(node->firstChild); } node = node->nextSibling; } } cout<<endl;}
- 深度优先遍历。这种遍历方法,是沿着树的深度进行遍历,当节点v的的所有子节点都已经访问的后,将回溯到v的父节点继续搜索,不断重复以上过程直到所有节点都被访问到。这里节点的访问顺序不同,得到的结果也是不一致的,目前有三种访问顺序:
- 前序遍历。这种顺序,先访问根节点,然后访问其他子树,算法如下:
void depthFirstTravel_DLR(TreeNode* root){ cout<<"dft_DLR:"; stack<TreeNode* > s; s.push(root); while (!s.empty()) { TreeNode* node = s.top(); s.pop(); cout<<node->data<<" "; //记录右兄弟 if (node->nextSibling) { s.push(node->nextSibling); } //记录第一个子节点 if (node->firstChild) { s.push(node->firstChild); } } cout<<endl;}
这里是先访问根节点,然后是第一个子树,最后是该子树的兄弟子树。由于需要回溯节点,我们需要借助stack。
2. 中序遍历。这种顺序是先访问第一个子树,然后访问根节点,最后子树的兄弟子树。算法如下:
void depthFirstTravel_LDR(TreeNode* root){ //先遍历第一个子女 TreeNode* child = root->firstChild; if (child) { depthFirstTravel_LDR(child); } //访问自己 cout<<root->data<<" "; //遍历其他子女 if (child) { TreeNode* sibling = child->nextSibling; while (sibling) { depthFirstTravel_LDR(sibling); sibling = sibling->nextSibling; } }}
这里直接使用了递归的实现,看起来更加简洁,但递归也是用stack实现的,实际本质是一样的。
3. 后序遍历。这种顺序先遍历所有的子树,然后再访问根节点。算法如下:
void depthFirstTravel_LRD(TreeNode* root){ //先遍历所有子女 TreeNode* child = root->firstChild; if (child) { depthFirstTravel_LRD(child); TreeNode* sibling = child->nextSibling; while (sibling) { depthFirstTravel_LRD(sibling); sibling = sibling->nextSibling; } } //访问自己 cout<<root->data<<" ";}
以上是三种遍历顺序,下面是测试代码:
void treeTest(){ TreeNode *a = new TreeNode(1); TreeNode *b = new TreeNode(2); TreeNode *c = new TreeNode(3); TreeNode *d = new TreeNode(4); TreeNode *e = new TreeNode(5); TreeNode *f = new TreeNode(6); TreeNode *g = new TreeNode(7); TreeNode *h = new TreeNode(8); TreeNode *i = new TreeNode(9); a->firstChild = b; b->firstChild = f; b->nextSibling = c; c->firstChild = h; c->nextSibling = d; d->nextSibling = e; f->nextSibling = g; h->nextSibling = i; breadthFirstTravel(a); //bft:1 2 3 4 5 6 7 8 9 depthFirstTravel_DLR(a); //dft_DLR:1 2 6 7 3 8 9 4 5 cout<<"dft_LDR:"; depthFirstTravel_LDR(a); //dft_LDR:6 2 7 1 8 3 9 4 5 cout<<endl; cout<<"dft_LRD:"; depthFirstTravel_LRD(a); //dft_LRD:6 7 2 8 9 3 4 5 1 cout<<endl;}
- 数据结构之Tree
- 数据结构之Tree
- 数据结构之tree
- 基本数据结构之Binary Search Tree
- C++数据结构 之 红黑树_Red Black Tree
- 高级数据结构之K-D-TREE
- 重学数据结构系列之——平衡树之SB Tree(Size Blanced Tree)
- 数据结构《17》---- 自动补齐之《二》----Ternary Search Tree
- 数据结构之二叉树的遍历 HDU1710 Binary Tree Traversals
- 数据结构之AVLTree(04-1:Root of AVL Tree)
- C++数据结构 之 二叉搜索树_Binary Search Tree
- Javascript数据结构之禅:二叉查找树(Binary Search Tree)
- Javascript数据结构之禅:平衡二叉树(Balanced Binary Tree, AVL Tree)
- K-D tree 数据结构
- 数据结构 uva 548-Tree
- B-Tree 数据结构
- K-D tree 数据结构
- K-D tree 数据结构
- 两个队列实现一个栈
- ng build -prod
- Java中violate关键字详解(2)?真正了解violate
- HTTPS加密流程
- 软件开发中的英文单词缩写
- 数据结构之tree
- [Unity3D课堂作业] 巡逻兵 GetAwayFromPatrols
- 【牛课堂第二季】第一章 俄国沙皇问题
- 通过Intent向service传递数据
- 网易2017年暑期实习编程题之赶去公司
- ubuntu安装多个版本jdk
- IQKeyboardManager 使用
- Java反射最佳实践
- 深入理解Objective C的ARC机制