声明:本文部分文章取自于

Java中关于二叉树详解_来学习的小张的博客-CSDN博客_java 二叉树原理

更多关于二叉树详情可以点击上面链接

目录

一:树形结构

二:二叉树

2.1 二叉树的遍历:

2.2 二叉树的查找:

2.3 二叉树删除节点:(简单版)

2.4 顺序存储二叉树:

2.5 线索化二叉树:


一:树形结构

是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 有一个特殊的节点,称为根节点,根节点没有前驱节点;
  • 除根节点外,其余节点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合 Ti (1 <= i<= m) 又是一棵与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的。

  1. 节点的度一个节点含有的子树的个数称为该节点的度; 如上图:A的度为6
  2. 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
  3. 叶子节点或终端节点度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点;
  4. 双亲节点或父节点若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点;
  5. 孩子节点或子节点一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点;
  6. 根结点:一棵树中,没有双亲结点的结点;如上图:A
  7. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  8. 树的高度或深度树中节点的最大层次; 如上图:树的高度为4

以下概念仅做了解即可

  1. 非终端节点或分支节点度不为0的节点; 如上图:D、E、F、G...等节点为分支节点;
  2. 兄弟节点具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点;
  3. 堂兄弟节点双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点;
  4. 节点的祖先从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先;
  5. 子孙以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙;
  6. 森林m(m>=0)棵互不相交的树的集合称为森林。

二:二叉树

2.1 二叉树的遍历:

使用前序、中序、后续来遍历二叉树

  • 前序遍历:先输出父节点,再遍历左子树和右子树
  • 中序遍历:先遍历左子树,再输出父节点,再遍历右子树
  • 后序遍历:先遍历左子树,再遍历右子树,最后输出父节点

小结:看输出父节点的顺序,就确定是前序、中序还是后序。


分析二叉树的前序,中序,后序的遍历步骤

1.创建一颗二叉树

2.前序遍历

  • 2.1先输出当前节点(初始的时候是root节点)
  • 2.2如果左子节点不为空,则递归继续前序遍历
  • 2.3如果右子节点不为空,则递归继续前序遍历

3.中序遍历

  • 3.1如果当前节点的左子节点不为空,则递归中序遍历
  • 3.2输出当前节点
  • 3.3如果当前的右子节点不为空,则递归中序遍历

4.后序遍历

  • 4.1如果当前节点的左子节点不为空,则递归后序遍历
  • 4.2如果当前节点的右子节点不为空,则递归后序遍历
  • 4.3输出当前节点

5.前、中、后序遍历代码实现:

public class BinaryTreeDemo {public static void main(String[] args) {//先需要创建一颗二叉树BinaryTree binaryTree=new BinaryTree();//创建需要的结点HeroNode root=new HeroNode(1,"宋江");HeroNode node2=new HeroNode(2,"吴用");HeroNode node3=new HeroNode(3,"卢俊义");HeroNode node4=new HeroNode(4,"林冲");HeroNode node5=new HeroNode(5,"武松");HeroNode node6=new HeroNode(6,"鲁智深");HeroNode node7=new HeroNode(7,"杨志");//说明,我们先手动创建该二叉树,后面我们学习递归的方式创建而常数root.setLeft(node2);root.setRight(node3);node2.setLeft(node4);node2.setRight(node5);node3.setLeft(node6);node3.setRight(node7);binaryTree.setRoot(root);//测试:System.out.println("前序遍历");//1,2,4,5,3,6,7binaryTree.preOrder();System.out.println("中序遍历");//4,2,5,1,6,3,7binaryTree.infixOrder();System.out.println("后序遍历");//4,5,2,6,7,3,1binaryTree.postOrder();}
}//定义二叉树
class BinaryTree{private HeroNode root;//根节点public void setRoot(HeroNode root){this.root=root;}//前序遍历public void preOrder(){if(this.root!=null){this.root.preOrder();}else {System.out.println("二叉树为空,无法遍历");}}//后序遍历public void infixOrder(){if(this.root!=null){this.root.infixOrder();}else {System.out.println("二叉树为空,无法遍历");}}//后序遍历public void postOrder(){if(this.root!=null){this.root.postOrder();}else {System.out.println("二叉树为空,无法遍历");}}}//先创建HeroNode结点
class HeroNode{private int no;private String name;private HeroNode left;//默认为nullprivate HeroNode right;//默认为nullpublic HeroNode(int no, String name) {this.no = no;this.name = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public HeroNode getLeft() {return left;}public void setLeft(HeroNode left) {this.left = left;}public HeroNode getRight() {return right;}public void setRight(HeroNode right) {this.right = right;}@Overridepublic String toString() {return "HeroNode [no=" + no + ", name=" + name + "]";}//编写前序遍历的方法public void preOrder(){System.out.println(this);//先输出父结点(谁调用这个方法,谁就是this)//递归向左子树前序遍历if(this.left!=null){this.left.preOrder();}//递归向右子树前序遍历if(this.right!=null){this.right.preOrder();}}//编写中序遍历的方法public void infixOrder(){//递归向左子树中序遍历if(this.left!=null){this.left.infixOrder();}//输出父结点System.out.println(this);//递归向右子树中序遍历if(this.right!=null){this.right.infixOrder();}}//编写后序遍历的方法public void postOrder(){//递归向左子树后序遍历if(this.left!=null){this.left.postOrder();}//递归向右子树后序遍历if(this.right!=null){this.right.postOrder();}//输出父结点System.out.println(this);}
}

2.2 二叉树的查找:

目的:使用前序、中序、后序的方式来查询指定的节点。

前序查找思路:

