您的位置:首页 > 理论基础 > 数据结构算法

数据结构之遍历二叉树

2018-02-24 20:19 267 查看
遍历二叉树
在二叉树的一些应用中,常常要求在树中查找具有某种特征的结点。这就提出遍历二叉树的问题,即如何按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。遍历对线性结构来说,是一个容易解决的问题。而对二叉树则不然,由于二叉树是一种非线性结构,每个结点都可能有两颗子树,因而需要寻找一种规律,以便使二叉树上的结点能排列在一个线性队列上,从而便于遍历。
回顾二叉树的递归定义可知,二叉树是由3个基本单元组成:根结点、左子树和右子树。因此,若能依次遍历这三部分,便是遍历了整个二叉树。例如以L、D、R分别表示遍历左子树、访问根结点和遍历右子树,则可有DLR、LDR、LRD、DRL、RDL、RLD这6种遍历二叉树的方案。若限定先左后右、则只有3种情况,分别称之为前(根)序遍历、中(根)序遍历和后(根)序遍历。基于二叉树的递归定义,可得下述遍历二叉树的递归算法定义。
1. 前序遍历二叉树的操作:
若二叉树为空,则空操作返回;否则
1.1.访问根结点中的数据;
1.2.前序遍历左子树;
1.3.前序遍历右子树。



前序遍历二叉树代码如下:// 前序遍历二叉树
void pre_order_traversal(BTreeNode* root)
{
// 二叉树不为空
if( root != NULL )
{
// 访问根结点中的数据并打印
printf("%c, ", ((struct Node*)root)->v);
// 前序遍历左子树
pre_order_traversal(root->left);
// 前序遍历右子树
pre_order_traversal(root->right);
}
}2.中序遍历二叉树的操作:
若二叉树为空,则空操作返回;否则
2.1.中序遍历左子树;
2.2.访问根结点中的数据;
2.3.中序遍历右子树。



中序遍历二叉树代码如下:// 中序遍历二叉树
void middle_order_traversal(BTreeNode* root)
{
// 二叉树不为空
if( root != NULL )
{
// 中序遍历左子树
middle_order_traversal(root->left);
// 访问根结点中的数据并打印
printf("%c, ", ((struct Node*)root)->v);
// 中序遍历右子树
middle_order_traversal(root->right);
}
}
3.后序遍历二叉树的操作:
若二叉树为空,则空操作返回;否则
3.1.中序遍历左子树;
3.2.中序遍历右子树;
3.3.访问根结点中的数据。



后序遍历二叉树代码如下:// 后序遍历二叉树
void post_order_traversal(BTreeNode* root)
{
// 二叉树不为空
if( root != NULL )
{
// 后序遍历左子树
post_order_traversal(root->left);
// 后序遍历右子树
post_order_traversal(root->right);
// 访问根结点中的数据并打印
printf("%c, ", ((struct Node*)root)->v);
}
}从上述二叉树遍历的定义可知,3种遍历算法之不同处仅在于访问根结点和遍历左、右子树的先后关系。
对二叉树进行遍历的搜索路径除了上述前序、中序和后序外,还可以从上到下、从左到右按层次进行。
4.层次遍历二叉树的操作:
若二叉树为空,则空操作返回;否则
4.1. 访问根结点中的数据;
4.2. 访问第二层所有结点的数据;
4.3. 访问第三层所有结点的数据;
4.4. 。。。。。。
显然,层次遍历二叉树不能使用递归的方法,那我们应该怎么办你?我们可以应用队列来实现,队列相关内容参考数据结构之链式队列的优化。首先将根结点入列,然后循环查找队列中是否有元素,有元素就出列一次,再将出列元素代表的结点的左右子树入列,周而复始,直至队列中没有元素为止。实现代码如下:// 层次遍历二叉树
void level_order_traversal(BTreeNode* root)
{
// 二叉树不为空
if( root != NULL )
{
// 创建队列
LinkQueue* queue = LinkQueue_Create();
// 队列创建成功
if( queue != NULL )
{
// 将二叉树根结点入列
LinkQueue_Append(queue, root);
// 队列长度不为零,即队列中有元素
while( LinkQueue_Length(queue) > 0 )
{
// 将结点元素出列
struct Node* node = (struct Node*)LinkQueue_Retrieve(queue);
// 打印出列的元素
printf("%c, ", node->v);
// 将结点的左右子树入列
LinkQueue_Append(queue, node->header.left);
LinkQueue_Append(queue, node->header.right);
}
}
// 销毁队列
LinkQueue_Destroy(queue);
}
}验证代码如下:int main(int argc, char *argv[])
{
// 创建二叉树
BTree* tree = BTree_Create();
// 定义二叉树内容
struct Node n1 = {{NULL, NULL}, 'A'};
struct Node n2 = {{NULL, NULL}, 'B'};
struct Node n3 = {{NULL, NULL}, 'C'};
struct Node n4 = {{NULL, NULL}, 'D'};
struct Node n5 = {{NULL, NULL}, 'E'};
struct Node n6 = {{NULL, NULL}, 'F'};
// 插入二叉树
BTree_Insert(tree, (BTreeNode*)&n1, 0, 0, 0);
BTree_Insert(tree, (BTreeNode*)&n2, 0x00, 1, 0);
BTree_Insert(tree, (BTreeNode*)&n3, 0x01, 1, 0);
BTree_Insert(tree, (BTreeNode*)&n4, 0x00, 2, 0);
BTree_Insert(tree, (BTreeNode*)&n5, 0x02, 2, 0);
BTree_Insert(tree, (BTreeNode*)&n6, 0x02, 3, 0);
// 显示整体二叉树
printf("Full Tree: \n");

BTree_Display(tree, printf_data, 4, '-');
// 测试前序遍历二叉树功能
printf("Pre Order Traversal:\n");

pre_order_traversal(BTree_Root(tree));

printf("\n");
// 测试中序遍历二叉树功能
printf("Middle Order Traversal:\n");

middle_order_traversal(BTree_Root(tree));

printf("\n");
// 测试后序遍历二叉树功能
printf("Post Order Traversal:\n");

post_order_traversal(BTree_Root(tree));

printf("\n");
// 测试层次遍历二叉树功能
printf("Level Order Traversal:\n");

level_order_traversal(BTree_Root(tree));

printf("\n");
// 销毁二叉树
BTree_Destroy(tree);

return 0;
}显然,遍历二叉树的算法中的基本操作是访问结点,则不论按哪一种次序进行遍历,对含有n个结点的二叉树,其时间复杂度均为O(n)。所需辅助空间为遍历过程中栈的最大容量,即树的深度,在最坏情况下为n,则空间复杂度也为O(n)。
通过以上代码发现,二叉树仅仅比单链表多了一个指针域,但其遍历算法的种类却增加了很多 。递归定义的数据结构采用递归的算法进行遍历往往能达到简单可靠的效果 。

遍历二叉树代码:遍历二叉树C实现代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: