12章 树的实际应用 二
1、引言
先看一个需求:
给你一个数列 (7, 3, 10, 12, 5, 1, 9),要求能够高效的完成对数据的查询和添加。
解决方案分析:
- 使用数组
数组未排序,
- 优点:直接在数组尾添加,速度快。
- 缺点:查找速度慢.
数组排序,
- 优点:可以使用二分查找,查找速度快,
- 缺点:为了保证数组有序,在添加新数据时,找到插入位置后,后面的数据需整体移动,速度慢
- 使用链式存储-链表
不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体移动。
- 使用二叉排序树
2、二叉排序树(BST树)
二叉排序树:BST: (Binary Sort(Search) Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点
比如针对前面的数据 (7, 3, 10, 12, 5, 1, 9) ,对应的二叉排序树为:
2.1 二叉排序树创建和遍历
一个数组创建成对应的二叉排序树,并使用中序遍历二叉排序树,比如: 数组为 Array(7, 3, 10, 12, 5, 1, ,2,9) , 创建成对应的二叉排序树为 :
package tree.binarySortTree;
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12,1 ,5, 2, 9};
// int[] arr = {1,2,3,4,5};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new Node(arr[i]));
}
//中序遍历二叉排序树
binarySortTree.midOrder();
}
}
//创建二叉排序树
class BinarySortTree{
private Node root;
public void add(Node node){
if(root == null){ //如果root为空则直接root指向node
root = node;
}else{
root.add(node);
}
}
//中序遍历
public void midOrder(){
if(root != null){
root.midOrder();
}else {
System.out.println("树为空,不能遍历");
}
}
}
//创建结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +"value=" + value +'}';
}
//添加结点的方法 7, 3, 10, 12,2 ,5, 1, 9
//递归的形式添加结点,注意需要满足二叉排序树的要求
public void add(Node node){
if(node == null){
return;
}
//判断传入的结点值,和当前子树的根节点的值关系
if(node.value < this.value ){
if(this.left == null){//如果当前结点左子结点为null
this.left = node;
}else{
this.left.add(node);
}
}else{//添加的结点大于当前结点值
if(this.right == null){//如果当前结点右子结点为null
this.right = node;
}else{
this.right.add(node);
}
}
}
//中序遍历方法
public void midOrder(){
if(this == null){
return;
}
if(this.left != null){
this.left.midOrder();
}
System.out.println(this);
if(this.right != null){
this.right.midOrder();
}
}
}
2.1 二叉排序树删除结点思路图解
二叉排序树的删除情况比较复杂,有下面三种情况需要考虑
删除叶子节点 (比如:2, 5, 9, 12)
删除只有一颗子树的节点 (比如:1)
删除有两颗子树的节点. (比如:7, 3,10 )
2.1.1 删除叶子节点
第一种情况: 删除叶子节点 (比如:2, 5, 9, 12)
思路
- 需求先去找到要删除的结点 delNode
- 找到delNode的 父结点 parentNode
- 确定delNode是 parentNode的左子结点 还是右子结点
- 根据前面的情况来对应删除 左子结点 parentNode.left =null 右子结点 parentNode.right = null;
//创建二叉排序树
class BinarySortTree{
private Node root;
//查找要删除的结点
public Node searchDel(int value){
if(root == null){
return null;
}else {
return root.searchDel(value);
}
}
//查找要删除的结点父节点
public Node searchParent(int value){
if(root == null){
return null;
}else {
return root.searchParent(value);
}
}
//删除结点
public void delNode(int value){
if(root == null){
return;
}else {
//1 先找到要删除的结点
Node delNode = root.searchDel(value);
if(delNode == null){//没有找到要删除的点
return;
}
//如果我们发现当前这课二叉排序树只有一个结点,且要删除的结点就是root
if(delNode == root && root.left == null && root.right == null){
root = null;
return;
}
//2 先找到要删除的父结点
Node parentNode = root.searchParent(value);
//如果delNode是叶子结点
if(delNode.left == null && delNode.right == null){
//判断delNode是父节点的左子结点 还是右子结点
if(parentNode.left != null && parentNode.left == delNode){
parentNode.left = null;
}else if(parentNode.right != null && parentNode.right == delNode){
parentNode.right = null;
}
}
}
}
//创建结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +"value=" + value +'}';
}
//中序遍历方法
public void midOrder(){
if(this == null){
return;
}
if(this.left != null){
this.left.midOrder();
}
System.out.println(this);
if(this.right != null){
this.right.midOrder();
}
}
/**
* 查找到要删除的结点
* @param value 想要删除结点的值
* @return 找到返回该结点,否则返回null
*/
public Node searchDel(int value){
if(this.value == value){
return this;
}else if(value < this.value && this.left != null){
return this.left.searchDel(value);
}else if( this.right != null){ //value > this.value && 可以省略
return this.right.searchDel(value);
}
return null;
/*if(value == this.value){ //说明找到了
return this;
}else if(value < this.value){ //查找的值小于当前结点的值,向左子树查找
if(this.left == null){ //左子结点为空
return null;
}
return this.left.searchDel(value);
}else{ //查找的值不小于当前结点的值,向右子树查找
if(this.right == null){
return null;
}
return this.right.searchDel(value);
}*/
}
/**
* 查找到要删除结点的父节点
* @param value 想要删除结点的父节点
* @return 找到返回该结点,否则返回null
*/
public Node searchParent(int value){
//如果当前结点是要删除结点的父节点 就返回
if( (this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)){
return this;
}else{
//如果要查找到值小于当前结点值,且左子结点不为空
if(value < this.value && this.left != null){
return this.left.searchParent(value);
}else if(value > this.value && this.right != null){
return this.right.searchParent(value);
}else{
return null;
}
}
}
}
2.1.2 删除只有一颗子树的节点
第二种情况: 删除只有一颗子树的节点 比如 1
思路
- 需求先去找到要删除的结点 delNode
- 找到delNode的 父结点 parent
- 确定delNode的子结点是左子结点还是右子结点
- delNode是 parent 的左子结点还是右子结点
- 如果delNode有左子结点
5.1 如果 delNode是 parent 的左子结点
parent .left = delNode.left
5.2 如果 delNode是 parent 的右子结点
parent .right = delNode.left- 如果delNode有右子结点
6.1 如果 delNode是 parent 的左子结点
parent .left = delNode.right
6.2 如果 targetNode 是 parentNode 的右子结点
parent .right = delNode.right
局部代码
if(delNode.left != null){//删除结点有一个左子节点
if(parentNode.left == delNode){ //delNode 是parentNode左子节点
parentNode.left = delNode.left;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.left;
}
}else if(delNode.right != null){//删除结点有一个右子节点
if(parentNode.left == delNode){//delNode 是parentNode左子节点
parentNode.left = delNode.right;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.right;
}
}
2.1.3 删除只有二颗子树的节点
情况三: 删除有两颗子树的节点. (比如:7, 3,10 )
思路
1.先去找到要删除的结点 delNode
2. 找到delNode的 父结点 parent
3. 从delNode的右子树找到最小的结点
4. 用一个临时变量(temp),将最小结点的值保存
5. 删除该最小结点
6. 最后将临时结点的值赋值给待删除的结点 (delNode.value = temp)
/**
* 1.返回以Node为根节点的二叉排序树的最小结点的值
* 2.删除node 为根节点的二叉排序树的最小结点
*
* @param node 二叉排序树的根结点
* @return 返回需要删除结点右子分支的最小结点值
*/
public int delRightTreeMin(Node node){
Node temp = node;
while(temp.left != null){
temp = temp.left;
}
delNode(temp.value);
return temp.value;
}
if(delNode.left != null && delNode.right != null){//如果delNode有两个子结点
int minValue = delRightTreeMin(delNode.right);
delNode.value = minValue;
}
BST添加 删除全部实现代码
package tree.binarySortTree;
public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12,1 ,5, 2, 9};
// int[] arr = {1,2,3,4,5};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new Node(arr[i]));
}
//中序遍历二叉排序树
binarySortTree.midOrder();
//查找待删除的结点
binarySortTree.delNode(1);
System.out.println("删除结点后的中序遍历");
binarySortTree.midOrder();
}
}
//创建二叉排序树
class BinarySortTree{
private Node root;
public void add(Node node){
if(root == null){ //如果root为空则直接root指向node
root = node;
}else{
root.add(node);
}
}
//中序遍历
public void midOrder(){
if(root != null){
root.midOrder();
}else {
System.out.println("树为空,不能遍历");
}
}
//查找要删除的结点
public Node searchDel(int value){
if(root == null){
return null;
}else {
return root.searchDel(value);
}
}
//查找要删除的结点父节点
public Node searchParent(int value){
if(root == null){
return null;
}else {
return root.searchParent(value);
}
}
/**
* 1.返回以Node为根节点的二叉排序树的最小结点的值
* 2.删除node 为根节点的二叉排序树的最小结点
*
* @param node 二叉排序树的根结点
* @return 返回需要删除结点右子分支的最小结点值
*/
public int delRightTreeMin(Node node){
Node temp = node;
while(temp.left != null){
temp = temp.left;
}
delNode(temp.value);
return temp.value;
}
//删除以node为根节点二叉排序树的最大结点
public int delLeftTreeMax(Node node){
Node temp = node;
while(temp.right != null){
temp =temp.right;
}
delNode(temp.value);
return temp.value;
}
//删除结点
public void delNode(int value){
if(root == null){
return;
}else {
//1 先找到要删除的结点
Node delNode = root.searchDel(value);
if(delNode == null){//没有找到要删除的点
return;
}
//如果我们发现当前这课二叉排序树只有一个结点,且要删除的结点就是root
if(delNode == root && root.left == null && root.right == null){
root = null;
return;
}
//2 先找到要删除的父结点
Node parentNode = root.searchParent(value);
//如果delNode是叶子结点
if(delNode.left == null && delNode.right == null){
//判断delNode是父节点的左子结点 还是右子结点
if(parentNode.left != null && parentNode.left == delNode){
parentNode.left = null;
}else if(parentNode.right != null && parentNode.right == delNode){
parentNode.right = null;
}
}else if(delNode.left != null && delNode.right != null){//如果delNode有两个子结点
/*int minValue = delRightTreeMin(delNode.right);
delNode.value = minValue;*/
int maxValue = delLeftTreeMax(delNode.right);
delNode.value = maxValue;
}else{//删除的只有一颗子树的结点
if(parentNode == null){ //在只有左(右)子树的情况下删除root结点
if(root.left != null){
root = root.left;
return;
}else if(root.right != null){
root = root.right;
return;
}
}
if(delNode.left != null){//删除结点有一个左子节点
if(parentNode.left == delNode){ //delNode 是parentNode左子节点
parentNode.left = delNode.left;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.left;
}
}else if(delNode.right != null){//删除结点有一个右子节点
if(parentNode.left == delNode){//delNode 是parentNode左子节点
parentNode.left = delNode.right;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.right;
}
}
}
}
}
}
//创建结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +"value=" + value +'}';
}
//添加结点的方法 7, 3, 10, 12,2 ,5, 1, 9
//递归的形式添加结点,注意需要满足二叉排序树的要求
public void add(Node node){
if(node == null){
return;
}
//判断传入的结点值,和当前子树的根节点的值关系
if(node.value < this.value ){
if(this.left == null){//如果当前结点左子结点为null
this.left = node;
}else{
this.left.add(node);
}
}else{//添加的结点大于当前结点值
if(this.right == null){//如果当前结点右子结点为null
this.right = node;
}else{
this.right.add(node);
}
}
}
//中序遍历方法
public void midOrder(){
if(this == null){
return;
}
if(this.left != null){
this.left.midOrder();
}
System.out.println(this);
if(this.right != null){
this.right.midOrder();
}
}
/**
* 查找到要删除的结点
* @param value 想要删除结点的值
* @return 找到返回该结点,否则返回null
*/
public Node searchDel(int value){
if(this.value == value){
return this;
}else if(value < this.value && this.left != null){
return this.left.searchDel(value);
}else if( this.right != null){ //value > this.value && 可以省略
return this.right.searchDel(value);
}
return null;
/*if(value == this.value){ //说明找到了
return this;
}else if(value < this.value){ //查找的值小于当前结点的值,向左子树查找
if(this.left == null){ //左子结点为空
return null;
}
return this.left.searchDel(value);
}else{ //查找的值不小于当前结点的值,向右子树查找
if(this.right == null){
return null;
}
return this.right.searchDel(value);
}*/
}
/**
* 查找到要删除结点的父节点
* @param value 想要删除结点的父节点
* @return 找到返回该结点,否则返回null
*/
public Node searchParent(int value){
//如果当前结点是要删除结点的父节点 就返回
if( (this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)){
return this;
}else{
//如果要查找到值小于当前结点值,且左子结点不为空
if(value < this.value && this.left != null){
return this.left.searchParent(value);
}else if(value > this.value && this.right != null){
return this.right.searchParent(value);
}else{
return null;
}
}
}
}
3、平衡二叉树(AVL树)
3.1 平衡二叉树(AVL树)介绍
看一个案例(说明二叉排序树可能的问题)
给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST), 并分析问题所在。
二叉排序树(BST) 存在的问题分析:
-
左子树全部为空,从形式上看,更像一个单链表.
-
插入速度没有影响
-
查询速度明显降低(因为需要依次比较), 不能发挥BST 的优势,因为每次还需要比较左子树,其查询速度比 单链表还慢
解决方案——》平衡二叉树(AVL)
基本介绍
- 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree)又被称为AVL树, 可以保证查询效率较高。
- 具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
- 平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。
举例说明, 看看下面哪些AVL树, 为什么? (图1 2是平衡二叉树,图三不是 因为左右子树高度超过了1)
3.2 AVL树左旋转思路图解
应用案例-单旋转(左旋转)
1.要求: 给你一个数列,创建出对应的平衡二叉树.数列 {4,3,6,5,7,8}
2.思路分析(示意图)
3.2.1 AVL树左旋步骤详解
- 创建新的结点,以当前根节点的值
- 把新节点的左子树设置成当前结点的左子树
- 把新节点的右子树设置成当前结点的右子树的左子树
- 把当前结点的值替换成当前结点右子树的值
- 把当前结点的右子树设置成 当前结点右子树的右子树
- 当前结点的左子树设置成新的结点
3.2.2 AVL树左旋代码实现
//左旋转方法
public void leftRotate(){
//创建新的结点,以当前根节点的值
Node newNode = new Node(value);
//把新节点的左子树设置成当前结点的左子树
newNode.left = this.left;
//把新节点的右子树设置成当前结点的右子树的左子树
newNode.right = this.right.left;
//把当前结点的值替换成当前结点右子树的值
this.value = right.value;
//把当前结点的右子树设置成 当前结点右子树的右子树
this.right = this.right.right;
//当前结点的左子树设置成新的结点
this.left = newNode;
}
3.3 AVL树右旋转图解和实现
应用案例-单旋转(右旋转)
- 要求: 给你一个数列,创建出对应的平衡二叉树.数列 {10,12, 8, 9, 7, 6}
- 思路分析(示意图)
3.3.1 AVL树右旋步骤详解
- 创建新的结点,以当前根节点的值
- 把新节点的右子树设置了当前节点的右子树
- 把新节点的左子树设置为当前节点的左子树的右子树
- 把当前节点的值换为左子节点的值
- 把当前节点的左子树设置成左子树的左子树
- 把当前节点的右子树设置为新节点
3.3.2 AVL树右旋代码实现
//右旋转方法
public void rightRotate(){
//创建新的结点,以当前根节点的值
Node newNode = new Node(this.value);
//把新节点的右子树设置了当前节点的右子树
newNode.right = this.right;
//把新节点的左子树设置为当前节点的左子树的右子树
newNode.left = this.left.right;
//把当前节点的值换为左子节点的值
this.value = this.left.value;
//把当前节点的左子树设置成左子树的左子树
this.left = this.left.left;
//把当前节点的右子树设置为新节点
this.right = newNode;
}
3.4 AVL树双旋转图解和实现
3.4.1 右转双旋
应用案例-双旋转
前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。比如数列
int[] arr = { 10, 11, 7, 6, 8, 9 }; 运行原来的代码可以看到,并没有转成AVL树.
int[]arr= {2,1,6,5,7,3}; // 运行原来的代码可以看到,并没有转成 AVL树
问题分析:
在满足右旋转条件时(根节点的左子树高度–右子树高度 > 1),要判断:
- 如果是 根节点左子结点的左子树高度 小于 根节点左子结点的右子树高度时:
- 就对当前根节点的左子树,先进行 左旋转,
- 然后, 再对当前根节点进行右旋转即可
.
- 否则,直接对当前节点(根节点)进行右旋转.即可.
3.4.2 左转双旋
同理可得
在满足左旋转条件时(根节点的右子树高度–左子树高度 > 1),要判断:
- 如果是 根节点右子结点的左子树高度 大于 根节点右子结点的右子树高度时:
- 就对当前根节点的右子树,先进行 右旋转,
- 然后, 再对当前根节点进行左旋转即可
.
- 否则,直接对当前节点(根节点)进行左旋转.即可.
AVL树的全部代码实例
public class AVLTreeDemo {
public static void main(String[] args) {
// int[] arr = {4,3,6,5,7,8};
// int[] arr = {10,12,8,9,7,6};
// int[] arr = {10,7,6,8,9,11};
int[] arr = {10,11,7,6,8,9};
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
//中序遍历
avlTree.midOrder();
System.out.println("树高"+avlTree.getRoot().height());
System.out.println("左子树高"+avlTree.getRoot().leftHeight());
System.out.println("右子树高"+avlTree.getRoot().rightHeight());
System.out.println("根结点"+ avlTree.getRoot());
System.out.println("根结点的左结点"+ avlTree.getRoot().left);
System.out.println("根结点的右结点"+ avlTree.getRoot().right);
System.out.println("根结点的右结点"+ avlTree.getRoot().right.left);
System.out.println("根结点的右结点"+ avlTree.getRoot().right.right);
}
}
//创建AVL树
class AVLTree{
private Node root;
public Node getRoot() {
return root;
}
public void add(Node node){
if(root == null){ //如果root为空则直接root指向node
root = node;
}else{
root.add(node);
}
}
//中序遍历
public void midOrder(){
if(root != null){
root.midOrder();
}else {
System.out.println("树为空,不能遍历");
}
}
//查找要删除的结点
public Node searchDel(int value){
if(root == null){
return null;
}else {
return root.searchDel(value);
}
}
//查找要删除的结点父节点
public Node searchParent(int value){
if(root == null){
return null;
}else {
return root.searchParent(value);
}
}
/**
* 1.返回以Node为根节点的二叉排序树的最小结点的值
* 2.删除node 为根节点的二叉排序树的最小结点
*
* @param node 二叉排序树的根结点
* @return 返回需要删除结点右子分支的最小结点值
*/
public int delRightTreeMin(Node node){
Node temp = node;
while(temp.left != null){
temp = temp.left;
}
delNode(temp.value);
return temp.value;
}
//删除以node为根节点二叉排序树的最大结点
public int delLeftTreeMax(Node node){
Node temp = node;
while(temp.right != null){
temp =temp.right;
}
delNode(temp.value);
return temp.value;
}
//删除结点
public void delNode(int value){
if(root == null){
return;
}else {
//1 先找到要删除的结点
Node delNode = root.searchDel(value);
if(delNode == null){//没有找到要删除的点
return;
}
//如果我们发现当前这课二叉排序树只有一个结点,且要删除的结点就是root
if(delNode == root && root.left == null && root.right == null){
root = null;
return;
}
//2 先找到要删除的父结点
Node parentNode = root.searchParent(value);
//如果delNode是叶子结点
if(delNode.left == null && delNode.right == null){
//判断delNode是父节点的左子结点 还是右子结点
if(parentNode.left != null && parentNode.left == delNode){
parentNode.left = null;
}else if(parentNode.right != null && parentNode.right == delNode){
parentNode.right = null;
}
}else if(delNode.left != null && delNode.right != null){//如果delNode有两个子结点
/*int minValue = delRightTreeMin(delNode.right);
delNode.value = minValue;*/
int maxValue = delLeftTreeMax(delNode.right);
delNode.value = maxValue;
}else{//删除的只有一颗子树的结点
if(parentNode == null){ //在只有左(右)子树的情况下删除root结点
if(root.left != null){
root = root.left;
return;
}else if(root.right != null){
root = root.right;
return;
}
}
if(delNode.left != null){//删除结点有一个左子节点
if(parentNode.left == delNode){ //delNode 是parentNode左子节点
parentNode.left = delNode.left;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.left;
}
}else if(delNode.right != null){//删除结点有一个右子节点
if(parentNode.left == delNode){//delNode 是parentNode左子节点
parentNode.left = delNode.right;
}else if(parentNode.right == delNode){//delNode 是parentNode右子节点
parentNode.right = delNode.right;
}
}
}
}
}
}
//创建结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +"value=" + value +'}';
}
//返回以该节点为根结点的树的高度
public int height(){
return Math.max( (left == null ? 0 : left.height()) , (right == null ? 0 : right.height()) ) + 1;
}
//返回左子树的高度
public int leftHeight(){
if(left == null){
return 0;
}
return left.height();
}
//返回右子树的高度
public int rightHeight(){
if(right == null){
return 0;
}
return right.height();
}
//左旋转方法
public void leftRotate(){
//创建新的结点,以当前根节点的值
Node newNode = new Node(value);
//把新节点的左子树设置成当前结点的左子树
newNode.left = this.left;
//把新节点的右子树设置成当前结点的右子树的左子树
newNode.right = this.right.left;
//把当前结点的值替换成当前结点右子树的值
this.value = right.value;
//把当前结点的右子树设置成 当前结点右子树的右子树
this.right = this.right.right;
//当前结点的左子树设置成新的结点
this.left = newNode;
}
//右旋转方法
public void rightRotate(){
//创建新的结点,以当前根节点的值
Node newNode = new Node(this.value);
//把新节点的右子树设置了当前节点的右子树
newNode.right = this.right;
//把新节点的左子树设置为当前节点的左子树的右子树
newNode.left = this.left.right;
//把当前节点的值换为左子节点的值
this.value = this.left.value;
//把当前节点的左子树设置成左子树的左子树
this.left = this.left.left;
//把当前节点的右子树设置为新节点
this.right = newNode;
}
//添加结点的方法 7, 3, 10, 12,2 ,5, 1, 9
//递归的形式添加结点,注意需要满足二叉排序树的要求
public void add(Node node){
if(node == null){
return;
}
//判断传入的结点值,和当前子树的根节点的值关系
if(node.value < this.value ){
if(this.left == null){//如果当前结点左子结点为null
this.left = node;
}else{
this.left.add(node);
}
}else{//添加的结点大于当前结点值
if(this.right == null){//如果当前结点右子结点为null
this.right = node;
}else{
this.right.add(node);
}
}
//添加完毕后 判断 如果当左右子树高度相差大于1
if(this.rightHeight() - this.leftHeight() > 1){
//如果当前结点的右子树的左子树高度 > 右子树的右子树点高度,
// 先要对右子节点进行一次右旋,然后对当前结点进行左旋转
if(this.right != null && this.right.leftHeight() > this.left.rightHeight()){
this.right.rightRotate();
}
this.leftRotate();//左旋转
return;//旋转玩直接退出
}
if(this.leftHeight() - this.rightHeight() > 1){
//如果当前结点的左子树的左子树高度 < 左子树的右子树结点高度,
// 先要对当前结点的左节点进行一次左旋
if(this.left != null && this.left.leftHeight() < this.left.rightHeight()){
this.left.leftRotate();
}
this.rightRotate();//右旋转
return;
}
}
//中序遍历方法
public void midOrder(){
if(this == null){
return;
}
if(this.left != null){
this.left.midOrder();
}
System.out.println(this);
if(this.right != null){
this.right.midOrder();
}
}
/**
* 查找到要删除的结点
* @param value 想要删除结点的值
* @return 找到返回该结点,否则返回null
*/
public Node searchDel(int value){
if(this.value == value){
return this;
}else if(value < this.value && this.left != null){
return this.left.searchDel(value);
}else if( this.right != null){ //value > this.value && 可以省略
return this.right.searchDel(value);
}
return null;
}
/**
* 查找到要删除结点的父节点
* @param value 想要删除结点的父节点
* @return 找到返回该结点,否则返回null
*/
public Node searchParent(int value){
//如果当前结点是要删除结点的父节点 就返回
if( (this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)){
return this;
}else{
//如果要查找到值小于当前结点值,且左子结点不为空
if(value < this.value && this.left != null){
return this.left.searchParent(value);
}else if(value > this.value && this.right != null){
return this.right.searchParent(value);
}else{
return null;
}
}
}
}