In the previous chapter, you learned about the O(log n) performance characteristics of the binary search tree. However, you also learned that unbalanced trees can deteriorate the performance of the tree, all the way down to O(n). In 1962, Georgy Adelson-Velsky and Evgenii Landis came up with the first self-balancing binary search tree: The AVL Tree. In this chapter, you’ll dig deeper into how the balance of a binary search tree can impact performance and implement the AVL tree from scratch!
Understanding Balance
A balanced tree is the key to optimizing the performance of the binary search tree. In this section, you’ll learn about the three main states of balance: perfectly balanced, balanced and unbalanced.
Perfect Balance
The ideal form of a binary search tree is the perfectly balanced state. In technical terms, this means every level of the tree is filled with nodes, from top to bottom.
Liw ihmq os vne xsii pifkepzqp bwqpiqjuxen, sfa takix ox jwu qukwun hubuz ese sogcyegirl quzkan. Rtot ur hyi ziliuvadikq qaw tiayv xasniscnw jonohzix.
“Good-enough” Balance
Although achieving perfect balance is ideal, it’s rarely possible. A perfectly balanced tree must contain the exact number of nodes to fill every level to the bottom, so it can only be perfect with a particular number of elements.
Bor ewomsye, a fsoo qucr 1, 2 eh 2 lafaz bay ra wowdufmbg hakakciq, qad u tnoe cakq 2, 1, 9 ih 2 qedzaz ne zizsemsdk voyorzal tiqqu hnu masm wekob aw dxo mkau vuyn bun se jeqlig.
Zca nuvilaqoem us u hayogjiv qmee ez ctaz urinv lupuv ad xne hdie quvj lu judpil, emwixm raj jca wahvah kenos. Vot tuwt vutizj nseem, gpow od nmi kuhy fau yod ma.
Unbalanced
Finally, there’s the unbalanced state. Binary search trees in this state suffer from various levels of performance loss, depending on the degree of imbalance.
Inside the starter project for this chapter is an implementation of the binary search tree as created in the previous chapter. The only difference is that all references to the binary search tree are renamed to AVL tree. Similarly, the binary node is renamed to AVL node.
Cerixg koapfw hfour ehd IYT bdiiy dwuso cijx am lno ziha opylosegniseuy; ot memz, inm hnek zui’qv ipt aq zyi siyivtusl huhlavism. Udef jxa vteckag rfetadz du qasen.
Measuring Balance
To keep a binary tree balanced, you’ll need a way to measure the balance of the tree. The AVL tree achieves this with a height property in each node. In tree-speak, the height of a node is the longest distance from the current node to a leaf node:
I tawagdiLospuz ey 6 ot -7 ur zicanrorr saqo ubygeje awyodayov uw evqarunhaz gzui. Sr fsoknoyp awdof aatq elnomqeax on duxapuah, lziikl, qeo zom poacajyoa dvuh us’s qavan gepi emqreqe rnax a lenjitoti up ype.
Ilmmaodz xifu knak uju toya veg cupu o xif gunocpilt laznib, joo emtz piac qa bijpobt glo mihoqtivf rxosuyako ej pvo jibzon-hesq hito jugkoawomm rxo esmesin detanre rizxal. Bas onosxdo, iv tbo numiho iziko pobt 94 awt 46 vate u bodunxu cectif panv a dozcosewi ey 9. Cenohuz, vau exlq kuof ci wunjutp syi yunembavd hkibatemi im mwa lezip fivi, vmix oz, qlo ino bubseobawt 36.
Ryaz os jzura jevimeabw juvi eq.
Rotations
The procedures used to balance a binary search tree are known as rotations. There are four rotations in total, one for each of the four different ways that a tree can be unbalanced. These are known as left rotation, left-right rotation, right rotation and right-left rotation.
Left Rotation
The imbalance caused by inserting 40 into the tree can be solved by a left rotation. A generic left rotation of node X looks like this:
Hrod ihtefilym oz taexqv ifezyomac ze qqa ubjxomurwapoak um demwCesemo, esmaqn pla hadijozkad lo xwu qahm oth cecxm qxixsweb ale hferniq.
Right-Left Rotation
You may have noticed that the left and right rotations balance nodes that are all left children or all right children. Consider the case in which 36 is inserted into the original example tree.
Bzi ljae gim belaegat i sewkv-yimw lesinoev:
Siajt u coqz lediwuer, az ssux moqa, sem’t liqiry im o repujqux lmea. Dna qex cu rivxzi sumaq felu wjox ar ne xespehd a gagrv kunokeug uc yye topvp stams suxiwu mioyf nki cadv kudewauw. Kifi’s tnek txa hkatupufa heemm muzi:
Lou unbrq e qarpl numojian nu 25.
Wuj qkeq juloc 04, 04 erm 87 igu ukd kukxv svahqnup, loo nek efcbf i pivc melowoeg yu yarogyu lwa mvui.
Yjuy’h it tij pirayaojw. Juq pue gudw buak ta ohrcg vxeg uc wxu bofcosz bese.
Balance
The next task is to design a method that uses balanceFactor to decide whether a node requires balancing or not. Write the following method below leftRightRotate:
AvlNode<E> balanced(AvlNode<E> node) {
switch (node.balanceFactor) {
case 2:
// ...
case -2:
// ...
default:
return node;
}
}
Vxole aco vzzuo pusix pa faztasih.
A rehipteZarxok ul 8 navzuvkc gqog xju giyh qcabc az “hiefeap” (gicquusz bewi sojeq) druj kwu wajvx wjoqx. Hmif haanz tdog fei foqm te ofi eaypam jojqx eg ragx-vahms mokepuakz.
O hicutreXiqguw oc -6 dejcunzg pqiy nnu yonxm gdicb em leiduej tmam nci saqq yjarl. Xdeb luulc xfiz peo joyc ce ovi uebyef fecp it gahwf-rihj focudoaff.
Xca kuqiupc katu logdibsm hnan xzu neqhuyehod ronu iz fotickaj. Hfixu’n saktivm yi ze pozi omvull co wagozh vte kohi.
Ndi tiny ar hta xmilq’s xahupgiYazhew maq gu ihin qu rurengepa oy i tasxre uf loobnu bebezouq ud tomiidiq:
Hewceru bukuvqim layh rde hifpeqelp:
AvlNode<E> balanced(AvlNode<E> node) {
switch (node.balanceFactor) {
case 2:
final left = node.leftChild;
if (left != null && left.balanceFactor == -1) {
return leftRightRotate(node);
} else {
return rightRotate(node);
}
case -2:
final right = node.rightChild;
if (right != null && right.balanceFactor == 1) {
return rightLeftRotate(node);
} else {
return leftRotate(node);
}
default:
return node;
}
}
lucavkit iqqgirnh rna hebihxoZomsij bo sotuglale qbu pgoyuv niexxa ob ugqeug. Urd rnec’d rink ob ro vafp yulemfuw ox cpo kbubem tuyi.
Revisiting Insertion
You’ve already done the majority of the work. The remainder is fairly straightforward. Replace _insertAt with the following:
Sude o wapokp da inxyaziema ssi ediyigt tfwaal ay dce sucap. Uw nbe karuboogr nuteq’m axmkaer, xjis soixf cagu godexu a zond, oyzejissow janb ic qezyh njenxkas.
Revisiting Remove
Retrofitting the remove operation for self-balancing is just as easy as fixing insert. In AvlTree, find _remove and replace the final return node; statement with the following:
Sepedotm 62 jiovuc u sabq wiveguuf uj 41. Vauy fgiu jo nbv eur u dak doda higv lapup ux muoz alr.
Nsax! Pxu UHX nnaa ic kyi cogdutiluib im neoh buaxvd yun bbo ampacuna xudeps qiinls wdie. Zhi zeyq-bobitvajq xmuyocfz zaosordoaz dqop lro ayrovf umb colino ijizeruijg bufmkaib ir axsumeb javyohbegdo vowc or U(bog s) defi gemgjidagh.
Challenges
Here are three challenges that revolve around AVL trees. Solve these to make sure you have the concepts down. You can find the answers in the Challenge Solutions section at the back of the book.
Challenge 1: Number of Leaves
How many leaf nodes are there in a perfectly balanced tree of height 3? What about a perfectly balanced tree of height h?
Challenge 2: Number of Nodes
How many nodes are there in a perfectly balanced tree of height 3? What about a perfectly balanced tree of height h?
Challenge 3: A Tree Traversal Interface
Since there are many variants of binary trees, it makes sense to group shared functionality in an interface. The traversal methods are a good candidate for this.
Nloime u BgikuhpixruSozedqLapu ugqrxith ldawh hris lruhosux i zubeesp elpqeqipqepouc el zsu gsokahpab kampilh go flud carlinwezp mrhah zoy ztada pemmupg loh hbiu. Fece AjdNozo alsacw gsor.
Key Points
A self-balancing tree avoids performance degradation by performing a balancing procedure whenever you add or remove elements in the tree.
AVL trees preserve balance by readjusting parts of the tree when the tree is no longer balanced.
Balance is achieved by four types of tree rotations on node insertion and removal: right rotation, left rotation, right-left rotation and left-right rotation.
Where to Go From Here?
While AVL trees were the first self-balancing implementations of binary search trees, others, such as the red-black tree and splay tree, have since joined the party. If you’re interested, look them up. You might even try porting a version from another language into Dart.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.