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

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--面试必问AVL树原理及实现

發(fā)布時(shí)間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--面试必问AVL树原理及实现 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

數(shù)據(jù)結(jié)構(gòu)與算法–AVL樹(shù)原理及實(shí)現(xiàn)

  • AVL(Adelson-Velskii 和landis)樹(shù)是帶有平衡條件的二叉查找樹(shù),這個(gè)平衡條件必須容易實(shí)現(xiàn),并且保證樹(shù)的深度必須是O(logN)。因此我們讓一棵AVL樹(shù)中每個(gè)節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)的高度最多相差1(空樹(shù)高度定義-1)如下圖,左邊是AVL樹(shù),右邊不是AVL樹(shù)。
  • 左圖中3 節(jié)點(diǎn)左子樹(shù)高度0,右子樹(shù)高度1,相差不超過(guò)1。
  • 右圖中節(jié)點(diǎn)3,左子樹(shù)蓋度0,右子樹(shù)高度2,相差超過(guò)1,不滿(mǎn)足AVL數(shù)要求。
  • 我們可以在每一個(gè)節(jié)點(diǎn)中保留高度信息,那我們可以推算出如下 高度h 與節(jié)點(diǎn)之間關(guān)系的公式
    • 在高度h的AVL樹(shù)中,最少節(jié)點(diǎn)數(shù)S(h)由S(h)=S(h-1)+S(h-2)+1標(biāo)識(shí)
    • 對(duì)于h,S(h)=1; h=1, S(h)=2,明顯函數(shù)S(h)與斐波那契數(shù)列密切相關(guān),由此我們可以推算出AVL數(shù)的高度的界限1.44log(N+2)-1.328。
insert分析
  • 經(jīng)過(guò)如上高度,限制條件分析,我們可以認(rèn)為,AVL樹(shù)操作都可以以時(shí)間O(logN)執(zhí)行。當(dāng)插入操作時(shí)候,我們需要更通向根節(jié)點(diǎn)路徑上那些節(jié)點(diǎn)的所有平衡信息,而插入操作比較棘手的地方子阿姨,一個(gè)插入節(jié)點(diǎn)可能破壞AVL的平衡性。

    • 上圖中左圖插入數(shù)據(jù)5,變成右圖,平衡性改變,不符合AVL樹(shù)特性
  • 因此我們需要考慮在insert之后恢復(fù)平衡性,這種操作總可以通過(guò)樹(shù)進(jìn)行簡(jiǎn)單的修正做到,我們成為旋轉(zhuǎn)。

  • 我們將insert后必須重新平衡的節(jié)點(diǎn)記錄為a。由于任意節(jié)點(diǎn)最多兩個(gè)兒子,因此出現(xiàn)高度不平衡需要a的左右子樹(shù)的高度相差2,容易推出如下四種情況:

    • 對(duì)a的左子樹(shù)的左子樹(shù)進(jìn)行一次insert
    • 對(duì)a的左子樹(shù)的右子樹(shù)進(jìn)行一次insert
    • 對(duì)a的右子樹(shù)的左子樹(shù)進(jìn)行一次insert
    • 對(duì)a的右子樹(shù)的右子樹(shù)進(jìn)行一次insert
  • 上面四點(diǎn)中 第一點(diǎn)與第四點(diǎn)鏡像對(duì)稱(chēng),第二點(diǎn)和第三點(diǎn)鏡像對(duì)稱(chēng),理論上我們只需要實(shí)現(xiàn)兩種就可以同樣的實(shí)現(xiàn)方法得出對(duì)稱(chēng)的方法。

  • 第一種情況發(fā)生在外側(cè)情況(左左或者右右),該情況通過(guò)一次單旋轉(zhuǎn)可以完成跳轉(zhuǎn)。

  • 第二中情況發(fā)生在內(nèi)部(左右或者右左),這種情況需要復(fù)雜一些的雙旋轉(zhuǎn)。

