在学习数据结构的时候学完栈、队列之后下一个比较难、比较重要的就是树。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:每个节点有零个或多个子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点;除了根节点外,每个子节点可以分为多个不相交的子树。
这是百度的一个词条介绍,一点都不通俗易懂
这就是一个抽象出来的通俗易懂的树的结构图,不过这是一种特殊的树的结构,也是今天我所要操作的树-------二叉树。其实二叉树很好理解,二叉树的每个结点至多只有二棵子树(不存在度大于2的结点)。
这是一些树的相关术语,如果之前没听过树的话,可以简单了解一下
树的结点:包含一个数据元素及若干指向子树的分支;
孩子结点:结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点;
祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
树的深度:树中最大的结点层
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
对这些概念有一个简单的了解就可以。
今天我们就来通过代码创建一个二叉树。今天主要实现的函数有,二叉树的创建,二叉树的三种遍历方式,二叉树的中的结点个数,二叉树中叶子节点的个数,二叉树第K层的叶子结点个数。
首先我们先通过画图软件来画出我们今天要实现的二叉树的抽象图。
然后将他转化成一个数组int a[] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6, '#', '#', '#' };
这是这个二叉树先序遍历的数组,如果下边没有结点是空的话,那就用 '#'来表示。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
BTDataType _data;
}BTNode;
BTNode* BuyBTNode(BTDataType x)
{
BTNode *Node =(BTNode*) malloc(sizeof(BTNode));
Node->_data = x;
Node->_left = NULL;
Node->_right = NULL;
return Node;
}
这是在创建二叉树之前的创建的二叉树结构体,以及给二叉树的空间分配函数。二叉树中包括你的左孩子指针,右孩子指针,以及你当前结点的数值。
BTNode* CreateBTree(BTDataType* a, size_t* pIndex, BTDataType invalid)
{
assert(a);
if (a[*pIndex] == invalid)
return NULL;
BTNode *root = BuyBTNode(a[*pIndex]);
++(*pIndex);
root->_left=CreateBTree(a, pIndex, invalid);
++(*pIndex);
root->_right=CreateBTree(a, pIndex, invalid);
return root;
}
之后是创建二叉树的函数,这里传入了三个参数,第一个是我们取值的数组,通过数组中的值来个二叉树中的结点赋值,pIndex是来访问的数组下标位置,正常的话我们这里传入一个数字就行,为什么要传入一个指针呢?因为看我们的程序,如果你传入的是一个数值。
root->_left=CreateBTree(a, Index++, invalid);这里写成这样,想一下,递归有什么特点?递归是在栈上进行的,栈大家也都清楚,在你当前函数运行结束之后,栈是会进行销毁的,但是你的数组可以一直进行下去的,通过图和数组对比也可以看到1的右孩子5的数组下标已经是8了。有的人说可以将你的index设置成全局或者说静态变量,这种方法在当前阶段也是可取的,但是当你存在多线程的时候全局变量就很容易受到其他线程的影响,导致程序出现一些bug所以这里我们传了指针。第三个参数是invalld也就是非法制也就是你的二叉树的结束值,这里在当前状态下也是可以不传的,但是当前的结束值是'#',如果以后改动的话还是传入一个参数比较方便控制。
这里创建二叉树的函数代码十分简单就是将你当前结点和你的左右孩子结点链接起来就可以了。通过递归来实现最后返回你当前的根节点。
void BTreePrevOrder(BTNode* root)
{
if (root == NULL)
return;
else
{
printf("%d", root->_data);
BTreePrevOrder(root->_left);
BTreePrevOrder(root->_right);
}
}
先序遍历和中序遍历以及后序遍历的代码只是顺序的区别,所以这里我就只介绍一个先序中序后序遍历分别是什么。
通过这个图来介绍,这里的先中后指的是你的根节点的顺序,也就是说,如果是先序遍历,你当前在2位置,那就直接输出2然后再去遍历他的左孩子,右孩子,也就是234,如果要是中序遍历也就是虽然你当前在2位置但是不输出当前数据,会先遍历他的左孩子,然后中序遍历根节点就在中间,之后才是右孩子,所以顺序会是324,如果要是后序遍历,代表着你当前的结点在最后边,也就是先遍历左孩子右孩子然后才是根节点,这里的顺序是342.
如果说这个图的先序遍历,那就是123456,遇到1输出,然后遍历他的左孩子,遇到2输出,然后遇到他的左孩子3输出,下边没孩子,然后返回2,去遍历2的右孩子遇到4输出,然后再返回2,之后开始遍历1的右孩子。
在简单介绍一下这个图的后序遍历,遇到1先不输出往他的左孩子走,走到2,2还有左孩子继续走,走到3,没有左右孩子,直接输出3,然后返回2继续不输出去遍历2的右孩子,4输出,然后返回2之后,再到1,1的右孩子还没有输出所以不能输出1,开始遍历1的右孩子5,因为5有左孩子那就遍历6输出6,返回5去遍历5的右孩子是空返回来,然后输出5,最后返回1输出。后序遍历的顺序就是342651
void BTreeInOrder(BTNode* root)//中序遍历
{
if (root == NULL)
return;
else
{
BTreeInOrder(root->_left);
printf("%d", root->_data);
BTreeInOrder(root->_right);
}
}
void BTreePostOrder(BTNode* root)//后序遍历
{
if (root == NULL)
return;
else
{
BTreePostOrder(root->_left);
BTreePostOrder(root->_right);
printf("%d", root->_data);
}
}
size_t BTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
else
return 1 + BTreeSize(root->_left) + BTreeSize(root->_right);
}
这是用来求二叉树中结点的个数,其实二叉树的基本操作要掌握一个关键词那就是递归,二叉树中用递归来实现程序是一个很方便的。对于这个求节点数,也是一样的,你当前数的结点数就是你当前结点1,加上你的左孩子的结点数,加上你的右孩子的结点数,这个子问题不断的向下扩展,一直到root是空的时候开始返回。
size_t BTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if ((root->_left== NULL) && (root->_right == NULL))
return 1;
return BTreeLeafSize(root->_left) + BTreeLeafSize(root->_right);
}
size_t BTreeKLevelSize(BTNode* root, size_t k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BTreeKLevelSize(root->_left, k - 1) + BTreeKLevelSize(root->_right, k - 1);
}
这两个分别是当前树的叶子结点数,以及树第K层。同样还是递归思想来实现,你的结点是你左孩子的叶子结点个数加上右孩子的叶子结点可数,一直划分下去的子问题。
void TestBinaryTree()
{
int a[] = { 1, 2, 3, '#', '#', 4, '#', '#', 5, 6, '#', '#', '#' };
size_t index = 0;
BTNode* tree = CreateBTree(a, &index, '#');
BTreePrevOrder(tree);
printf("\n");
BTreeInOrder(tree);
printf("\n");
BTreePostOrder(tree);
printf("\n");
printf("BTreeSize%d\n", BTreeSize(tree));
printf("BTreeLeafSize%d\n", BTreeLeafSize(tree));
printf("BTreeKLevelSize?%d\n", BTreeKLevelSize(tree, 2));
}