【数据结构学习】-二叉树的概念与遍历

链表、栈或队列都是线性结构,包含一个数据元素序列。而二叉树是一种层次结构。一颗二叉树要么为空,要么由一个数据元素(称为跟)和两颗独立的二叉树(称为左子树和右子树)。某个节点的左(右)子树的根节点称为该节点的左(右)孩子节点。两颗子树均为空的节点称为叶子节点。

搜索二叉树:其左子树任意节点的值都小于此节点的值,其右子树中任意节点的值都大于此节点的值。

完全二叉树:如果一颗二叉树除最后一层外都保证是满的,且若最后一层不满,所有节点均位于最左边,则称为完全二叉树。

平衡二叉树:它是一颗空树,或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。

树的遍历:就是按某种特定次序不遗漏、不重复地访问树的每个节点的过程。访问次序有很多种,主要有中序遍历、前序遍历、后序遍历、深度优先遍历和广度优先遍历几种方式。中序遍历:先访问当前节点的左子树,然后访问当前节点,最后访问当前节点的右子树。后序遍历:先访问当前节点的左子树,再访问当前节点的右子树,最后访问当前节点。前序遍历:先访问当前节点,然后访问当前节点的左子树,最后访问当前节点的右子树。对于二叉树而言,深度优先遍历和先序遍历是相同的。广度优先遍历:首先访问根节点,然后由左至右访问根节点的孩子节点,再由左至右访问跟节点的孙子节点,依次类推。

1)先序遍历

递归方式实现:按照“根节点-左孩子-右孩子”顺序遍历,代码如下:

void preorder(TreeNode *root)		//前序递归实现
{
	if (root==NULL)
	{
		return;
	}
	cout<<root->val<<"\t";
	preorder(root->left);
	preorder(root->right);
}

非递归方式实现:利用栈的特性来实现。实现过程:对于任一节点P,如果P不为空,将P压入栈中。取出栈顶元素赋值给P,输出P节点对应的值。如果P的右孩子节点不为空,则将右孩子节点压入栈;如果P的左孩子节点不为空,则将左孩子节点压入栈。如此循环,直至栈中元素为空。代码如下:

void preorder_stack(TreeNode *root)		//前序非递归实现
{
	if (root==NULL)
	{
		return;
	}
	stack<TreeNode *> nodes;
	TreeNode *temp;
	nodes.push(root);
	while(!nodes.empty())
	{
		temp=nodes.top();
		nodes.pop();
		cout<<temp->val<<"\t";
		if(temp->right!=NULL)
		{
			nodes.push(temp->right);
		}
		if(temp->left!=NULL)
		{
			nodes.push(temp->left);
		}
	}
}

2)中序遍历

递归实现:按照“左孩子-根节点-右孩子”顺序遍历,代码如下:

void inorder(TreeNode *root)		//中序递归实现
{
	if (root==NULL)
	{
		return;
	}
	inorder(root->left);
	cout<<root->val<<"\t";
	inorder(root->right);
}

非递归实现:利用栈的特性来实现。实现过程:对于任一节点P,若其左孩子不为空,则将P压入栈并把P的左孩子置为P,然后对当前节点P再进行相同的处理。若其左孩子不为空,则取栈顶元素并进行出栈操作,然后将P置为栈顶元素的右孩子节点。如此循环,直至P为NULL且栈为空为止。代码如下:

void inorder_stack(TreeNode *root)		//中序非递归实现
{
	if (root==NULL)
	{
		return;
	}
	stack<TreeNode *> nodes;
	TreeNode *temp=root;
	while(temp!=NULL || !nodes.empty())
	{
		while(temp!=NULL)
		{
			nodes.push(temp);
			temp=temp->left;
		}
		if(!nodes.empty())
		{
			temp=nodes.top();
			nodes.pop();
			cout<<temp->val<<"\t";
			temp=temp->right;
		}
	}
}

3)后序遍历

递归实现:按照“左孩子-右孩子-根节点”顺序遍历,代码如下:

void postorder(TreeNode *root)		//后序递归实现
{
	if(root==NULL)
	{
		return;
	}
	postorder(root->left);
	postorder(root->right);
	cout<<root->val<<"\t";
}

非递归实现:后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。下面介绍一种网上看到比较容易理解的思路。实现过程如下:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。代码如下:

void postorder_stack(TreeNode *root)		//后序非递归实现
{
	if(root==NULL)
	{
		return;
	}
	stack<TreeNode *> nodes;
	TreeNode *curNode;
	TreeNode *preNode=NULL;
	nodes.push(root);
	while(!nodes.empty())
	{
		curNode=nodes.top();
		if((curNode->left==NULL && curNode->right==NULL) || (preNode!=NULL &&(preNode==curNode->left || preNode==curNode->right)))
		{
			cout<<curNode->val<<"\t";
			nodes.pop();
			preNode=curNode;
		}
		else
		{
			if(curNode->right!=NULL)
			{
				nodes.push(curNode->right);
			}
			if(curNode->left!=NULL)
			{
				nodes.push(curNode->left);
			}
		}
	}
}

4)深度优先遍历

对于二叉树而言,深度优先遍历与先序遍历是相同的,不再赘述。

5)广度优先遍历

利用队列的特性来实现。实现过程:对于任一节点P,如果P不为空,将P压入队列中。取出队首元素赋值给P,输出P节点对应的值。如果P的左孩子节点不为空,则将左孩子节点压入队列;如果P的右孩子节点不为空,则将右孩子节点压入队列。如此循环,直至队列中元素为空。代码如下:

void width(TreeNode *root)		//广度优先遍历
{
	if(root==NULL)
	{
		return;
	}
	queue<TreeNode *> nodes;
	nodes.push(root);
	TreeNode *temp;
	while(!nodes.empty())
	{
		temp=nodes.front();
		nodes.pop();
		cout<<temp->val<<"\t";
		if(temp->left!=NULL)
		{
			nodes.push(temp->left);
		}
		if(temp->right!=NULL)
		{
			nodes.push(temp->right);
		}
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 12:50:50

【数据结构学习】-二叉树的概念与遍历的相关文章

【算法与数据结构】二叉树的 中序 遍历

前一篇写了二叉树的先序遍历,本篇记录一下二叉树的中序遍历,主要是非递归形式的中序遍历. 由于距离上篇有好几天了,所以这里把二叉树的创建和存储结构也重复的写了一遍. 二叉树如下 二叉树的存储方式依然是二叉链表方式,其结构如下 typedef struct _tagBinTree { unsigned char value; struct _tagBinTree* left; struct _tagBinTree* right; }BinTree, *PBinTree; 先序递归形式的创建二叉树代码

数据结构学习笔记(四)---遍历二叉树

遍历二叉树   二叉树是一种非线性的数据结构.所谓的遍历二叉树就是按某种顺序访问二叉树中的每个节点,要求每个节点被访问一次且仅一次. 遍历操作实际上是将非线性结构线性化过程,其结果为线性序列.   二叉树的操作 (1)先序遍历---结束的条件是二叉树是否为空 TLR 先访问根节点: 再先序访问左子树: 再先序访问右子树. (2)中序遍历---结束的条件是二叉树是否为空  LTR 先中序遍历左子树: 再访问根节点: 再中序遍历右子树. (3)后序遍历---结束的条件是二叉树是否为空  LRT 先后

【数据结构】二叉树链式结构--遍历二叉树

树是n(n>=0)个结点的有限集. 在任一颗非空数中: 1)有且仅有一个根节点 2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集,其中每一个集合也是一颗树,并且成为根的子树. 二叉树:n个结点的有限集合,该集合或者为空集(空),或者由一个根节点和两棵互不相交的.分别称为根结点的左子树和右子树的二叉树组成. 二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,我们称这样的链表为二叉链表. 代码: #include "string.h" #inc

数据结构学习---二叉树简单实现

想必二叉树大家都很熟悉.结点有左右孩子,二叉树非常使用,编写二叉树和链表差不多,多多运用迭代就能很好的解决问题,但是这仅仅限于在量比较小的情况下,非递归的代码执行效率大家有目共睹. #ifndef _TREENODE_H_ #define _TREENODE_H_ typedef int Elemtype; typedef struct treeNode{ Elemtype tRoot; struct treeNode * pLight; struct treeNode * pRight; }t

【自考】数据结构之二叉树遍历

什么是数据结构? 首先看看维基百科的定义:在计算机科学或信息科学中,数据结构(英语:data structure)是计算机中存储.组织数据的方式.通常情况下,精心选择的数据结构可以带来最优效率的算法. 课本中的定义:指一组相互之间存在一种或多种特定关系的数据的组织方式和它们在计算机中的存储方式,以及定义在该组数据上的一组操作. 有哪些内容?    直接看图了解数据结构的知识点,如下: 不管是算法还是逻辑结构,都是用数据说话的,所以要先明白了数据的基本概念.大的联系图中已给出,小的知识点需要我们去

数据结构学习之第7章 树和二叉树

数据结构学习之第7章 树和二叉树 0x7.1.1 树的基本概念 ?1.树的定义 ? 树是由n(n>=0)个结点(或元素)组成的有限集合(记为T) ? 如果n>0,这n个结点中有且仅有一个结点作为树的根结点,简称为根,其余结点可分为m(m>=0)个互不相交的有限集\[T_{1}T_{2}\cdots T_{m}\],其中每个子集又是一棵符合定义的子树,称为根结点的子树. 知识点:由树的定义我们可以看出来树的结构是递归的 ?2.树的逻辑表示法 ? 1.树形表示法 ? 2.文氏图表示法 ? 3

数据结构之二叉树的遍历汇总

声明:小弟写博客不久,主要是边上班边学习边写博客,如果错误,望各位包涵并指导. 二叉树是一种常用的非线性数据结构,二叉树是由一个根节点和称为根的左.右子树的两颗互不相交的二叉树构成.二叉树具有一些特殊的性质,如第i层上最多有2^(i-1)个结点.二叉树的链式存储结构如下: typedef struct BTNode { char data; //字符型数据; struct BTNode* lChild,*rChild; //左右子结点的指针; }BTNode,*BiTree; data为二叉树中

【数据结构】树与树的表示、二叉树存储结构及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树与哈夫曼编码、集合及其运算

1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找--方法一:顺序查找(时间复杂度O(n)) int SequentialSearch(St

小朋友学数据结构(3):二叉树的建立和遍历

小朋友学数据结构(3):二叉树的建立和遍历 一.基本概念 BinaryTree.png 二叉树:每个结点的子结点个数不大于2的树,叫做二叉树. 根结点:最顶部的那个结点叫做根结点,根结点是所有子结点的共同祖先.比如上图中的"7"结点就是根结点. 子结点:除了根结点外的结点,都叫子结点. 叶子结点:没有子结点的结点,叫做叶子结点.比如上图中的"1"结点."5"结点和"11"结点. 二叉树的遍历,有三种: (1)前序遍历:先遍历根