單旋轉(zhuǎn)

  • 如上圖左圖中的樹(shù)節(jié)點(diǎn)4不滿(mǎn)足AVL樹(shù),因?yàn)樗淖笞訕?shù)的深度比右子樹(shù)深2層,改圖中描述的情況是第一點(diǎn)1中描述的情況。在插入2 節(jié)點(diǎn)之后,原來(lái)滿(mǎn)足AVL數(shù)的性質(zhì)遭到了破壞,子樹(shù) 3多出了一層我們需要調(diào)整節(jié)點(diǎn)然他在平衡。
  • 我們將3節(jié)點(diǎn)上移,并且將4節(jié)點(diǎn)下移,得到右圖中所示的新的樹(shù)滿(mǎn)足AVL特性
  • 我們可以形象的將樹(shù)看成是柔軟的,抓住3 節(jié)點(diǎn)向上提在重力的作用下3節(jié)點(diǎn)變成了新的根節(jié)點(diǎn)(子樹(shù)的根)。由于二叉查找樹(shù)的特性,4>3,
    • 所以4節(jié)點(diǎn)變成了3節(jié)點(diǎn)的右節(jié)點(diǎn)。
    • 如果3 有右節(jié)點(diǎn)x,那么3的右節(jié)點(diǎn)應(yīng)該變成4的左節(jié)點(diǎn),因?yàn)閤>3 并且x<4
    • 2的左右節(jié)點(diǎn)不變,4的右節(jié)點(diǎn)不變
      -我們?cè)趤?lái)插入一個(gè)數(shù)據(jù)1 ,得出如下結(jié)果:

  • 如上左圖我們插入1 后,破壞了5 根節(jié)點(diǎn)的平衡性,5節(jié)點(diǎn)的左子樹(shù)深度3, 右子樹(shù)深度1,超過(guò)2,因此我們應(yīng)該在3 節(jié)點(diǎn)處進(jìn)行如上步驟的操作得到最終的AVL數(shù)右圖:
    • 3節(jié)點(diǎn)編程根節(jié)點(diǎn),5節(jié)點(diǎn)編程3的右節(jié)點(diǎn)
    • 3的右節(jié)點(diǎn)交給5的左節(jié)點(diǎn)
    • 其他節(jié)點(diǎn)不變
雙旋轉(zhuǎn)
  • 上面案例中的算法小時(shí),在下圖找那個(gè)是無(wú)效的:

  • 如上左圖到右圖的變化,9節(jié)點(diǎn)在插入 7 節(jié)點(diǎn)后,時(shí)序平衡性,我們?cè)?與9節(jié)點(diǎn)之間作旋轉(zhuǎn),按上面描述的算法,得到右圖,還是非AVL數(shù)

  • 我們通過(guò)如上的實(shí)驗(yàn)得出5 和9 之間的旋轉(zhuǎn)無(wú)法解決問(wèn)題。也就是5, 9 作為根都無(wú)法得到一個(gè)平衡樹(shù),那么只能由6 節(jié)點(diǎn)作為跟節(jié)點(diǎn)

  • 如此的話(huà),節(jié)點(diǎn)的順序就一目了然了: 5節(jié)點(diǎn)是6左子樹(shù),9 是右子樹(shù),7 變?yōu)? 的左子樹(shù)。得到如下結(jié)果:

  • 我們繼續(xù)接著上面的案例插入 15, 14,13。插入13容易,因?yàn)樗粫?huì)破壞節(jié)點(diǎn)的平衡性,但是插入12 之后硬氣10 節(jié)點(diǎn)的高度不平衡,這個(gè)屬于上面描述的情況3 的案例:需要通過(guò)一次右-左雙旋來(lái)解決這個(gè)問(wèn)題。
  • 如上左圖中,右-左旋流程:
    • 我們先在15 節(jié)點(diǎn)和14節(jié)點(diǎn)之間做一次右旋轉(zhuǎn),我們?cè)谧鲞@次右旋轉(zhuǎn)流程時(shí)候,假設(shè)14節(jié)點(diǎn)左節(jié)點(diǎn)還有一個(gè)節(jié)點(diǎn)是我們假設(shè)的虛擬節(jié)點(diǎn)
    • 那么此時(shí)15 節(jié)點(diǎn)就滿(mǎn)足上述情況中的第一點(diǎn)左 左描述情況,那么我們用右旋得到如下圖情況:
  • 如上圖我們完成了第一次右邊選擇,圖中虛線(xiàn)表示虛擬節(jié)點(diǎn)不存在
  • 那么我們?cè)倏创藭r(shí)10 節(jié)點(diǎn)也是不符合平衡性質(zhì),而且恰好此時(shí)滿(mǎn)足第四點(diǎn)情況描述的右右的描述
  • 那么我們按照之前的算法描述需要對(duì)10,14 節(jié)點(diǎn)之間進(jìn)行左旋得到如下結(jié)果

