二叉树的基本操作
遍历
(1)创建如下所示的一棵二叉树;
(2)对其采用以下四种不同的方式进行遍历;
为了更快学习掌握二叉树,先手动创建一棵如上图结构的二叉树,(真正的创建方式并不是以下这样哦~
)
代码如下:
public class BinaryTree {
public class BTNode{
//采用孩子表示法来表示一棵二叉树
int value; //保存自己的值域
BTNode left; //指向左孩子
BTNode right; //指向右孩子
//构造方法
BTNode(int value){
this.value = value;
}
}
private BTNode root; //指向根结点
public void createBinaryTree(){
BTNode node1 = new BTNode(1);
BTNode node2 = new BTNode(2);
BTNode node3 = new BTNode(3);
BTNode node4 = new BTNode(4);
BTNode node5 = new BTNode(5);
BTNode node6 = new BTNode(6);
node1.left=node2;
node1.right=node4;
node2.left=node3;
node4.left=node5;
node4.right=node6;
root= node1;
}
}
调试运行部分结果所示:
所谓
遍历
,就是指沿着某条搜索路径,依次对树中每个结点做一次且只做一次访问,访问结点所做的具体操作依赖于具体的应用问题(比如:打印结点的内容);
在遍历二叉树时,如果没有进行约定,不同的人遍历的结果往往是不同的,也分辨不清楚对错与否。但是,如果按照某种规则进行约定,则每个人对于同一棵树的遍历结果肯定是相同的;所以就有了以下四种遍历方式。他们分别为:前序遍历
、中序遍历
、后续遍历
和 层序遍历
;
下来分别做介绍:
前序遍历(先序遍历)
访问次序为
:
- 访问根结点—>访问根的左子树—>访问根的右子树
代码如下:
//将下面该代码进行包装的目的:减少用户传参,降低出错的风险
public void preOrder(){
System.out.print("前序遍历:");
preOrder(root) ;
}
//前序遍历
private void preOrder(BTNode treeRoot) {
// 分两种情况
//树为空
if(treeRoot==null){
return ;
}
//树不空
//遍历根结点
System.out.print(treeRoot.value +" ");
//遍历根的左子树
preOrder(treeRoot.left);
//遍历根的右子树
preOrder(treeRoot.right);
}
输出结果:
中序遍历
访问次序为
:
- 访问根的左子树—>访问根结点—>访问根的右子树
public void inOrder(){
System.out.print("中序遍历:");
inOrder(root) ;
//System.out.println();
}
//中序遍历
private void inOrder(BTNode treeRoot) {
if(treeRoot!=null){
inOrder(treeRoot.left);
System.out.print(treeRoot.value +" ");
inOrder(treeRoot.right);
}
}
输出结果:
后序遍历
访问次序为
:
- 访问根的左子树—>访问根的右子树—>访问根结点
public void postOrder(){
System.out.print("后序遍历:");
postOrder(root) ;
}
//后序遍历
private void postOrder(BTNode treeRoot) {
if(treeRoot!=null){
postOrder(treeRoot.left);
postOrder(treeRoot.right);
System.out.print(treeRoot.value +" ");
}
}
输出结果:
层序遍历
假设二叉树的根结点所在的层数为第1层:
层序遍历:
就是从所在二叉树的根结点出发,先访问第一层的根结点,然后从左往右访问第2层上的结点,再访问第三层的结点,以此类推,从上往下,从左往右逐层访问树的结点的过程;
由于层序遍历的过程与队列的特性相匹配,即:先被放到空间中的结点先被访问;因此,借助队列来实现层序遍历。
实现步骤:
1.先 new 一个队列,用来存放结点的引用;
2.将根结点(root)入队列;
3.判断队列是否为空,如果不空,循环进行下面三步操作:
(1)获取队头元素----poll
;
(2)遍历该结点
;
(3)检测该结点是否存在左右孩子,如果有左孩子,让其左孩子入队列,如果有右孩子,让其右孩子入队列
;
代码如下:
// 二叉树不为空-----需要借助队列完成二叉树的层序遍历
Queue<BTNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
BTNode cur = q.poll(); // poll(): 获取到队头元素并出队列
System.out.print(cur.data + " ");
// 如果cur的左孩子存在,则让cur的左孩子入队列
if(cur.left != null){
q.offer(cur.left);
}
// 如果cur的右孩子存在,则让cur的右孩子入队列
if(cur.right != null){
q.offer(cur.right);
}
}
System.out.println();
}
其他常见操作
(1)获取树结点的个数
//对下面代码进行包装了一层
//目的:减少传参的风险
public int size(){
return size(root);
}
// 获取树中结点的个数
private int size(BTNode treeRoot) {
if(treeRoot==null){
return 0;
}
return 1+size(treeRoot.left)+size(treeRoot.right);
}
(2)获取叶子结点的个数
//对下面代码进行包装了一层
//目的:减少传参的风险
public int getLeafNodeCount(){
return getLeafNodeCount(root);
}
// 获取叶子结点的个数
private int getLeafNodeCount(BTNode treeRoot){
if(treeRoot==null){
return 0;
}
if(treeRoot.left==null && treeRoot.right==null){
return 1;
}
return getLeafNodeCount(treeRoot.left)+getLeafNodeCount(treeRoot.right);
}
(3)获取第K层结点的个数
//对下面代码进行包装了一层
//目的:减少传参的风险
public int getKLevelNodeCount(int k){
return getKLevelNodeCount(root, k);
}
// 获取第K层结点的个数
private int getKLevelNodeCount(BTNode treeRoot,int k){
if(treeRoot==null || k<=0){
return 0;
}
if(k==1){
return 1;
}
return getKLevelNodeCount(treeRoot.left, k-1)+getKLevelNodeCount(treeRoot.right, k-1);
}
(4)获取二叉树的高度
//对下面代码进行包装了一层
//目的:减少传参的风险
public int height(){
return height(root);
}
// 获取二叉树的高度
private int height(BTNode treeRoot){
if(treeRoot==null){
return 0;
}
int leftHeight=height(treeRoot.left);
int rightHeight=height(treeRoot.right);
return leftHeight>rightHeight?leftHeight+1:rightHeight+1;
}
(5)检测值为 value 的元素是否存在,并返回
//对下面代码进行包装了一层
//目的:减少传参的风险
public BTNode find( int value){
return find(root,value);
}
// 检测值为value的元素是否存在
private BTNode find(BTNode treeRoot, int value){
if(treeRoot==null){
return null;
}
if(treeRoot.value==value){
return treeRoot;
}
BTNode ret=find(treeRoot.left,value);
if(ret!=null){
return ret;
}
return find(treeRoot.right,value);
}