二叉树的四种遍历(基础知识)

二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合可以为空,或者由一个根几点和俩颗互不相交的子树组成

 

 

特点

  • 每个结点最多有两棵子树,故不存在度大于2的结点(要和图的度分清,图是有出度和入度之分的,这里的度就是它的儿子结点)
  • 左子树和右子树是有顺序的,次序不能任意颠倒
  • 即使树中某结点只有一棵子树,也要区分是左子树还是右子树

 

 

五种基本形态

  • 空二叉树
  • 只有一个根节点
  • 根节点只有左子树
  • 根节点只有右子树
  • 根节点既有左子树又有右子树

 

特殊二叉树

  • 斜树:所有结点都只有左子树的二叉树叫左斜树,对应有右斜树,统称为斜树

  • 满二叉树:所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上的二叉树

    • 叶子只能出现在最下一层,否则不平衡
    • 非叶子节点的度一定是2
    • 同样深度的二叉树,满二叉树的结点个数最多,叶子数最多

    binary_tree_full

 

完全二叉树: 对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树

  • 叶子结点只能出现在最下两层
  • 最下层的叶子一定集中在左部连续位置
  • 倒数二层,若有叶子结点,一定都在右部连续位置
  • 如果结点度为1,则该结点只有左孩子,即不存在只有右子树的情况

binary_tree_complete

二叉树的性质

  • 在二叉树的第i层上至多有2i−12i−1个结点(i≥1)(i≥1)
  • 深度为k的二叉树至多有2k−12k−1个结点(k≥1)(k≥1)
  • 对任何一棵二叉树T, 如果其终端结点数为n0n0, 度为2的结点树为n2n2, 则n0=n2+1n0=n2+1
  • 具有n个结点的完全二叉树的深度为⌊log2n⌋+1⌊log2n⌋+1 (⌊x⌋表示不大于x的最大整数)(⌊x⌋表示不大于x的最大整数)
  • 如果对一棵具有n个结点的完全二叉树(其深度为⌊log2n⌋+1⌊log2n⌋+1)的结点按层序编号(从第1层到第⌊log2n⌋+1⌊log2n⌋+1层,每层从左到右),对任一结点 i(1≤i≤n)i(1≤i≤n)有: 
    • 如果 i = 1, 则结点i是二叉树的根,无双亲;如果i>1i>1, 则其双亲是结点⌊i/2⌋⌊i/2⌋
    • 如果 2i>n2i>n, 则结点i无左孩子(结点i为叶子结点); 否则即2i≤n2i≤n时其左孩子是结点2i2i
    • 如果2i+1>n2i+1>n, 则结点i无右孩子;否则2i+1≤n2i+1≤n时, 其右孩子是结点2i+1

 

二叉树的存储结构

二叉树顺序存储结构

顺序存储结构一般只用于 完全二叉树,否则会造成空间浪费

binary_tree_sequential_storage

二叉链表(链式存储结构)

二叉树每个结点最多有两个孩子,故结点结构为一个数据域和两个指针域

typedef struct node;
typedef node *tree;
struct node
{
	int data;
	tree L, R;
};

 

遍历二叉树

二叉树的遍历(traversing binary tree)是指从根节点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。 由前序和后序无法得到唯一二叉树

前序遍历

规则是若二叉树为空,则空操作返回,否则先返回根节点,然后前序遍历左子树,再前序遍历右子树。下图遍历顺序为ABDGHCEIF

binary_tree_pre_order_traverse

 

中序遍历

规则是若树为空,则空操作返回,否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。下图遍历顺序为GDHBAEICF

binary_tree_in_order_traverse

 

后序遍历

规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根节点。下图遍历顺序为GHDBIEFCA

binary_tree_post_order_traverse

 

层序遍历

规则是若树为空,则空操作返回,否则从树的第一层,也就是根节点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。下图遍历顺序为ABCDEFGHI

binary_tree_level_traverse

 

非递归实现四种遍历

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
#include<stack>
#include<queue>
using namespace std;

typedef struct node;
typedef node *tree;
struct node
{
	int data;
	tree L, R;
};
int pos[] = { 7,4,5,2,8,9,6,3,1 };
int in[] = { 7,4,2,5,1,3,8,6,9 };
tree bt;
void build(tree &bt, int l1, int r1, int l2, int r2)
{
	if (l1 > r1 || l2 > r2) return;
	int mid = l2;
	while (pos[r1] != in[mid]) mid++;
	bt = new node;
	bt->data = pos[r1];
	bt->L = NULL;
	bt->R = NULL;
	build(bt->L, l1, l1 + (mid - l2) - 1, l2, mid - 1);
	build(bt->R, l1 + (mid - l2), r1 - 1, mid + 1, r2);
}
void preprint(tree bt)
{
	stack<tree>s;
	tree p = bt;
	while (p != NULL || !s.empty()) {
		while (p != NULL) {
			s.push(p);
			cout << p->data ;
			p = p->L;
		}
		if (!s.empty()) {
			p = s.top();
			s.pop();
			p = p->R;
		}
	}
}
void inprint(tree bt)
{
	stack<tree>s;
	tree p = bt;
	while (p != NULL || !s.empty()) {
		while (p != NULL) {
			s.push(p);
			p = p->L;
		}
		if (!s.empty()) {

			p = s.top();
			cout << p->data;
			s.pop();
			p = p->R;
		}
	}
}


void posprint(tree bt)
{
	stack<tree>s, st;
	tree p;
	s.push(bt);
	while (!s.empty()) {
		p = s.top();
		s.pop();
		st.push(p);
		if (p->L) s.push(p->L);
		if (p->R) s.push(p->R);
	}
	while (!st.empty()) {
		cout << st.top()->data;
		st.pop();
	}
}
void leveprint(tree bt)
{
	if (bt == NULL) return;
	queue<tree>Q;
	tree p;
	Q.push(bt);
	while (!Q.empty()) {
		p = Q.front();
		Q.pop();
		cout << p -> data;
		if (p->L) Q.push(p->L);
		if (p->R) Q.push(p->R);

	}
}
int main()
{
	cout << "二叉树的建立,在知道后续和中序的情况下" << endl;
	build(bt, 0, 8, 0, 8);
	cout << "前序输出" << endl;
	preprint(bt);
	cout << endl;
	cout << "中序输出" << endl;
	inprint(bt);
	cout << endl;
	cout << "后序输出" << endl;
	posprint(bt);
	cout << endl;
	cout << "层序遍历" << endl;
	leveprint(bt);

}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值