日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

java avl_Java底层实现AVL 平衡二叉树

發(fā)布時(shí)間:2023/11/27 生活经验 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java avl_Java底层实现AVL 平衡二叉树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1、為什么要有AVL平衡二叉樹

我們之前寫過文章叫做BST二分搜索樹,這種數(shù)據(jù)結(jié)構(gòu)有一個(gè)缺點(diǎn)就是會(huì)退化為鏈表形式,到這導(dǎo)致了我們的樹結(jié)構(gòu)發(fā)揮不出來它應(yīng)有的優(yōu)勢。

從上圖可以發(fā)現(xiàn)如果按照順序進(jìn)行添加操作,那么二分搜索樹就會(huì)退化為鏈表形式,樹結(jié)構(gòu)也就失去了它的意義。

AVL(Adelson-Velsky-Landis Tree)是以創(chuàng)造者的名字命名的。這種樹結(jié)構(gòu)就是一種平衡二叉樹。它是最早的認(rèn)為可以自平衡的一種樹結(jié)構(gòu)。

2、什么是AVL平衡二叉樹

我們之前寫過的滿二叉樹、完全二叉樹、線段樹、最大堆等等都是一種平衡樹的例子(葉子節(jié)點(diǎn)的高度差不會(huì)大于 1 )。其實(shí)上面的平衡二叉樹都是比較理想的例子。但是在AVL樹中維護(hù)的平衡二叉樹有所不同。對(duì)于任意一個(gè)節(jié)點(diǎn),左子樹和右子樹的高度差不能超過 1

上面的規(guī)則相對(duì)來說更加寬松一些。比如下面這張圖:

這個(gè)結(jié)構(gòu)并不滿足我們之前的平衡二叉樹的規(guī)則,根節(jié)點(diǎn)的左右子樹高度差不大于 1 。卻滿足我們上面的規(guī)則(對(duì)于任意節(jié)點(diǎn))。

對(duì)于時(shí)間復(fù)雜度方面,平衡二叉樹的高度和節(jié)點(diǎn)數(shù)量之間也是O(log (N) )。

3、AVL樹的基本實(shí)現(xiàn)

3.1、實(shí)現(xiàn)的方法

首先,我們需要標(biāo)注每個(gè)節(jié)點(diǎn)的高度信息和平衡因子,平衡因子的計(jì)算就是左子樹的高度減去右子樹的高度的絕對(duì)值。這樣我們就可以依靠平衡因子來維護(hù)的AVL樹結(jié)構(gòu)。

3.2、構(gòu)造函數(shù)

我們首先需要構(gòu)造一個(gè)節(jié)點(diǎn)信息作為內(nèi)部類,主要包含我們要存儲(chǔ)的內(nèi)容,左右子樹的指針,還有高度信息。對(duì)于平衡因子我們可以使用左右子樹的差來進(jìn)行計(jì)算。 節(jié)點(diǎn)信息:

private class Node{ //內(nèi)部類 public K key;

public V value;

public Node left, right;

public int height;

public Node(K key, V value){

this.key = key;

this.value = value;

left = null;

right = null;

height = 1; //新節(jié)點(diǎn)高度為 1 }

構(gòu)造函數(shù):

private Node root;

private int size;

public AVLTree(){

root = null;

size = 0;

}

3.3、基本成員函數(shù)

首先我們需要獲取樹結(jié)構(gòu)的大小信息getSize()方法和判斷是否為空isEmpty()方法。

程序?qū)崿F(xiàn):

public int getSize() {

return size;

}

public boolean isEmpty() {

return size == 0;

}

因?yàn)槲覀冃枰叨刃畔砼袛鄻浣Y(jié)構(gòu),所以引入getHeight()方法。

程序?qū)崿F(xiàn):

private int getHeight(Node node) {

if (node == null)

return 0;

return node.height;

}

為了方便我們后續(xù)的操作,我們引入getNode()方法,通過索引key值來獲得節(jié)點(diǎn)。

程序?qū)崿F(xiàn):

private Node getNode(K key) {

return getNode(root, key);

}

