二叉树详解及二叉树的前序、中序、后序遍历(递归和非递归)

介绍二叉树之前先介绍一下树相关的概念。

树的定义:树是n(n>=0)个有限个数据的元素集合,形状像一颗倒过来的树。

树的概念

节点:结点包含数据和指向其它节点的指针。

根节点:树第一个结点称为根节点。

结点的度:结点拥有的子节点个数。

叶节点:没有子节点的节点(度为0)。

父子节点:一个节点father指向另一个节点child,则child为孩子节点,father为父亲节点

兄弟节点:具有相同父节点的节点互为兄弟节点。

节点的祖先:从根节点开始到该节点所经的所有节点都可以称为该节点的祖先。

子孙:以某节点为根的子树中任一节点都称为该节点的子孙。

树的高度:树中距离根节点最远节点的路径长度。

树的存储结构

struct TreeNode
{
	int _data;//节点值
	TreeNode* _firstChild;//左孩子
	TreeNode* _nextSlbling;//右兄弟
};

树的应用:文件系统---目录树

介绍完树,接下来介绍二叉树。

二叉树定义:二叉树是一棵特殊的树,二叉树每个节点最多有两个孩子结点,分别称为左孩子和右孩子。

二叉树类型:

(1)完全二叉树——若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

(2)满二叉树——高度为N的满二叉树有2^N - 1个节点的二叉树。

(3)平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

二叉树的存储结构

1、数组表示  :用数组方式存储二叉树结构,就是用一组连续的存储单元存储二叉树的数据元素。数组存储法用于完全二叉树的存储表示非常有效,但是表示一般二叉树很不理想,容易造成空间浪费。此外,在一棵树中进行插入和删除时,需要多次移动节点,效率低。链式存储解决了这些缺点。

2、链式存储表示:二叉树的每一个节点至少包括三个域:数据data、左孩子节点指针leftChild、右孩子节点指针rightChild。这种链式结构称为二叉链表。为了便于查找任一节点的双亲节点,可以在节点中加一个双亲指针域parent,被称为三叉链表。

template <class T>
struct BinaryTreeNode
{
	T _value;//节点值
	BinaryTreeNode<T> *_left;//左孩子
	BinaryTreeNode<T> *_right;//右孩子

	BinaryTreeNode(const T& value)
		:_value(value)
		, _left(NULL)
		, _right(NULL)
	{}
};

二叉树性质

(1) 在非空二叉树中,第 i 层的结点总数不超过

, i>=1;

(2) 深度为h的二叉树最多有

个结点(h>=1),最少有h个结点;

(3) 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

(4) 具有n个结点的完全二叉树的深度为

(5)有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:

若I为结点编号则 如果I>1,则其父结点的编号为I/2;

如果2*I<=N,则其左儿子(即左子树的根结点)的编号为2*I;若2*I>N,则无左儿子;

如果2*I+1<=N,则其右儿子的结点编号为2*I+1;若2*I+1>N,则无右儿子。

(6)给定N个节点,能构成h(N)种不同的二叉树。     h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)。

(7)设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i。

二叉树遍历:

前序遍历(先根遍历):(1)先访问根节点;  (2)前序访问左子树;(3)前序访问右子树;

1、递归前序遍历:(1)访问根节点(2)递归遍历左子树(3)递归遍历右子树

2、非递归前序遍历:通过栈实现。

如果根节点不为空,将根节点压入栈,访问根节点;如果根节点的左子树和右子树不为空,压根节点的右子树和左子树,访问根节点的左子树;如果左子树的左孩子不为空,压左孩子的右节点和左节点,访问左子树的左节点,如果为空,访问根节点的右树。

void PrevOrder_NonR()  //前序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		if (_root)
		{
			s.push(_root);
		}
		while (!s.empty())
		{
			BinaryTreeNode<T>* top = s.top();
			cout << top->_value << " ";
			s.pop();

			if (top->_right)
				s.push(top->_right);
			if (top->_left)
				s.push(top->_left);
		}
		cout << endl;
	}

中序遍历:          (1)中序访问左子树;(2)访问根节点;    (3)中序访问右子树;

1、递归中序遍历:(1)递归遍历左子树(2)访问节点(3)递归遍历右子树

2、非递归中序遍历:通过栈实现。

如果根节点不为空,cur指向根节点,压所有左路节点,访问栈顶(最左节点),如果最左节点的右子树不为空,cur指向最左节点的右子树,如果最左节点的右子树不为空,压它的所有左路节点,如果最左节点的右子树为空,访问最左节点的根节点。

