二叉树的遍历有深度遍历(前序、中序、后序)和广度遍历(层次遍历)。二叉树的顺序存储结构就是使用一维数组存储二叉树中的结点,并且结点的存储位置,就是数组的下标索引,如下图所示就是完全二叉树的顺序存储方式,但一般它也只适用于完全二叉树,与数组、向量、链表都是一种顺序容器,它们提供了按位置访问数据的手段。但是有一个缺点,它们都是按照位置来确定数据,想要通过值来获取数据,只能通过遍历的方式。而二叉树在很大程度上解决了这个缺点,二叉树是按值来保存元素,也按值来访问元素。
1.前序遍历就是从二叉树的根节点出发,当第一次到达节点时就输出节点数据,按先左后右方式访问,故上图结果为ABDHIEJCFG。
2.中序遍历就是从二叉树的根节点出发,当第二次到达节点时就输出节点数据,按先左后右方式访问,故上图结果为HDIBJEAFCG。
3.后序遍历就是从二叉树的根节点出发,当第三次到达节点时就输出节点数据,按先左后右方式访问,故上图结果为HIDJEBFGCA。
4.层次遍历就是按照树的层次自上而下的遍历二叉树,故上图结果为ABCDEFGHIJ。
以下代码通过C#实现递归的方式进行二叉树的几种遍历,如果是非递归实现可以参考点这
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BinaryTree
{
class BinaryTree
{
private Node _head;
private string cStr;
public Node Head
{
get { return _head; }
set{_head=value;}
}
public BinaryTree()
{
_head=null;
}
public BinaryTree(string constructStr)//默认的是以层序序列构造二叉树
{
cStr=constructStr;
if(cStr[0]=='#')
{
_head=null;
return;//根节点为空,则无树建立
}
_head=new Node(cStr[0]);
Add(_head,0);
}
//按照给定层序序列给二叉树添加节点
private void Add(Node parent,int index)
{
int leftIndex=2*index+1;
if(leftIndex<cStr.Length)
{
if (cStr[leftIndex] != '#')
{
parent.Left = new Node(cStr[leftIndex]);
Add(parent.Left, leftIndex);
}
}
int rightIndex = 2 * index + 2;
if (rightIndex < cStr.Length)
{
if (cStr[rightIndex] != '#')
{
parent.Right = new Node(cStr[rightIndex]);
Add(parent.Right, rightIndex);
}
}
}
//递归先序遍历
public void PreOrder(Node node)
{
if (node != null)
{
Console.Write(node);
PreOrder(node.Left);
PreOrder(node.Right);
}
}
//递归中序遍历
public void InOrder(Node node)
{
if (node != null)
{
InOrder(node.Left);
Console.Write(node);
InOrder(node.Right);
}
}
//递归后序遍历
public void AfterOrder(Node node)
{
if (node != null)
{
AfterOrder(node.Left);
AfterOrder(node.Right);
Console.Write(node);
}
}
//广度优先遍历,就是层次遍历
public void LevelOrder()
{
Node node = _head;
Queue<Node> queue = new Queue<Node>();//队列(Queue)代表了一个先进先出的对象集合,参考https://www.runoob.com/csharp/csharp-queue.html
queue.Enqueue(node);
while (queue.Count > 0)
{
node = queue.Dequeue();
Console.Write(node);
if (node.Left != null)
{
queue.Enqueue(node.Left);
}
if (node.Right != null)
{
queue.Enqueue(node.Right);
}
}
}
/*二叉树遍历的应用*/
//1、计算叶子节点的个数(先序遍历)
//度为2的节点和度为0的节点也就是叶子节点的关系是n0=n2+1;加上可以统计节点的个数所以就可以
//分别统计度为0、度为1和度为2的节点数了。
public void CountLeaf(Node node, ref int count)
{
if (node != null)
{
if ((node.Left == null) && (node.Right == null))
count++;
CountLeaf(node.Left, ref count);
CountLeaf(node.Right, ref count);
}
}
//计算节点数
public int Count(Node root)
{
if (root == null) return 0;
return Count(root.Left) + Count(root.Right) + 1;
}
//2、计算树的高度(后序遍历)
public int Height(Node root)
{
int a, b;
if (root == null) return 0;
a = Height(root.Left);
b = Height(root.Right);
if (a > b) return a + 1; else return b + 1;
}
//3、复制二叉树(后序遍历)
public Node CopyTree(Node root)
{
Node newroot;
if (root == null)
{
newroot = null;
}
else
{
CopyTree(root.Left);
CopyTree(root.Right);
newroot = root;
}
return newroot;
}
/*4、建立二叉树饿存储结构(建立二叉树的二叉链表)。上面的复制也是种建立方法*/
//(1)按给定先序序列建立二叉树
public static BinaryTree CreateByPre(string s)
{
BinaryTree tree = new BinaryTree(s);//先以层序序列初始化个树,再调整
int _count = 0;
Node node = tree.Head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
node.Data = s[_count++];
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)//第2步
{
node = stack.Pop();
node = node.Right;
}
}
return tree;
}
//以中序序列建立二叉树
public static BinaryTree CreateByIn(string s)
{
BinaryTree tree = new BinaryTree(s);//先以层序序列初始化个树,再调整
int _count = 0;
Node node = tree.Head;
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
node = stack.Pop();
node.Data = s[_count++];
node = node.Right;
}
}
return tree;
}
public static BinaryTree CreateByAfter(string s)
{
BinaryTree tree = new BinaryTree(s);//先以层序序列初始化个树,再调整
int _count = 0;
Node node = tree.Head;
Node pre = tree.Head; ;
//pre指针指向“之前出栈节点”,如果为null有问题,这里指向头节点,因为后续遍历中头节点肯定是最后被访问的。
Stack<Node> stack = new Stack<Node>();
while (node != null || stack.Count > 0)
{
while (node != null)
{
stack.Push(node);
node = node.Left;
}
if (stack.Count > 0)
{
Node temp = stack.Peek().Right;//获取栈顶元素的右孩子,C#的栈因为有了这个方法使得操作简单
if (temp == null || temp == pre)//满足规则1
{
node = stack.Pop();//出栈进行访问
node.Data = s[_count++];
pre = node;//设置“之前出栈节点”
node = null;//防止null再次入栈
}
else
{
node = temp;//规则2 继续循环。将栈顶节点的右孩子入栈,重复规则1的操作
}
}
}
return tree;
}
//static void Main(string[] args)
//{
// BinaryTree binaryTree = new BinaryTree("123456789");
//}
//**********
}
class Program
{
static void Main(string[] args)
{
BinaryTree binaryTree = new BinaryTree("123456789");
binaryTree.PreOrder(binaryTree.Head);
Console.WriteLine(" ");
binaryTree.InOrder(binaryTree.Head);
Console.WriteLine(" ");
binaryTree.AfterOrder(binaryTree.Head);
Console.ReadLine();
}
}
}