private Node getNode(Node node, K key) {

if (node == null)

return null;

if (node.key.compareTo(key) < 0)

return getNode(node.left, key);

else if (node.key.compareTo(key) > 0)

return getNode(node.right, key);

else

return node;

}

在添加操作的時(shí)候,我們需要根據(jù)高度信息來獲得平衡因子,進(jìn)而判斷樹結(jié)構(gòu)是否滿足平衡樹的性質(zhì)。大于0:偏左,小于0:偏右

程序?qū)崿F(xiàn):

private int getBalanceFactor(Node node){

if (node == null)

return 0;

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

}

4、左旋轉(zhuǎn)和右旋轉(zhuǎn)

我們知道原來平衡的樹變成不平衡會(huì)是在添加元素的時(shí)候,所以在添加元素的時(shí)候我們需要維護(hù)平衡性。下面就分四種情況分別討論:

4.1、LL 右旋轉(zhuǎn)

有一種添加元素后的情況是這樣的。

可以認(rèn)為添加的元素在節(jié)點(diǎn)的左邊(L)的右邊(L)。 在添加元素2,后會(huì)導(dǎo)致樹結(jié)構(gòu)的平衡性破壞,對(duì)于這種情況的判斷就是當(dāng)前節(jié)點(diǎn)的平衡因子大于1(向左偏斜),并且左節(jié)點(diǎn)的平衡因子大于0(向左偏斜),這樣就保證了這種情況的出現(xiàn),向左偏斜。if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

那么我們就需要對(duì)上面的結(jié)構(gòu)進(jìn)行維護(hù)。進(jìn)行右旋轉(zhuǎn)操作。

為了不失一般性,我們引入更復(fù)雜的情況,如下圖:

按照平衡二叉樹的排序規(guī)則,對(duì)元素進(jìn)行右旋轉(zhuǎn)(類似于 y 繞 x 右旋轉(zhuǎn)),相當(dāng)于降低樹的高度。旋轉(zhuǎn)后仍然滿足二叉樹的排序規(guī)則。我們可以發(fā)現(xiàn)相對(duì)位置發(fā)生改變的就是節(jié)點(diǎn) y 和 x 的右子樹 T3 。最后更新高度信息,高度發(fā)生變化的只有節(jié)點(diǎn) x , y 。 程序?qū)崿F(xiàn):

private Node rightRotate(Node y) {

Node x = y.left; //獲得旋轉(zhuǎn)中心 Node T3 = x.right;

x.right = y; //進(jìn)行旋轉(zhuǎn)操作 y.left = T3;

y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; //重新更新高度信息 x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

return x;

}

4.2、RR 左旋轉(zhuǎn)

我們有了上面的右旋轉(zhuǎn)的概念,那么左旋轉(zhuǎn)就變得簡單多了,左旋轉(zhuǎn)的發(fā)生的前提就是節(jié)點(diǎn)的平衡因子小于-1(偏右),右子樹的平衡因子小于0(偏右),也就是類似于下面的結(jié)構(gòu)。 可以認(rèn)為添加的元素在節(jié)點(diǎn)的右邊(R)的右邊(R)。if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

下面就是對(duì)結(jié)構(gòu)進(jìn)行左旋轉(zhuǎn)操作,維護(hù)平衡性。

以x為中心,進(jìn)行左旋轉(zhuǎn)操作(類似于 y 繞 x 左旋轉(zhuǎn)),需要轉(zhuǎn)移的分別是 x 的左子樹 T2 和節(jié)點(diǎn) y 。 程序?qū)崿F(xiàn):

private Node leftRotate(Node y) {

Node x = y.right;

Node T2 = x.left;

x.left = y; //需要轉(zhuǎn)移的元素 y.right = T2;

y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; // 更新高度信息 x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

return x;

}

4.3、LR 左右旋轉(zhuǎn)

這種情況就是添加元素在節(jié)點(diǎn)的左邊的右邊,左右旋轉(zhuǎn)指的是先進(jìn)行左旋轉(zhuǎn),后進(jìn)行右旋轉(zhuǎn)。類似于下面這種情況。