void InOrder_NonR() //中序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		BinaryTreeNode<T>* cur = _root;

		while (cur || !s.empty())
		{
			//左节点都入栈
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			if (!s.empty())
			{
				BinaryTreeNode<T>* top = s.top();
				cout << top->_value << " ";
				s.pop();

				if (top->_right)
				{
					cur = top->_right;
				}
			}
		}
		cout << endl;
	}</span>

后序遍历(后根遍历):(1)后序访问左子树;(2)后序访问右子树;(3)访问根节点;

1、递归后序遍历 (1)递归遍历左子树(2)递归遍历右子树 (3)访问节点

2、非递归后序遍历:通过栈实现。

void PostOrder_NonR()  //后序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		BinaryTreeNode<T>* cur = _root;
		BinaryTreeNode<T>* vistedNode = NULL;

		while (cur || !s.empty())
		{
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			// 右为空或者右节点等于上一个访问的节点时,表示左右子树均已访问
			BinaryTreeNode<T>* top = s.top();
			if (top->_right == NULL || top->_right == vistedNode)
			{
				s.pop();
				cout << top->_value << " ";
				vistedNode = top;
			}
			else
			{
				cur = top->_right;
			}
		}
		cout << endl;
	}

层序遍历:         (1)一层层节点依次遍历。       通过队列实现

如果根节点不为空,根节点入队列,判断队列不为空,根节点出队列,如果根节点的左子树和右子树不为空,根节点的左子树和右子树入队列,再访问。

void _LevelOrder(BinaryTreeNode<T>* root)
	{
		queue<BinaryTreeNode<T>* > q;
		if (root)
		{
			q.push(root);
		}
		while (!q.empty())
		{
			BinaryTreeNode<T>* front = q.front();
			cout << front->_value << " ";
			q.pop();

			if (front->_left)
				q.push(front->_left);
			if (front->_right)
				q.push(front->_right);
		}
	}
	

完整代码:

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

template <class T>
struct BinaryTreeNode
{
	T _value;//节点值
	BinaryTreeNode<T> *_left;//左孩子
	BinaryTreeNode<T> *_right;//右孩子

	BinaryTreeNode(const T& value)
		:_value(value)
		, _left(NULL)
		, _right(NULL)
	{}
};

template <class T>
class BinaryTree
{
public:
	BinaryTree()
		:_root(NULL)
	{}
	BinaryTree(char *str)
	{
		_CreateTree(_root,str);
	}
	BinaryTree(BinaryTree<T>& t)
	{
		_root = _CopyTree(t._root);
	}
	/*BinaryTree& operator=(BinaryTree<T>& t)
	{
		if (this != &t)
		{
			_Destroy(t._root);
			_CopyTree(t._root);
		}

		return *this;
	}*/
	BinaryTree& operator=(BinaryTree<T> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~BinaryTree()
	{
		_DestoryTree(_root);
	}

	void _CreateTree(BinaryTreeNode<T>*& root,char*& str)
	{
		if (*str != '#'&&*str != '\0')
		{
			root = new BinaryTreeNode<T>(*str);
			_CreateTree(root->_left,++str);

			if (*str == '\0')
				return;

			_CreateTree(root->_right, ++str);
		}
	}
	void PrevOrder_NonR()  //前序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		if (_root)
		{
			s.push(_root);
		}
		while (!s.empty())
		{
			BinaryTreeNode<T>* top = s.top();
			cout << top->_value << " ";
			s.pop();

			if (top->_right)
				s.push(top->_right);
			if (top->_left)
				s.push(top->_left);
		}
		cout << endl;
	}
	void InOrder_NonR() //中序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		BinaryTreeNode<T>* cur = _root;

		while (cur || !s.empty())
		{
			//左节点都入栈
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			if (!s.empty())
			{
				BinaryTreeNode<T>* top = s.top();
				cout << top->_value << " ";
				s.pop();

				if (top->_right)
				{
					cur = top->_right;
				}
			}
		}
		cout << endl;
	}
	void PostOrder_NonR()  //后序遍历(非递归)
	{
		stack<BinaryTreeNode<T>* > s;
		BinaryTreeNode<T>* cur = _root;
		BinaryTreeNode<T>* vistedNode = NULL;

		while (cur || !s.empty())
		{
			while (cur)
			{
				s.push(cur);
				cur = cur->_left;
			}
			// 右为空或者右节点等于上一个访问的节点时,表示左右子树均已访问
			BinaryTreeNode<T>* top = s.top();
			if (top->_right == NULL || top->_right == vistedNode)
			{
				s.pop();
				cout << top->_value << " ";
				vistedNode = top;
			}
			else
			{
				cur = top->_right;
			}
		}
		cout << endl;
	}
	void Size()//节点个数
	{
		_Size(_root);
	}
	void LeafNodeNum()//叶子节点个数
	{
		_LeafNodeNum(_root);
	}
	void Depth()  //深度
	{
		_Depth(_root);
	}
	void KLevelNodeNum()//第K层节点个数
	{
		_KLevelNodeNum(_root);
	}
	void PrevOrder()//递归前序
	{
		_PrevOrder(_root);
		cout << endl;
	}
	void InOrder()//递归中序
	{
		_InOrder(_root);
		cout << endl;
	}
	void PostOrder()//递归后序
	{
		_PostOrder(_root);
		cout << endl;
	}
	void LevelOrder() //层序遍历
	{
		_LevelOrder(_root);
		cout << endl;
	}