  • 先判断当前节点的no是否等于要查找的
  • 如果是相等的,则返回当前节点
  • 如果不相等,则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
  • 如果左递归前序查找,找到节点,则返回,否则继续判断,当前的节点的右子节点是否为空,如果不为空,则继续向右递归前序查找

中序查找思路:

  • 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
  • 如果找到,则返回,如果没有找到,就和当前节点比较,如果是则返回当前节点,否则继续进行右递归的中序查找
  • 如果右递归中序查找,找到就返回,否则为null

后序查找思路:

  • 判断当前节点的左子节点是否为空,如果不为空,则递归后序查找
  • 如果找到,就返回,如果没有找到,就判断当前节点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回
  • 就和当前节点进行,如果找到则返回,否则返回null

查找指定节点的部分代码实现:

//编写前序查找的方法public HeroNode preSearch(int no){//先判断当前节点是否符合if(this.no==no){return this;}HeroNode resNode=null;//向左递归if(this.left!=null){resNode=this.left.preSearch(no);}//向右递归(判断左递归得到的节点是否为空,如果为空,则进行右递归)if(resNode==null&&this.right!=null){resNode=this.right.preSearch(no);}return resNode;//如果最终还是没有找到,则返回null}//编写中序查找的方法public HeroNode infixSearch(int no){HeroNode resNode=null;if(this.left!=null){resNode=this.left.infixSearch(no);}if(this.no==no){return this;}if(this.right!=null&&resNode==null){//左边没有找到,找右边resNode=this.right.infixSearch(no);}return resNode;//不管有没有找到,都返回resNode}//编写后序查找的方法public HeroNode postSearch(int no){HeroNode resNode=null;if(this.left!=null){resNode=this.left.postSearch(no);}if(this.right!=null&&resNode==null){resNode=this.right.postSearch(no);}if(this.no==no){return this;}return resNode;}

2.3 二叉树删除节点:(简单版)

规定:如果要删除的节点是叶子节点,则直接删除,如果是非叶子节点,则删除该子树。

思路:

首先先处理:

考虑如果树是空树root,如果只有一个root节点,则等价将二叉树置空。

如果不是,则进行下面步骤:

  1. 因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除节点,而不能去判断当前这个节点是不是需要删除节点。
  2. 如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null;并且就返回(结束递归删除)
  3. 如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
  4. 如果第2和第3步没有删除节点,那么我们就需要向左子树进行递归删除。
  5. 如果第4步也没有删除节点,则应当向右子树进行递归删除。

节点删除的代码实现:

//二叉树部分:删除节点public void deleteNode(int no){if(root!=null){//如果只有一个root节点,这里立即判断root是不是要删除节点if(root.getNo()==no){root=null;}else {//递归删除root.deleteNode(no);}}else {System.out.println("二叉树为空,删除失败");}}
//链表部分:
//递归删除节点public void deleteNode(int no){//判断当前节点的左子节点if(this.left!=null&&this.left.no==no){this.left=null;return;}//判断当前节点的右子节点if(this.right!=null&&this.right.no==no){this.right=null;return;}//左子树递归删除if(this.left!=null){this.left.deleteNode(no);}//右子树递归删除if(this.right!=null){this.right.deleteNode(no);}}

2.4 顺序存储二叉树:

基本说明:从数据存储来啊看,数组存储方式和树的存储方式可以相互转化,即数组可以转换成树,也可以转换成数组。