具體細(xì)節(jié)的旋轉(zhuǎn)如下:

先進(jìn)行左旋轉(zhuǎn)操作,結(jié)構(gòu)就會(huì)變成我們之前LL的形式,然后對(duì)其進(jìn)行右旋轉(zhuǎn)操作即可。

4.4、RL 右左旋轉(zhuǎn)

這種情況就是添加元素在節(jié)點(diǎn)的右邊的左邊,右左旋轉(zhuǎn)指的是先進(jìn)行右旋轉(zhuǎn),后進(jìn)行左旋轉(zhuǎn)。類似于下面這種情況。

具體細(xì)節(jié)的旋轉(zhuǎn)如下: 先進(jìn)行左旋轉(zhuǎn)操作,結(jié)構(gòu)就會(huì)變成我們之前LL的形式,然后對(duì)其進(jìn)行右旋轉(zhuǎn)操作即可。

4.5、四種情況總結(jié)

上面的四種情況完全包含了添加元素所需要的情況。

// LLif (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

return rightRotate(node);

// RRif (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

return leftRotate(node);

// LRif (balanceFactor > 1 && getBalanceFactor(node.left) < 0){

node.left = leftRotate(node.left);

return rightRotate(node);

}

// RLif (balanceFactor < -1 && getBalanceFactor(node.right) > 0){

node.right = rightRotate(node.right);

return leftRotate(node);

}

5、增刪改查操作的實(shí)現(xiàn)

5.1、添加操縱

步驟:遞歸到底的時(shí)候添加元素,否則就更新元素

更新每個(gè)節(jié)點(diǎn)的高度信息 + 對(duì)結(jié)構(gòu)進(jìn)行平衡處理

private Node add(Node node, K key, V value) {

/** BST 的源代碼片段 ····*/

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

int balanceFactor = getBalanceFactor(node);

// LL if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

return rightRotate(node);

// RR if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

return leftRotate(node);

// LR if (balanceFactor > 1 && getBalanceFactor(node.left) < 0){

node.left = leftRotate(node.left);

return rightRotate(node);

}

// RL if (balanceFactor < -1 && getBalanceFactor(node.right) > 0){

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

5.2、刪除操作

這里的刪除操作,在原本BST二分搜索樹的基礎(chǔ)上進(jìn)行改變,增加刪除元素后對(duì)節(jié)點(diǎn)進(jìn)行平衡后的處理,同添加操作基本相同,這里只對(duì)增加的程序片段進(jìn)行展示。

private Node remove(Node node, K key) {

/** BST 的源代碼片段 ····*/

if (retNode == null)

return null;

retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right)); //更新高度值 int balanceFactor = getBalanceFactor(retNode);

// LL if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)

return rightRotate(retNode);

// RR if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)

return leftRotate(retNode);

// LR if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){

retNode.left = leftRotate(retNode.left);

return rightRotate(retNode);

}

// RL if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){

retNode.right = rightRotate(retNode.right);

return leftRotate(retNode);

}

return retNode;

}

5.3、查詢操作

查詢操作主要包含查看元素是否在樹結(jié)構(gòu)中,另一個(gè)就是通過key值查找對(duì)應(yīng)的 value 值。兩者均是借助getNode方法進(jìn)行實(shí)現(xiàn)。 程序?qū)崿F(xiàn):

public boolean contains(K key) {

return getNode(key) != null;

}

public V get(K key) {

Node node = getNode(key);

return node == null ? null : node.value;

}

5.4、更改操作

更改操作也是借助getNode方法進(jìn)行更改。

public void set(K key, V newValue) {

Node node = getNode(key);

if (node != null)

node.value = newValue;

else

throw new IllegalArgumentException(key + "doesn't exist");

}

最后

更多精彩內(nèi)容,大家可以轉(zhuǎn)到我的主頁:源碼地址在主頁內(nèi)哦~~首頁 | 曲怪曲怪?quguai.cn

總結(jié)

以上是生活随笔為你收集整理的java avl_Java底层实现AVL 平衡二叉树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。