protected:
	void _DestoryTree(BinaryTreeNode<T>* root)
	{
		if (root)
		{
			_DestoryTree(root->_left);
			_DestoryTree(root->_right);

			delete root;
			root = NULL;
		}
	}
	BinaryTreeNode<T>* _CopyTree(BinaryTreeNode<T>* root)
	{
		BinaryTreeNode<T>* copyRoot = NULL;
		if (root)
		{
			copyRoot = new BinaryTreeNode<T>(root->_value);
			copyRoot->_left = _CopyTree(root->_left);
			copyRoot->_right = _CopyTree(root->_right);
		}
		return copyRoot;
	}
	int _Size(BinaryTreeNode<T>* root)
	{
		if (root == NULL)
		{
			return 0;
		}
		if (root->_left == NULL&&root->_right == NULL)
		{
			return 1;
		}
		else
		{
			return 1 + _Size(root->left) + _Size(root->_right);
		}
	}
	int _LeafNodeNum(BinaryTreeNode<T>* root)
	{
		if (root == NULL)
		{
			return 0;
		}
		else if (root->_left == NULL&&root->_right == NULL)
		{
			return 1;
		}
		else
		{
			int leftNum = _LeafNodeNum(root->_left);
			int rightNum = _LeafNodeNum(root->_right);
			return (leftNum + rightNum);
		}
	}
	int _Depth(BinaryTreeNode<T>* root)
	{
		if (root == NULL)
			return 0;
		int leftDepth = _Depth(root->_left);
		int rightDepth = _Depth(root->_right);

		return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);
	}
	void _KLevelNodeNum(BinaryTreeNode<T>* root,int k)
	{
		if (root == NULL || k < 1)
		{
			return 0;
		}
		else if (k == 1)
		{
			return 1;
		}
		else
		{
			int leftNum = _LeafNodeNum(root->_left,k-1);// 左子树中k-1层的节点个数
			int rightNum = _LeafNodeNum(root->_right,k-1);// 右子树中k-1层的节点个数
			return (leftNum + rightNum);
		}
	}
	void _PrevOrder(BinaryTreeNode<T>* root)
	{
		if (root)
		{
			cout << root->_value << " ";

			if (root->_left)
				_PrevOrder(root->_left);
			if (root->_right)
				_PrevOrder(root->_right);
		}
	}
	void _InOrder(BinaryTreeNode<T>* root)
	{
		if (root)
		{
			if (root->_left)
				_InOrder(root->_left);
			cout << root->_value << " ";
			if (root->_right)
				_InOrder(root->_right);
		}
	}
	void _PostOrder(BinaryTreeNode<T>* root)
	{
		if (root)
		{
			if (root->_left)
				_PostOrder(root->_left);

			if (root->_right)
				_PostOrder(root->_right);

			cout << root->_value << " ";
		}
	}
	void _LevelOrder(BinaryTreeNode<T>* root)
	{
		queue<BinaryTreeNode<T>* > q;
		if (root)
		{
			q.push(root);
		}
		while (!q.empty())
		{
			BinaryTreeNode<T>* front = q.front();
			cout << front->_value << " ";
			q.pop();

			if (front->_left)
				q.push(front->_left);
			if (front->_right)
				q.push(front->_right);
		}
	}

private:
	BinaryTreeNode<T> *_root;
};

int main()
{
	char* str = "12#3##45#6#7##8";
	BinaryTree<char> bt1(str);

	bt1.PrevOrder();
	bt1.PrevOrder_NonR();
	bt1.InOrder();
	bt1.InOrder_NonR();
	bt1.PostOrder();
	bt1.PostOrder_NonR();
	bt1.LevelOrder();

	//cout<<"Size:"<<bt1.Size()<<endl;
	//cout<<"Depth:"<<bt1.Depth()<<endl;

	BinaryTree<char> bt2(bt1);
	bt2.PrevOrder_NonR();

	BinaryTree<char> bt3;
	bt3 = bt1;
	bt3.PrevOrder_NonR();
	return 0;
}

时间: 2024-10-10 13:12:16

