1.普通二叉树的构建
构建结点以及搜索与删除的方法
public class Node {
private int no;
private String name;
private Node left;
private Node right;
private Node parent;
public Node(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"no=" + no +
", name='" + name + '\'' +
", left=" + left +
", right=" + right +
'}';
}
// 前序遍历
// 先输出父结点,再遍历左子树和右子树
public void preOrder(){
System.out.println(this);
if (this.left != null){
this.left.preOrder();
}
if (this.right != null){
this.right.preOrder();
}
}
// 中序遍历
// 先遍历左子树,再遍历父结点,再遍历右子树
public void midOrder(){
if (this.left != null){
this.left.midOrder();
}
System.out.println(this);
if (this.right != null){
this.right.midOrder();
}
}
// 后序遍历 左右中
public void postOrder(){
if (this.left != null){
this.left.preOrder();
}
if (this.right != null){
this.right.preOrder();
}
System.out.println(this);
}
// 前序遍历查找
public Node preSearch(int no){
if (this.no == no){
return this;
}
if (this.left != null){
return this.left.preSearch(no);
}
if (this.right != null){
return this.right.preSearch(no);
}
return null;
}
// 前序遍历查找
public Node midSearch(int no){
if (this.left != null){
return this.left.midSearch(no);
}
if (this.no == no){
return this;
}
if (this.right != null){
return this.right.midSearch(no);
}
return null;
}
// 前序遍历查找
public Node postSearch(int no){
if (this.left != null){
return this.left.postSearch(no);
}
if (this.right != null){
return this.right.postSearch(no);
}
if (this.no == no){
return this;
}
return null;
}
// 删除结点的两种情况
// 1. 删除的是叶子结点
// 2. 删除的结点是子树,非叶子结点
// 3. 单向二叉树
public void delete(int no){
/**注意:
* 1、二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除,而不能去判断当前这个结点是不是需要删除结点
* 2、如果当前结点左子结点不为空,并且左子结点就是需要删除的结点,则将this.left = null,返回即可
* 3、如果当前结点右子结点不为空,并且右子结点就是需要删除的结点,就将this.right = null,返回即可
* 4、如果2,3步没有执行,那么需要向左子树进行递归删除
* 5、如果第4步也没有删除结点,则向右子树进行递归删除
**/
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.delete(no);
}
if (this.right != null){
this.right.delete(no);
}
}
}
构建二叉树类并实现删除搜索的方法
public class BinaryTree {
private Node root;
public void setRoot(Node root){
this.root = root;
}
// 前序遍历
public void preOrder(){
if (root != null){
root.preOrder();
}else {
System.out.println("二叉树为空");
}
}
// 中序遍历
public void midOrder(){
if (root != null){
root.midOrder();
}else {
System.out.println("二叉树为空");
}
}
// 后序遍历
public void postOrder(){
if (root != null){
root.postOrder();
}else {
System.out.println("二叉树为空");
}
}
public Node preSearchById(int no){
if (root != null){
return root.preSearch(no);
}else {
System.out.println("未找到");
return null;
}
}
public Node midSearchById(int no){
if (root != null){
return root.midSearch(no);
}else {
System.out.println("未找到");
return null;
}
}
public Node postSearchById(int no){
if (root != null){
return root.postSearch(no);
}else {
System.out.println("未找到");
return null;
}
}
public void delNode(int no){
if (root != null){
if (root.getNo() == no){
root = null;
}else {
root.delete(no);
}
}else {
System.out.println("未找到");
}
}
}
2.线索二叉树
将数列[1,3,6,8,10,14] 构建成一颗二叉树
![在这里插入图片描述](https://img-blog.csdnimg.cn/b103c25d371244bc8db0be82d8fad944.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYmVjb2Vt,size_20,color_FFFFFF,t_70,g_se,x_16
问题分析:
1、当我们对上述二叉树进行中序遍历时,数列为[8,3,10,1,6,14] 8是3的前驱,10是3的后继
2、6,8,10,14这几个结点左右指针并没有很好的利用上。
3、如果考虑能够让每个结点充分利用左右指针指向自己的前后结点怎么去解决呢
这时只能选择使用线索二叉树
4、一个结点的前一个结点,称之为前驱结点,一个结点后一个结点称之为后继结点。
线索二叉树介绍
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化
![在这里插入图片描述](https://img-blog.csdnimg.cn/2713f1badbd34cc29734f7d92648b5f8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAYmVjb2Vt,size_20,color_FFFFFF,t_70,g_se,x_16)
对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树。(前驱和后继和选择哪种遍历方法有关)
这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
HeroNode 构建二叉树
public class HeroNode {
private int no;
private String name;
private HeroNode left;
private HeroNode right;
private HeroNode parent;
// 如果是0,表示指向的是左子树, 1表示指向前驱
private int noLeft;
// 如果是0,表示指向的是右子树, 1表示指向后继
private int noRight;
public HeroNode() {
}
public 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;
}
public int getNoLeft() {
return noLeft;
}
public void setNoLeft(int noLeft) {
this.noLeft = noLeft;
}
public int getNoRight() {
return noRight;
}
public void setNoRight(int noRight) {
this.noRight = noRight;
}
public HeroNode getParent() {
return parent;
}
public void setParent(HeroNode parent) {
this.parent = parent;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
// 删除结点
// 删除的可能是一个叶子结点,也可能是非叶子结点
public void delete(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.delete(no);
}
if (this.right != null){
this.right.delete(no);
}
}
// 前序遍历
public void preSearch(){
System.out.println(this);
if (this.left != null){
this.left.preSearch();
}
if (this.right != null){
this.right.preSearch();
}
}
// 根据编号查找
public HeroNode preSearchByNo(int no){
if (this.no == no){
return this;
}
HeroNode temp = null;
if (this.left != null){
temp = this.left.preSearchByNo(no);
}
if (temp != null){
return temp;
}
if (this.right != null){
temp = this.right.preSearchByNo(no);
}
return temp;
}
// 中序遍历
public void midSearch(){
if (this.left != null){
this.left.midSearch();
}
System.out.println(this);
if (this.right != null){
this.right.midSearch();
}
}
// 后序遍历
public void postSearch(){
if (this.left != null){
this.left.postSearch();
}
if (this.right != null){
this.right.postSearch();
}
System.out.println(this);
}
}
构建线索二叉树并进行搜索
public class ClueBinaryTree {
private HeroNode root;
// 当前结点的前驱结点的指针
private HeroNode pre; // 前驱
public void setRoot(HeroNode root) {
this.root = root;
}
// 非线索化二叉树转线索化二叉树
public void cluePreNode(){
this.cluePreNode(root);
}
public void clueNode(){
this.clueNode(root);
}
/*
把普通二叉树转线索化二叉树
*/
public void cluePreNode(HeroNode node){
// 中左右
if (node == null){
return;
}
if (node.getLeft() == null){
node.setLeft(pre);
node.setNoLeft(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setNoRight(1);
}
pre = node;
// 如果左子树没有线索化,那么线索化左子树
if (node.getNoLeft() != 1){
cluePreNode(node.getLeft());
}
//如果右子树没有线索化,那么线索化左子树
if (node.getNoRight() != 1){
cluePreNode(node.getRight());
}
}
/*
把普通二叉树转线索化二叉树
*/
public void cluePostNode(HeroNode node){
if (node == null){
return;
}
cluePreNode(node.getLeft());
cluePreNode(node.getRight());
if (node.getLeft() == null){
node.setLeft(pre);
node.setNoLeft(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setNoRight(1);
}
pre = node;
}
/*
把普通二叉树转线索化二叉树
*/
public void clueNode(HeroNode node){
if (node == null){
return;
}
clueNode(node.getLeft());
if (node.getLeft() == null){
node.setLeft(pre);
node.setNoLeft(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(node);
pre.setNoRight(1);
}
pre = node;
clueNode(node.getRight());
}
// 遍历线索化的二叉树
public void cluePreList(){
// 临时结点
HeroNode node = root;
while (node != null){
// 左中右
// 向左查找头结点
// node.getNoLeft() == 0没有连接前驱的就不是最后
System.out.println(node);
while (node.getNoLeft() == 0){
node = node.getLeft();
}
// 查看有没有连接后继,连接了后继就可以通过这个寻找到后继,并将指针上移至后继处
while(node.getNoRight() == 1){
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
// 遍历线索化的二叉树
public void clueList(){
// 临时结点
HeroNode node = root;
while (node != null){
// 左中右
// 向左查找头结点
// node.getNoLeft() == 0 没有连接前驱的就不是最后
while (node.getNoLeft() == 0){
node = node.getLeft();
}
System.out.println(node);
// 查看有没有连接后继,连接了后继就可以通过这个寻找到后继,并将指针上移至后继处
while(node.getNoRight() == 1){
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
}
3.顺序存储二叉树
二叉树的顺序存储就是用一组连续的存储单元(数组)存放二又树中的结点元素,一般按照二叉树结点自上向下、自左向右的顺序存储。
顺序存储二叉树特点:
1、顺序二叉树通常只考虑完全二叉树
2、第n个元素的左子结点编号为2n+1 比如元素为6的那个结点,它是第3个元素,则其左子结点编号为 2 * 3 + 1 = 7
3、第n个元素的右子结点编号为2n+2
4、第n个元素的父结点为(n-1)/2
5、n表示二叉树中第几个元素,按编号从0开始
public class ArrayBinaryTree {
private final int[] arrays;
public ArrayBinaryTree(int[] arrays){
this.arrays = arrays;
}
// 前序遍历顺序存储二叉树
// 从编号index开始遍历
public void preSelect(int index){
if (this.arrays == null || arrays.length == 0){
System.out.println("存储的数字为空");
return;
}
/**
* 向左递归
*/
System.out.printf("arrays[%d] = %d\t", index, arrays[index]);
if((index * 2 + 1) < arrays.length){
preSelect(index * 2 + 1);
}
/**
* 向右递归
*/
if ((index * 2 + 2) < arrays.length){
preSelect(index * 2 + 2);
}
}
public void midSelect(int index){
if (this.arrays == null || arrays.length == 0){
System.out.println("存储的数字为空");
return;
}
/**
* 向左递归
*/
if((index * 2 + 1) < arrays.length){
preSelect(index * 2 + 1);
}
System.out.printf("arrays[%d] = %d\t", index, arrays[index]);
/**
* 向右递归
*/
if ((index * 2 + 2) < arrays.length){
preSelect(index * 2 + 2);
}
}
public void postSelect(int index){
if (this.arrays == null || arrays.length == 0){
System.out.println("存储的数字为空");
return;
}
/**
* 向左递归
*/
if((index * 2 + 1) < arrays.length){
preSelect(index * 2 + 1);
}
/**
* 向右递归
*/
if ((index * 2 + 2) < arrays.length){
preSelect(index * 2 + 2);
}
System.out.printf("arrays[%d] = %d\t", index, arrays[index]);
}
}