 要求:

(1)图中的二叉树的节点,要求以数组的方式来存放arr【1,2,3,4,5,6,7】

(2)要求在遍历数组arr时,仍然可以以前序遍历、中序遍历和后序遍历的范围广是完成节点的遍历。

思路:

  • 顺序二叉树通常只考虑完全二叉树。
  • 数组下标为n的元素所对应的左子节点所在的下标为(2*n+1);
  • 数组下标为n的元素所对应的右子节点所在的下标为(2*n+2);
  • 数组下标为n的元素所对应的父节点为(n-1)/2;

提示:(可以自己对着上图验证一下)

顺序存储二叉树的代码实现:

public class ArrayBinaryTreeDemo {public static void main(String[] args) {int[] arr={1,2,3,4,5,6,7};ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree(arr);arrayBinaryTree.preOrder(); //1,2,4,5,3,6,7}
}
//编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
class ArrayBinaryTree{private int[] arr;//存储数据节点的数组public ArrayBinaryTree(int[] arr){this.arr=arr;}//编写一个方法,完成顺序存储二叉树的前序遍历//重载preOrderpublic void preOrder(){this.preOrder(0);}public void preOrder(int index){//如果数组为空,或arr.length=0if(arr==null || arr.length==0){System.out.println("数组为空,不能按照二叉树的前序遍历");}//输出当前这个元素System.out.println(arr[index]);//向左递归遍历if((index*2+1)<arr.length){preOrder(2*index+1);}//向右递归遍历if((index*2+2)<arr.length){preOrder(2*index+2);}}}

2.5 线索化二叉树:

 问题分析:

  • 当我们对上面的二叉树进行中序遍历时,数列为{8,3,10,1,6,14};
  • 但是6,8,10,14这几个节点的左右指针,并没有完全的利用上
  • 如果我们希望充分利用各个节点的左右指针,让各个节点可以指向自己的前后节点,怎么办?
  • 解决方案:线索化二叉树

使用场景:

  • 线索化的实质就是将二叉链表中的空指针改为指向前驱节点或后继节点的线索;
  • 线索化的过程就是修改二叉链表中空指针的过程,可以按照前序、中序、后序的方式进行遍历,分别生成不同的线索二叉树;
  • 有了线索二叉树之后,我们再次遍历时,就相当于操作一个双向链表。
  • 使用场景:如果我们在使用二叉树过程中经常需要遍历二叉树或者查找节点的前驱节点和后继节点,可以考虑采用线索二叉树存储结构。

线索二叉树基本介绍:

  • n个节点的二叉链表中含有(n+1)【公式:2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的钱去和后继节点的指针(这种附加的指针称为“线索”)。
  • 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Thread BinartTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
  • 一个节点的前一个节点,称为前驱节点。
  • 一个节点的后一个节点,称为后继节点。

思路分析: 

说明:当线索化二叉树后,Node节点的属性left和right,有如下情况:

(1)left指向的是左子树,也可能是指向的前驱节点,比如 1 节点left指向的左子树,而 10 节点的left指向的就是前驱节点。

(2)rigth指向的是右子树,也可能是指向后继节点,比如 1 节点right指向的是右子树,而 10 节点的right指向的是后继节点。

public class ThreadedBinaryTreeDemo {/*中序线索化二叉树*/public static void main(String[] args) {HeroNode root=new HeroNode(1,"tom");HeroNode node2=new HeroNode(3,"jack");HeroNode node3=new HeroNode(6,"smith");HeroNode node4=new HeroNode(8,"mary");HeroNode node5=new HeroNode(10,"king");HeroNode node6=new HeroNode(14,"dim");//二叉树,后面我们要递归创建,现在简单处理使用手动创建root.setLeft(node2);root.setRight(node3);node2.setLeft(node4);node2.setRight(node5);node3.setLeft(node6);BinaryTree binaryTree = new BinaryTree();binaryTree.setRoot(root);binaryTree.threadedNodes();//测试:HeroNode leftNode = node5.getLeft();HeroNode rightNode = node5.getRight();System.out.println("10 节点的前驱节点:"+leftNode);System.out.println("10 节点的后继节点:"+rightNode);}
}class BinaryTree{private HeroNode root;//根节点//为了实现线索化,需要创建要给指向当前节点的前驱节点的指针//在递归进行线索化时,pre总是保留前一个节点private HeroNode pre=null;public void setRoot(HeroNode root){this.root=root;}//重载threadedNodes方法public void threadedNodes(){this.threadedNodes(root);}//编写对二叉树进行中序线索化的方法/**** @param node:就是当前需要线索化的节点*/public void threadedNodes(HeroNode node){//如果node==null,不能线索化if(node==null){return;}//(一)先线索化左子树threadedNodes(node.getLeft());//(二)线索化当前节点//处理当前节点的前驱节点//以8节点来理解//8节点的.left=null,8节点的.leftType=1if(node.getLeft()==null){//让当前节点的左指针指向前驱节点node.setLeft(pre);//修改当前节点的左指针的类型,指向前驱节点node.setLeftType(1);}//处理后继节点if(pre!=null&&pre.getRight()==null){//让前驱节点的右指针指向当前节点pre.setRight(node);//修改前驱节点的右指针类型pre.setRightType(1);}//!!! 每处理一个节点后,让当前节点是下一个节点的前驱节点pre=node;//(三)再线索化右子树threadedNodes(node.getRight());}}//先创建HeroNode结点
class HeroNode{private int no;private String name;private HeroNode left;//默认为nullprivate HeroNode right;//默认为null//说明//1.如果leftType==0表示指向的是左子树,1则表示指向前驱节点//2.如果rightType==0表示指向是右子树,1则表示指向后继节点private int leftType;private int rightType;public HeroNode(int no, String name) {this.no = no;this.name = name;}public int getLeftType() {return leftType;}public void setLeftType(int leftType) {this.leftType = leftType;}public int getRightType() {return rightType;}public void setRightType(int rightType) {this.rightType = rightType;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public HeroNode getLeft() {return left;}public void setLeft(HeroNode left) {this.left = left;}public HeroNode getRight() {return right;}public void setRight(HeroNode right) {this.right = right;}@Overridepublic String toString() {return "HeroNode [no=" + no + ", name=" + name + "]";}
}

遍历线索化二叉树:

说明:对前面的中序线索化的二叉树,进行遍历

分析:因为线索化后,各个节点指向有变化,因此原来的遍历方式不能使用,这是需要使用新的方式遍历线索化二叉树,各个节点开头通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。遍历的次序应当和中序遍历保持一致。

代码实现: 

//遍历线索化二叉树public void threadedList(){//定义一个变量,存储当前遍历的节点,从root开始HeroNode node=root;while(node!=null){//循环的找到leftType==1的节点,第一个找到就是8节点//后面随着遍历而变化,因为当leftType==1是,说明该节点按照线索化处理后的有效节点while(node.getRightType()==0){node=node.getLeft();}//打印当前这个节点System.out.println(node);//如果当前节点的右指针指向的是后继节点,就一直输出while (node.getRightType()==1){//获取到当前节点的后继节点node=node.getRight();System.out.println(node);}//替换这个遍历的节点node=node.getRight();}}

二叉树(Java实现)相关推荐

  1. 《LeetCode力扣练习》剑指 Offer 28. 对称的二叉树 Java

    <LeetCode力扣练习>剑指 Offer 28. 对称的二叉树 Java 一.资源 题目: 请实现一个函数,用来判断一棵二叉树是不是对称的.如果一棵二叉树和它的镜像一样,那么它是对称的 ...

  2. 《LeetCode力扣练习》第617题 合并二叉树 Java

    <LeetCode力扣练习>第617题 合并二叉树 Java 一.资源 题目: 给你两棵二叉树: root1 和 root2 . 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些 ...

  3. 《LeetCode力扣练习》第226题 翻转二叉树 Java

    <LeetCode力扣练习>第226题 翻转二叉树 Java 一.资源 题目: 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点. 示例 1: 输入:root = [4, ...

  4. 【算法】有序链表转换二叉搜索树和从中序与后序遍历序列构造二叉树Java解答参考

    三道算法题 1.有序链表转换二叉搜索树 Java代码参考 2.从中序与后序遍历序列构造二叉树 Java代码参考 3.移除元素 Java代码参考 1.有序链表转换二叉搜索树 给定一个单链表,其中的元素按 ...

  5. 【剑指offer】面试题32:从上到下打印二叉树(java)

    从上往下打印二叉树的每个节点,同一层的节点按照从左到右的顺序打印.例如输入下图的二叉树,则一次打印出8,6,10,5,7,9,11. 思路:利用队列,将左右子树加入队列末尾,取出结点 代码: pack ...

  6. JZ4 重建二叉树--java实现

    描述 给定某二叉树的前序遍历和中序遍历,请重建出该二叉树并返回它的头结点. 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出如下图所示. ...

  7. 二叉树 递归 java_递归实现二叉树 JAVA

    今天顶着要交年终文档的压力,总算理清了递归方式实现二叉树的方法,彻底弄明白要如何建一颗二叉树了!终于了切了一桩心愿!苦苦思索近两个星期了,郁闷得快不行了.终于一见庐山真面目,真是凡事一定要坚持到底呀! ...

  8. 简单二叉树Java代码实现

    二叉树 文章目录 二叉树 一.二叉树是什么? 二叉树的性质 二.二叉树简单JAVA代码实现 1.Node(节点) 2.MyTree(简单二叉树) 总结 一.二叉树是什么? 二叉树是每个结点最多有两个子 ...

  9. 反转二叉树 Java代码 (二叉树,中序遍历,层序遍历)【PAT甲级1102】

    输入样例: 8 1 - - - 0 - 2 7 - - - - 5 - 4 6 输出样例: 3 7 2 6 4 0 5 1 6 5 7 4 3 2 0 1 算法思路: 要将二叉树翻转,可以先按题意建好 ...

  10. java 实现最优二叉树_哈夫曼树(最优二叉树) - Java实现

    简介 哈夫曼树(Huffman)树又称最优二叉树,是指对于一组带有确定权值的叶子结点所构造的具有带权路径长度最短的二叉树.从树中一个结点到另一个结点之间的分支构成了两结点之间的路径,路径上的分支个数称 ...

最新文章

  1. matlab语言实验二,实验二 MATLAB语言基础
  2. CMake命令之list
  3. 开源组件ExcelReport 1.5.2 使用手册
  4. IOS sqlite数据库增删改查
  5. python的前端框架_web前端三大主流框架之Python异步框架如何工作?
  6. UDP对应的应用层协议之DHCP协议
  7. Windows10系统安装 .NET Framework 3.5
  8. 微信小程序使用阿里图标
  9. c++ pat 乙级 ---1004 成绩排名
  10. Java文档阅读笔记-JPA中getOne()和findById的区别
  11. 全球10大受欢迎的顶级编程语言与薪资水平
  12. 手把手教你强化学习 (七) 强化学习中的无模型控制
  13. linux下物理内存不足,vm中linux物理内存不足解决方案
  14. 操作 神通数据库_神通数据库OGDCProvider使用手册.PDF
  15. 从《征途》看互联网盈利模式的设计
  16. Java版点餐小程序2022最新版笔记,Springboot+Mysql+Freemarker+Bootstrap+微信小程序实现扫码点餐小程序
  17. UVA - 10098 Generating Fast
  18. 如何有效投诉中国电信、中国联通、中国移动?
  19. 华为云教程(云数据库RDS)
  20. kafka 创建 topic 报错 Error: Exception thrown by the agent : java.rmi.server.ExportException: Port alrea

热门文章

  1. c++ ege 坦克大战
  2. 大连芒果云计算机,芒果网manGO怎么样
  3. natapp软件使用
  4. 【icourse163】学习python爬虫的代码整理
  5. flask基于保利威做视频认证
  6. 计算机系统软硬件结构图,计算机系统结构组成.ppt
  7. 徐小湛概率论与数理统计课件_考研数学 徐小湛教授线性代数90讲
  8. mysql单表多次内联接查询学科名称,一级学科名称二级学科名称三级学科名称
  9. Java8新特性-Lambda表达式
  10. WPS快捷键之 通用基础