二叉树详解及二叉树的前序、中序、后序遍历(递归和非递归)的相关文章

二叉树详解

二叉树详解: 采用递归的方式进行遍历,这样做的好处时代码十分简洁. 顺序存储:数组 链表存储:链表 typedef struct bi_t_node{ telemetype data; struct bi_t_node *lchild, *rchild; }bi_t_node, *bi_tree; 前序遍历:第一次到达节点时,自左向右 中序遍历:第二次到达节点时,自左向右 后序遍历:第三次到达节点时,自左向右 层序遍历:自上而下,,自左向右 示例: 1 /* 二叉树前序遍历递归算法 */ 2 v

算法实验-二叉树的创建和前序-中序-后序-层次 遍历

对于二叉树的创建我是利用先序遍历的序列进行创建 能够对于树节点的内容我定义为char型变量 '0'为空,即此处的节点不存在 头文件 Tree.h //链式二叉树的头文件 #pragma once #include<iostream> #include<queue> using namespace std; class BinaryTreeNode { public: char data; BinaryTreeNode *leftChild,*rightChild; BinaryTr

二叉树的前序中序后序遍历相互求法

二叉树的前中后序遍历,他们的递归非递归.还有广度遍历,参见二叉树的前中后序遍历迭代&广度遍历和二叉树的前中后序遍历简单的递归 现在记录已知二叉树的前序中序后序遍历的两个,求另外一个.一般,这两个中一定有中序遍历. 1.已知前序和中序,求后序遍历: 前序:ABDECFG  中序:DBEAFCG 思路简单:前序的第一个节点就是根节点, 中序中找到根节点的位置,根节点之前是其左子树,之后是右子树   按此顺序,依次在左子树部分遍历,右子树部分遍历 C++ 代码: TreeNode *BinaryTre

经典白话算法之二叉树中序前序序列(或后序)求解树

这种题一般有二种形式,共同点是都已知中序序列.如果没有中序序列,是无法唯一确定一棵树的. <1>已知二叉树的前序序列和中序序列,求解树. 1.确定树的根节点.树根是当前树中所有元素在前序遍历中最先出现的元素. 2.求解树的子树.找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树.若根节点左边或右边为空,则该方向子树为空:若根节点 边和右边都为空,则根节点已经为叶子节点. 3.递归求解树.将左子树和右子树分别看成一棵二叉树,重复1.2.3步,直到所有的节点完成定

二叉树的前序、中序、后序遍历(递归、非递归)实现

本文部分来源于CSDN兰亭风雨大牛的原创.链接为http://blog.csdn.net/ns_code/article/details/12977901 二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的.二叉树有前.中.后三种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但其开销也比较大,而若采用非递归方法实现三种遍历,则要用栈来模拟实现(递归也是用栈实现的).下面先简要介绍三种遍历方式的递归实现,再详细介绍三种遍

已知二叉树前、中序遍历,求后序 / 已知二叉树中、后序遍历,求前序

void solve(int start,int end,int root) { // 前序和中序 -> 后序 // 每次调用solve()函数,传入pre-order的start,end,root if (start > end) // 递归边界 return; int i = start; while (i < end && in.at(i) != pre.at(root)) // 找到左右子树的分割点 i++; solve(start, i - 1, root +

二叉树的前序中序后序遍历-非递归-使用同一段代码实现

树的遍历通常使用递归,因为它的实现更简单,代码也更容易理解. 但在面试,或者特殊的情境中会使用到迭代算法(非递归). 此时需要使用栈去模拟函数栈调用过程. 本文将给出一段代码去实现这三种遍历 相比于传统的方式:前序遍历,中序遍历,后序遍历,使用不同的方式代码去实现,并且后续遍历更为难理解一些 可拓展性更好(比如N叉树的遍历),也更容易理解 考虑,对于一个函数栈,它除了存储了一些变量和指令,同时还存储了当前执行位置. 对于树的遍历,无非为:t->val,t->left ,t->right

日常学习随笔-用链表的形式实现普通二叉树的新增、查找、遍历(前、中、后序)等基础功能(侧重源码+说明)

一.二叉树 1.二叉树的概念 二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree),其次序不能任意颠倒. 2.性质 (1)若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0): (2)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1). (空树的高度为-1): (3)对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n,

Java数据结构系列之——树(4):二叉树的中序遍历的递归与非递归实现

package tree.binarytree; import java.util.Stack; /** * 二叉树的中序遍历:递归与非递归实现 * * @author wl * */ public class BiTreeInOrder { // 中序遍历的递归实现 public static void biTreeInOrderByRecursion(BiTreeNode root) { if (root == null) { return; } biTreeInOrderByRecursi