總結(jié)
  • 至此我們對(duì)上面兩種旋轉(zhuǎn)做一個(gè)總結(jié),為了將項(xiàng)X的一個(gè)新節(jié)點(diǎn)插入到一個(gè)AVL樹(shù)中,
    • 我們遞歸的將X插入到T數(shù)對(duì)應(yīng)的子樹(shù)位置,記錄改子樹(shù)為T(mén)_lr
    • 如果T_lr的高度不變那么插入完成
    • 如果T中出現(xiàn)高度不平衡,則根據(jù)X以及T和T_lr中項(xiàng)做適當(dāng)?shù)膯涡蛘唠p旋來(lái)更新這些高度
    • 解決旋轉(zhuǎn)之后其他節(jié)點(diǎn)的歸屬問(wèn)題
  • 經(jīng)過(guò)如上分析,我們給出以下實(shí)現(xiàn)(二叉查找樹(shù)上一節(jié)已經(jīng)實(shí)現(xiàn),在此基礎(chǔ)上進(jìn)行修改):
算法實(shí)現(xiàn)
  • 節(jié)點(diǎn)定義,還是和上一節(jié)二叉查找樹(shù)中類(lèi)似,只不過(guò)我們?cè)诠?jié)點(diǎn)中需要維護(hù)一個(gè)高度信息,并且修改了comparable方法。如下實(shí)現(xiàn):
/*** 二叉樹(shù)節(jié)點(diǎn)對(duì)象定義** @author liaojiamin* @Date:Created in 15:24 2020/12/11*/ public class BinaryNode implements Comparable {private Object element;private BinaryNode left;private BinaryNode right;/*** 樹(shù)高度*/private int height;private int count;public BinaryNode(Object element, BinaryNode left, BinaryNode right) {this.element = element;this.left = left;this.right = right;this.count = 1;this.height = 0;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public Object getElement() {return element;}public void setElement(Object element) {this.element = element;}public BinaryNode getLeft() {return left;}public void setLeft(BinaryNode left) {this.left = left;}public BinaryNode getRight() {return right;}public void setRight(BinaryNode right) {this.right = right;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}@Overridepublic int compareTo(Object o) {if (o == null) {return 1;}int flag;if (o instanceof Integer) {int myElement = (int) this.element - (int) o;flag = myElement > 0 ? 1 : myElement == 0 ? 0 : -1;} else {flag = this.element.toString().compareTo(o.toString());}if (flag == 0) {return 0;} else if (flag > 0) {return 1;} else {return -1;}} }
  • 單左旋,單右旋代碼實(shí)現(xiàn):
/*** 左單旋一次* */private BinaryNode rotateWithLeftChild(BinaryNode k2){BinaryNode k1 = k2.getLeft();k2.setLeft(k1.getRight());k1.setRight(k2);k2.setHeight(Math.max(height(k2.getLeft()), height(k2.getRight())) + 1);k1.setHeight(Math.max(height(k1.getLeft()), height(k2)) + 1);return k1;}/*** 右單旋一次* */private BinaryNode rotateWithRightChild(BinaryNode k2){BinaryNode k1 = k2.getRight();k2.setRight(k1.getLeft());k1.setLeft(k2);k2.setHeight(Math.max(height(k2.getLeft()), height(k2.getRight())) + 1);k1.setHeight(Math.max(height(k2), height(k1.getRight())) + 1);return k1;}
  • 如上代碼實(shí)現(xiàn),用下圖說(shuō)明,6節(jié)點(diǎn)因?yàn)? 的插入破壞平衡性
    • 我們?cè)?節(jié)點(diǎn)和5節(jié)點(diǎn)之前進(jìn)行選擇
    • 5節(jié)點(diǎn)成為新根節(jié)點(diǎn)
    • 6 節(jié)點(diǎn)成為5節(jié)點(diǎn)的右節(jié)點(diǎn)
    • 5節(jié)點(diǎn)的右節(jié)點(diǎn)編程6節(jié)點(diǎn)的左節(jié)點(diǎn)
    • 和如上左旋流程一樣,達(dá)到目的

  • 雙左旋,雙右旋轉(zhuǎn)實(shí)現(xiàn):
