树是一种非线性的数据结构。要理解树的概念及其术语的含义,用一个例子说明是最好的方法。就比如下图就是一棵树,它是若干节点的集合。是由唯一的根(A)和若干互不相交的子树。就比如说,A,D,H,M,I,J这六个结点组成的树就是一颗子树组成的。其中,每一棵子树又是一棵树,也是由唯一的根结点和若干棵互不相交的子树组成的。由此而知,树的定义是递归的,也就是在树的定义中又用到了树的定义。要注意的是,树的结点数目可以为0,当为0时,这棵树称为一颗空树。
刚刚那棵树的A,B,C等都是结点,结点不仅包含数据元素,而且包含指向子树的分支。也就是说A的结点不仅包含数据元素A,而且包含三个指向子树的指针。
结点拥有的子树个数或者分支的个数称为结点的度。就比如说A结点有三棵子树,所以A结点的度为3
树中各结点度的最大值。比如说树的结点度最大为3(就像刚才那棵树的A结点),最小为0(就像是K,L,M等)
叶子结点又叫做终端结点,指度为0的结点,就比如刚才那棵树K,L,F,G,M,I,J都是叶子结点
非终端结点又叫做分支结点,指度不为0的结点,刚才那棵树除了叶子节点外,其他的都是非终端结点。除了根节点之外的非终端结点,也叫做内部结点。就像B,C,D,E,H结点都是内部结点
结点的子树的根,就比如说刚才那棵树的B,C,D都属于A的孩子
与孩子的定义对应,如B,C,D结点都是双亲A
同一个双亲的孩子之间互为兄弟。就比如说B,C,D就互为兄弟。因为他们都是A的孩子
从根到某路径上的所有结点都是这个结点的祖先。比如说M的祖先是A,D,H。因为从A到M的路径是 A -> D -> H -> M
以某结点为根的子树中所有结点都是该结点的子孙。就比如说B的子孙为E,F,K,L
从根开始,根为第一层,根的孩子为第二层,根的孩子的孩子为第三层以此类推。而高度的话呢,其实就是树中结点的最大层次。就比如说刚刚那棵树最大层次是4层所以高度是4
双亲在同一层的结点互为堂兄弟。如G和H互为堂兄弟,因为G的双亲是C,H的双亲是D,C和D在同一层上。
树中结点的子树从左到右都是有次序的,不能交换。这样的树叫做有序树
树中结点的子树没有顺序,可以任意交换,这样的树叫做无序树
丰满树也就是理想平衡树,要求除最底层外,其他层都是满的。
若干棵互不相交的树的集合。例子中如果把A去掉,剩下的三棵子树互不相交。它们组成一个森林
树的顺序存储结构中最简单直观的是双亲存储结构,用整形一维数组即可实现。最简单的定义方法可以是int tree[maxsize],也就是用一个数组就可以存储一棵树的信息
就比如说上面这张图,用数组的下标来表示树中的结点,然后我们用数组元素的内容来表示该结点的双亲结点。这样有了结点(下标)以及结点之间的关系(内容),就可以表示一棵树了。
在这张图中,下标5上的内容为3,就是说5的双亲结点为3。下标1的内容为-1,就表示是根节点了~。这种定义主要就是当知道一个结点之后,就很容易找到双亲结点。
注意:这里介绍的双亲存储结构是高度简化的形式,实际应用中肯定是不会这么做的。
树的链式存储最常用的有以下两种:
孩子存储结构
孩子兄弟存储结构
在理解了树的定义之后,二叉树就很好理解了。将一般的树满足
每个结点最多只能有两颗子树,也就是说二叉树中结点的度只能为0、1、2。
二叉树有左子树和右子树,并且不能颠倒。这样就得到二叉树了~
空二叉树,只有根节点,只有左子树,只有右子树,既有左子树也有右子树
在一颗二叉树中,如果所有分支结点都有左孩子和右孩子结点。并且叶子结点都集中在二叉树最下面一层。而这样的二叉树称为满二叉树。约定编号从1开始,从上到下,从左到右进行
如果对一颗深度为k,有n个结点的二叉树进行编号后,各结点的编号与深度为k的满二叉树中相同位置上的结点的编号均相同,那么这颗二叉树就是完全二叉树
顺序存储结构就是用一个数组来存储一颗二叉树,这种存储方式最适合完全二叉树。如果用来存储一般的二叉树的话,就会浪费大量的空间。将完全二叉树的结点按照编号依次存入一个一维数组中,也就完成了一个二叉树的顺序存储。
顺序存储结构显然有一个很大的局限性,不便于存储任意形态的二叉树。观察二叉树的状态可以发现是一个根节点与两棵子树之间的关系。因此设计出了含有一个数据域和两个指针域的链式存储结构。
typedef struct BinaryNode {
char data; //数据域
struct BinaryNode* left; //指针域 左孩子
struct BinaryNode* right; //指针域 右孩子
}Tree;
#include
#include
typedef struct BinaryNode {
char data; //数据域
struct BinaryNode* left; //指针域 左孩子
struct BinaryNode* right; //指针域 右孩子
}Tree;
//赋值
Tree* getTree(char data) {
Tree* tree = (Tree*)malloc(sizeof(Tree));
tree->data = data;
tree->left = NULL;
tree->right = NULL;
return tree;
}
//先序遍历
void preOrder(Tree* root) {
if (root != NULL) {
printf("%c ", root->data);
preOrder(root->left);
preOrder(root->right);
}
}
//中序遍历
void inOrder(Tree* root) {
if (root != NULL) {
inOrder(root->left);
printf("%c ", root->data);
inOrder(root->right);
}
}
//后序遍历
void postOrder(Tree* root) {
if (root != NULL) {
postOrder(root->left);
postOrder(root->right);
printf("%c ", root->data);
}
}
int main(int argc, char* argv[]) {
Tree* tree = (Tree*)malloc(sizeof(Tree));
tree = getTree('A');
tree->left = getTree('B');
tree->right = getTree('C');
tree->left->left = getTree('D');
tree->right->left = getTree('E');
tree->right->right = getTree('F');
//先序遍历
printf("先序遍历:\t");
preOrder(tree);
//中序遍历
printf("\n中序遍历:\t");
inOrder(tree);
//后序遍历
printf("\n后序遍历:\t");
postOrder(tree);
return 0;
}