代码详解AVL树的插入

AVL树是一种自平衡二叉搜索树,它是通过维护每个节点的平衡因子来实现的。平衡因子是指该节点的左子树高度和右子树高度的差值。当AVL树中的节点插入或者删除后,可能会导致某个节点的平衡因子超过1,这时需要通过旋转操作来进行调整,使得整棵树重新恢复平衡。

1. 插入操作概述

插入操作是一种向AVL树中添加节点的操作。它与普通的二叉搜索树插入操作类似,但是需要在插入后对整棵树进行调整,以维护平衡性。

1.1 插入节点

首先,我们需要将新节点插入到AVL树中。插入的方法可以与普通的二叉搜索树插入方法相同。我们从根节点开始遍历,如果要插入的节点小于当前节点,则遍历其左子树,否则遍历其右子树,直到找到一个空的位置,将节点插入其中。

下面是AVL树的插入代码:

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

return node;

}

1.2 更新节点高度

当我们将新节点插入到AVL树中后,我们需要更新每个节点的高度。我们定义节点的高度为其左右子树高度的最大值加1。我们可以通过递归的方式更新每个节点的高度。我们先更新插入节点的高度,然后递归更新其父节点的高度,直到更新到根节点为止。

下面是更新节点高度的代码:

private int height(Node node)

{

if (node == null)

return 0;

return 1 + Math.max(height(node.left), height(node.right));

}

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

node.height = height(node);

return node;

}

1.3 平衡树

当我们将新节点插入到AVL树中并更新了节点高度之后,我们需要判断树是否失去了平衡。我们可以通过计算每个节点的平衡因子来判断树的平衡性。如果一个节点的平衡因子超过1,则需要通过旋转操作来进行调整。

对于一个节点node,其平衡因子bf=height(node.left)-height(node.right),如果bf超过1,则需要进行旋转操作。

下面是平衡树的代码:

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

node.height = height(node);

int balance = getBalance(node);

if (balance > 1 && key < node.left.key)

return rightRotate(node);

if (balance > 1 && key > node.left.key)

{

node.left = leftRotate(node.left);

return rightRotate(node);

}

if (balance < -1 && key > node.right.key)

return leftRotate(node);

if (balance < -1 && key < node.right.key)

{

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

private int getBalance(Node node)

{

if(node==null)

return 0;

return height(node.left)-height(node.right);

}

private Node rightRotate(Node y)

{

Node x = y.left;

Node T2 = x.right;

x.right = y;

y.left = T2;

y.height = height(y);

x.height = height(x);

return x;

}

private Node leftRotate(Node x)

{

Node y = x.right;

Node T2 = y.left;

y.left = x;

x.right = T2;

x.height = height(x);

y.height = height(y);

return y;

}

2. 插入操作详解

接下来,我们将详细地解释AVL树的插入操作,包括节点插入、更新节点高度和平衡树操作。

2.1 插入节点

在插入节点时,我们需要从根节点开始遍历AVL树,找到要插入的节点应该放置的位置。如果要插入的节点的键值小于当前节点的键值,则继续在当前节点的左子树中查找;否则,在当前节点的右子树中查找。一直查找到一个空的位置,将新节点插入其中。

下面是节点插入的代码:

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

return node;

}

我们使用递归的方式来实现节点插入。如果当前节点为空,则返回一个新的节点,并将要插入节点的键值设置为该新节点的键值。如果要插入的节点的键值小于当前节点的键值,则递归地在当前节点的左子树中插入该节点;否则,在当前节点的右子树中插入该节点。如果要插入的节点的键值与当前节点的键值相等,则直接返回当前节点。

2.2 更新节点高度

当新节点插入到AVL树中后,我们需要更新每个节点的高度。节点的高度是该节点的左子树高度和右子树高度的最大值加一。我们可以通过递归的方式更新节点高度。我们首先更新插入节点的高度,然后递归地更新其父节点的高度,直到更新到根节点为止。

下面是更新节点高度的代码:

private int height(Node node)

{

if (node == null)

return 0;

return 1 + Math.max(height(node.left), height(node.right));

}

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

node.height = height(node);

return node;

}

