平衡二叉树的删与我前一篇文章《排序二叉树的思路记录》一致,在此不再描述。
今天来谈谈平衡二叉树的添加,它与排序二叉树有所不同,在每次添加后,有两种情况
情况一、如果左子节点的高度-右子节点的高度>1,则表明左边长,需要右旋转。(这属于单旋转内容),当左子节点的右边的高度>左子节点的左边的高度,则还需要先将左子节点进行左旋转。(这属于双旋转内容) 216-222行代码
情况二、如果右子节点的高度-左子节点的高度>1,则表明右边长,需要左旋转。(这属于单旋转内容),当右子节点的左边的高度>左子节点的右边的高度,则还需要先将右子节点进行右旋转。 (这属于双旋转内容) 224-230行代码
对于为什么会产生双旋转的内容,可以看文章后面进行刨析。
方法阐述:
①leftRoute:当前节点的左子节点长,右旋转
②RightRoute:当前节点的右子节点长,左旋转
③height:当前节点的高度
④RightHeight:当前节点的右子节点高度
⑤leftHeight:当前节点的左子节点高度
现在介绍为什么会有双旋转的内容:
先看图片,如果对于10的左子节点的右边>10的左子节点的左边,还不进行双旋转会产生的后果,就是单旋转后,还是不符合平衡二叉树的规范,所以我们需要先将10的左子节点(节点7先进行左旋转,然后再进行节点10的右旋转就可以变成图二的样子)
最后贴上实现代码:
package ysh.tree.AvlTree;
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, 11, 7, 6, 8, 9 };
//创建一个 AVLTree 对象
AvlTree avlTree = new AvlTree();
//添加结点
for(int i=0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
//遍历
System.out.println("前序遍历");
avlTree.PreOrder();
System.out.println("在平衡处理~~");
System.out.println("树的高度=" + avlTree.getRoot().height()); //3
System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight()); // 2
System.out.println("树的右子树高度=" + avlTree.getRoot().RightHeight()); // 2
System.out.println("当前的根结点=" + avlTree.getRoot());//8
}
}
class AvlTree {
private Node root;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
public void add(Node node){
if(root==null){
root=node;
}else{
root.add(node);
}
}
public void PreOrder() {
if(root==null){
System.out.println("树没有节点");
}else{
root.PreOrder();
}
}
//查找要删除的结点
public Node findNode(int value) {
if(root == null) {
return null;
} else {
return root.findNode(value);
}
}
//查找父结点
public Node findParentNode(int value) {
if(root == null) {
return null;
} else {
return root.findParentNode(value);
}
}
public void delNode(int i) {
if(root==null){
System.out.println("树没有节点可以删了");
}else if(root.getLeft()==null&&root.getRight()==null){//整个树只有root这个节点可以删除
root=null;
}else{
Node node = findNode(i);
if(node==null){
System.out.println("没有可以删除的节点");
}else{
Node parentNode = findParentNode(i);
if(parentNode==null){//删除节点有四种情况①(删除的是父节点)如果没有这个父节点,则只有一种可能,就是父节点是要删除的节点,属于有两个子节点的情况,不过也有特殊,需要改root
root.setValue(findMax(root.getLeft()));
}else{//删除子节点的另外三种情况,②删除的节点是叶子节点③删除的节点有两个子节点④删除的节点有一个子节点
if(node.getLeft()==null&&node.getRight()==null) {//第二种情况
if(node.getValue()<parentNode.getValue()){
parentNode.setLeft(null);
}else{
parentNode.setRight(null);
}
}else if(node.getLeft()!=null&&node.getRight()!=null){//第三种
int max = findMax(node.getLeft());
node.setValue(max);
}else{//第四种,也分四种情况,1.1 要删除的节点在父节点的左边且要删除的节点下面的节点在左边 1.2要删除的节点在父节点的左边且要删除的节点下面的节点在右边
//2.1 要删除的节点在父节点的右边且要删除的节点下面的节点在左边 2.2要删除的节点在父节点的右边且要删除的节点下面的节点在右边
if(node.getValue()>parentNode.getValue()){ //2.1 2.2的情况
if(node.getLeft()==null){
parentNode.setRight(node.getRight());
}else{
parentNode.setRight(node.getLeft());
}
}else{ //1.1 1.2的情况
if(node.getLeft()==null){
parentNode.setLeft(node.getRight());
}else{
parentNode.setLeft(node.getLeft());
}
}
}
}
}
}
}
/**
* 当删除有两个子节点的节点时,要么找到左子树的最大值,要么找到右子树的最小值
* @param node 以node为子树
* @return 找到以node为子树的,其子树下面的最大值
*/
public int findMax(Node node){
Node temp=node;
while(temp.getRight()!=null){
temp=temp.getRight();
}
delNode(temp.getValue());
return temp.getValue();
}
}
class Node {
private int value;
private Node left;
private Node right;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
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{" +
"value=" + value +
'}';
}
//左子树高度
public int leftHeight(){
if(left==null){
return 0;
}else{
return left.height();
}
}
//右子树高度
public int RightHeight(){
if(right==null){
return 0;
}else{
return right.height();
}
}
public int height(){
return Math.max(left==null?0:left.height(),right==null?0:right.height())+1;
}
//左旋转
public void leftRoute(){
Node newnode = new Node(value);
newnode.setLeft(left);
newnode.setRight(right.left);
value=right.value;
right=right.right;
left=newnode;
}
//右旋转
public void RightRoute(){
Node newnode = new Node(value);
newnode.setRight(right);
newnode.setLeft(left.right);
value=left.value;
left=left.left;
right=newnode;
}
public void add(Node root){
//如果要添加的节点小于当前节点
if(root.value<this.value){
if(this.left==null){//如果为空,则直接插入
this.left=root;
}else{//如果不为空,则往左递归
left.add(root);
}
}else{//要添加的节点大于等于当前节点
if(this.right==null){//如果为空,则直接插入
this.right=root;
}else{//如果不为空,则往右递归
right.add(root);
}
}
if(leftHeight()-RightHeight()>1){
//左边长,右旋转
//如果左边子节点的右边长,则又需要子节点左旋转先,形成双旋转模型,否则就会旋转过去后还是不符合
if(left!=null&&left.RightHeight()>left.leftHeight()){
left.leftRoute();
}
RightRoute();
}else if(RightHeight()-leftHeight()>1){
//右边长,左旋转
//如果右边子节点的左边长,则又需要子节点右旋转先,形成双旋转模型,否则就会旋转过去后还是不符合
if(right!=null&&right.leftHeight()>right.RightHeight()){
right.RightRoute();
}
leftRoute();
}
}
public void PreOrder() {
System.out.println(this);
if(left!=null){
left.PreOrder();
}
if(right!=null){
right.PreOrder();
}
}
/**
* 找到要删除的节点
* @param i 要删除节点的值
* @return 返回要删除的节点
*/
public Node findNode(int i){
if(this.value==i){
return this;
}
if(i<this.value){//要找的值小于当前值
if(left!=null){
return left.findNode(i);
}
}else{//要找的值大于等于当前值
if(right!=null){
return right.findNode(i);
}
}
return null;
}
/**
*
* @param i 要删除节点的值
* @return 要删除节点的父节点
*/
public Node findParentNode(int i){
if((left!=null&&left.value==i)||(right!=null&&right.value==i)){//如果当前节点的左右节点不为空且是要找到值,则直接返回找到的父节点
return this;
}
if(i<this.value){//如果要删除的值小于当前节点
if(left!=null) {
return left.findParentNode(i);
}
}else{//如果要删除的值大于等于当前节点
if(right!=null) {
return right.findParentNode(i);
}
}
return null;
}
}