/*** 右旋轉(zhuǎn) 接左旋轉(zhuǎn),雙旋轉(zhuǎn)* */private BinaryNode doubleWithLeftChild(BinaryNode k3){k3.setLeft(rotateWithRightChild(k3.getLeft()));return rotateWithLeftChild(k3);}/*** 左旋轉(zhuǎn) 接右旋轉(zhuǎn),雙旋轉(zhuǎn)* */private BinaryNode doubleWithRightchild(BinaryNode k3){k3.setRight(rotateWithLeftChild(k3.getRight()));return rotateWithRightChild(k3);}
  • 雙旋的情況我們將他看出是兩段,兩段分別進(jìn)行單旋操作:

    • 第一步,如下圖,有K2節(jié)點(diǎn)進(jìn)行右旋得到右圖中樹(shù)結(jié)構(gòu)
  • 第二步,對(duì)k3進(jìn)行左旋

  • 刪除操作,添加操作:添加操作破壞平衡性,之后在糾正,由于二叉查找樹(shù)的刪除比插入更加復(fù)雜,英雌AVL刪除也同樣,我們也可以和insert方法一樣,在insert之后在糾正平衡性,這樣就可以在二叉查找樹(shù)的基礎(chǔ)上進(jìn)行很簡(jiǎn)單的實(shí)現(xiàn):