我们使用递归的方式来实现节点高度的更新。节点高度的计算方式为:1 + max(左子树高度,右子树高度)。如果节点为空,则节点高度为0。在节点插入后,我们需要递归地更新每个节点的高度,从插入节点开始,直到更新到根节点为止。

2.3 平衡树

当新节点插入到AVL树中并更新了节点高度之后,我们需要判断树是否失去平衡。我们可以通过计算每个节点的平衡因子来判断树的平衡性。如果一个节点的平衡因子超过1,则需要通过旋转操作来进行调整。

对于一个节点node,其平衡因子bf=height(node.left)-height(node.right),如果bf超过1,则需要进行旋转操作。我们可以通过四种旋转操作来调整AVL树的平衡:

1. 左旋转(左左子树不平衡):对于节点A,如果其左子树高度比右子树高度高2,即bf(A)=2,则进行左旋转操作。左旋转操作是指将节点A向左旋转,使得其右子节点B成为其新的父节点,A的左子节点C成为B的右子节点。左旋转操作可以将原来的树:

A              B

/ \ => / \

B D C A

/ \ / \

C E E D

2. 右旋转(右右子树不平衡):对于节点A,如果其右子树高度比左子树高度高2,即bf(A)=-2,则进行右旋转操作。右旋转操作是指将节点A向右旋转,使得其左子节点B成为其新的父节点,A的右子节点C成为B的左子节点。右旋转操作可以将原来的树:

A              B

/ \ => / \

D B A E

/ \ / \

E C D C

3. 左右旋转(左右子树不平衡):对于节点A,如果其左子树高度比右子树高度高2,且其左子节点B的右子树高度比其左子树高度高,即bf(B)=-1,则进行左右旋转操作。左右旋转就是先对B进行右旋转,然后再对A进行左旋转。左右旋转可以将原来的树:

A              A              C

/ \ => / \ => / \

B D C D B A

/ \ / / \

E C B E D

4. 右左旋转(右左子树不平衡):对于节点A,如果其右子树高度比左子树高度高2,且其右子节点B的左子树高度比其右子树高度高,即bf(B)=1,则进行右左旋转操作。右左旋转就是先对B进行左旋转,然后再对A进行右旋转。右左旋转可以将原来的树:

A              A              C

/ \ => / \ => / \

D B D C A B

/ \ \ / \

C E B D E

下面是平衡树的代码:

public Node insert(Node node, int key)

{

if (node == null)

return new Node(key);

if (key < node.key)

node.left = insert(node.left, key);

else if (key > node.key)

node.right = insert(node.right, key);

else

return node;

node.height = height(node);

int balance = getBalance(node);

if (balance > 1 && key < node.left.key)

return rightRotate(node);

if (balance > 1 && key > node.left.key)

{

node.left = leftRotate(node.left);

return rightRotate(node);

}

if (balance < -1 && key > node.right.key)

return leftRotate(node);

if (balance < -1 && key < node.right.key)

{

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

private int getBalance(Node node)

{

if(node==null)

return 0;

return height(node.left)-height(node.right);

}

private Node rightRotate(Node y)

{

Node x = y.left;

Node T2 = x.right;

x.right = y;

y.left = T2;

y.height = height(y);

x.height = height(x);

return x;

}

private Node leftRotate(Node x)

{

Node y = x.right;

Node T2 = y.left;

y.left = x;

x.right = T2;

x.height = height(x);

y.height = height(y);

return y;

}

在平衡树的代码中,我们首先计算了当前节点的平衡因子,即左子树高度减去右子树高度的差值。然后,我们根据平衡因子的不同,进行不同的旋转操作来恢复AVL树的平衡。

3. 总结

AVL树是一种自平衡二叉搜索树,它通过维护每个节点的平衡因子来保证树的平衡性。插入操作是向AVL树中添加节点的操作,它与普通的二叉搜索树插入操作类似,但是需要在插入后对整棵树进行调整,以维护平衡性。插入操作包括节点插入、更新节点高度和平衡树操作。在执行插入操作时,我们首先插入新节点,然后更新每个节点的高度,最后判断是否需要进行旋转操作来恢复AVL树的平衡。常用的旋转操作包括左旋转、右旋转、左右旋转和右左旋转。通过调整这些旋转操作的组合,我们可以维护AVL树的平衡,使得其具有较快的查找和插入性能。

后端开发标签