/*** 插入節(jié)點(diǎn)** @author: liaojiamin* @date: 15:48 2020/12/15*/private BinaryNode insert(Object x, BinaryNode t) {if (x == null) {return t;}if (t == null || t.getElement() == null) {t = new BinaryNode(x, null, null);return t;}int flag = t.compareTo(x);if (flag > 0) {t.setLeft(insert(x, t.getLeft()));} else if (flag < 0) {t.setRight(insert(x, t.getRight()));} else {t.setCount(t.getCount() + 1);}return balance(t);}/*** 刪除節(jié)點(diǎn)** @author: liaojiamin* @date: 15:48 2020/12/15*/private BinaryNode remove(Object x, BinaryNode t) {if (x == null) {return t;}int flag = t.compareTo(x);if (flag > 0) {return remove(x, t.getLeft());} else if (flag < 0) {return remove(x, t.getRight());} else if (t.getLeft() != null && t.getRight() != null) {//找到對(duì)應(yīng)節(jié)點(diǎn),將節(jié)點(diǎn)右子樹(shù)下面最小的值替換當(dāng)前值BinaryNode min = findMin(t.getRight());t.setElement(min.getElement());//遞歸刪除右子樹(shù)下最小值remove(min.getElement(), t.getRight());} else {//找到對(duì)應(yīng)節(jié)點(diǎn),但是當(dāng)前節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn)// 遞歸思想:只考慮最簡(jiǎn)單情況,當(dāng)只有當(dāng)前節(jié)點(diǎn)與其左子節(jié)點(diǎn),刪除當(dāng)前節(jié)點(diǎn)返回當(dāng)節(jié)點(diǎn)左子節(jié)點(diǎn),右節(jié)點(diǎn)同理t = t.getLeft() != null ? t.getLeft() : t.getRight();}return balance(t);}
  • 平衡性糾正方法:
private static final int MAXBALANCE_HEIGH = 1;/*** 平衡查找二叉樹(shù)** */public BinaryNode balance(BinaryNode t){if(t == null){return t;}if(height(t.getLeft()) - height(t.getRight()) > MAXBALANCE_HEIGH){if(height(t.getLeft().getLeft()) >= height(t.getLeft().getRight())){t = rotateWithLeftChild(t);}else {t = doubleWithLeftChild(t);}}else if(height(t.getRight()) - height(t.getLeft()) > MAXBALANCE_HEIGH ){if(height(t.getRight().getRight()) >= height(t.getRight().getLeft())){t = rotateWithRightChild(t);}else {t = doubleWithRightchild(t);}}t.setHeight(Math.max(height(t.getLeft()), height(t.getRight())) + 1);return t;}
  • 其他方法:
/*** 按順序打印節(jié)點(diǎn)信息:左中右** @author: liaojiamin* @date: 15:48 2020/12/15*/public void printTree(BinaryNode t) {if (t == null || t.getElement() == null) {return;}printTree(t.getLeft());for (int i = 0; i < t.getCount(); i++) {System.out.print(t.getElement() + " ");}printTree(t.getRight());}/*** 獲取樹(shù)高度* */private int height(BinaryNode t){return t == null ? -1 : t.getHeight();}public void makeEmpty(BinaryNode root) {root = null;}public boolean isEmpty(BinaryNode root) {return root == null;}public static void main(String[] args) {BinaryNode node = new BinaryNode(null, null, null);AvlTree searchTree = new AvlTree();Random random = new Random();for (int i = 0; i < 20; i++) {node = searchTree.insert(random.nextInt(100), node);}System.out.println(searchTree.findMax(node).getElement());System.out.println(searchTree.findMin(node).getElement());searchTree.printTree(node);if(!searchTree.contains(13, node)){node = searchTree.insert(13, node);}System.out.println(searchTree.contains(13, node));node = searchTree.remove(13, node);System.out.println(searchTree.contains(13, node));}/*** 節(jié)點(diǎn)元素是否存在** @author: liaojiamin* @date: 15:48 2020/12/15*/private boolean contains(Object x, BinaryNode t) {if (x == null) {return false;}if (t == null) {return false;}int flag = t.compareTo(x);if (flag > 0) {return contains(x, t.getLeft());} else if (flag < 0) {return contains(x, t.getRight());} else {return true;}}/*** 查找最小元素節(jié)點(diǎn)** @author: liaojiamin* @date: 15:48 2020/12/15*/private BinaryNode findMin(BinaryNode t) {if (t == null) {return null;}if (t.getLeft() != null) {return findMin(t.getLeft());}return t;}/*** 查找最大元素節(jié)點(diǎn)** @author: liaojiamin* @date: 15:48 2020/12/15*/private BinaryNode findMax(BinaryNode t) {if (t == null) {return null;}if (t.getRight() != null) {return findMax(t.getRight());}return t;}

上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–二叉查找樹(shù)實(shí)現(xiàn)原理
下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–二叉堆(最大堆,最小堆)實(shí)現(xiàn)及原理

總結(jié)

以上是生活随笔為你收集整理的数据结构与算法--面试必问AVL树原理及实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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