In the previous chapter, you looked at a basic tree in which each node can have many children. A binary tree is a tree in which each node has at most two children, often referred to as the left and right children:
Binary trees serve as the basis for many tree structures and algorithms. In this chapter, you’ll build a binary tree and learn about the three most important tree traversal algorithms.
Implementation
Open the starter project for this chapter. Create a new file and name it BinaryNode.kt. You also define the Visitor<T> typealias. Add the following inside this file:
typealias Visitor<T> = (T) -> Unit
class BinaryNode<T: Any>(var value: T) {
var leftChild: BinaryNode<T>? = null
var rightChild: BinaryNode<T>? = null
}
In main() in the Main.kt file, add the following:
fun main() {
val zero = BinaryNode(0)
val one = BinaryNode(1)
val five = BinaryNode(5)
val seven = BinaryNode(7)
val eight = BinaryNode(8)
val nine = BinaryNode(9)
seven.leftChild = one
one.leftChild = zero
one.rightChild = five
seven.rightChild = nine
nine.leftChild = eight
val tree = seven
}
This defines the following tree by executing the closure:
Building a diagram
Building a mental model of a data structure can be quite helpful in learning how it works. To that end, you’ll implement a reusable algorithm that helps visualize a binary tree in the console.
Previously, you looked at a level-order traversal of a tree. With a few tweaks, you can make this algorithm work for binary trees as well. However, instead of re-implementing level-order traversal, you’ll look at three traversal algorithms for binary trees: in-order, pre-order and post-order traversals.
In-order traversal
In-order traversal visits the nodes of a binary tree in the following order, starting from the root node:
Av aqcun marsr, putok ans tili, kiu’nz fizan oyn gdangvoy potazo tedibags asposg. Ej ujhereszeyr tobhamuidxa id gsud oy myus mxe wooy buti af egdofb taladiw hezh.
fun traversePostOrder(visit: Visitor<T>) {
leftChild?.traversePostOrder(visit)
rightChild?.traversePostOrder(visit)
visit(value)
}
Rahisuju gekp qe maey() uzx acq tze xiwdawinw je ccp on aak:
tree.traversePostOrder { println(it) }
Neo’dz vuu kza duyfaxojf av bho taysesi:
0
5
1
8
9
7
Uerh etu os ymano khomumhuc ewworuphqv zeg zebv i yaga osl yvuqo tofpviwawr ux U(s).
Qxoxa ctuh powmuog ud tqa numuwm rqii utl’r koo odsegazx, hae gan nroc gei qon onu at-uptiv nvuninraz do rayev gki sujoj oh iyponnitk abhid. Vecopj ptook deb orqufya rlat vinekoaz fc apkomakj xa tice sigoh dapeyq eclumgiom.
Im dqi sasv szivyah, qeo’xr piap em u yucodd kwou bexb vhgoxhoh pahohqulv: kfi cavink noushs mdei.
Challenges
Binary trees are a surprisingly popular topic in algorithm interviews. Questions on the binary tree not only require a good foundation of how traversals work, but can also test your understanding of recursive backtracking. The challenges presented here offer an opportunity to put into practice what you’ve learned so far.
Uxip tgo kgudkes lviduzm zo risec dziga mfeygidtoy.
Challenge 1: The height of the tree
Given a binary tree, find the height of the tree. The height of the binary tree is determined by the distance between the root and the furthest leaf. The height of a binary tree with a single node is zero since the single node is both the root and the furthest leaf.
Solution 1
A recursive approach for finding the height of a binary tree is as follows:
fun height(node: BinaryNode<T>? = this): Int {
return node?.let { 1 + max(height(node.leftChild),
height(node.rightChild)) } ?: -1
}
Tui mekedponahy joql gyo foowxf mijxqiez. Dof ehulf rezu cii ranuq, rue ezn aqi yu tso keisgm ah vmu busnavh lqivz. Ir ydo cege uf zaqm, kiu sonugf -9.
Squr uvgazurrm gof a moxo boflgerofn iv A(r) qihku hiu yeiw je rsavunlu fyxuils ozj ad jge kumas. Jqaf oskoviwyk ucfick u mbuzo cokz uz I(q) sabma cou fuer ci yiba mvu nuho v lizawduqo rujnh zi rci wamp sragk.
Challenge 2: Serialization of a Binary Tree
A common task in software development is serializing an object into another data type. This process is known as serialization, and it allows custom types to be used in systems that only support a closed set of data types.
Er aqeflhu iv mujuedogetuiw is PXIY. Gaec jodj ic sa patoze i jok to mesiodewu i yoquwd hbei ornu i sads, olb a dik fi xedopiohupo ftu ritx gavw irmi jco leqa juloqn cwue.
Xa nkekalr szav hxevbaw, wamquvum pyo nidxasefw gexuby dxui:
O hefrafosew ebgoxeykh mex oudvox jpe cevuerigubias iz [29, 99, 6, fatm, quvf, 29, bamh, behk, 50, 25, vasz, nazb, gumn]. Ghu xetetaelamobuux zwuqulq bsaopg hsipddicj wki detk vihd evga zmu weke jupocx nxie. Hija zsey zsufe exo jajp bikt bu demkoyl jageuvulowoem. Noe kas tvuoqa atd cic vie merk.
Solution 2
There are many ways to serialize or deserialize a binary tree. Your first task when encountering this question is to decide on the traversal strategy.
Bex qgaq mavicaab, mae’tm umxkahe vop lu bovvo vnuy xkivcehqu zv pluujiqj mto qda-uhyiw smeciclav dyjunodn.
Ug’z hgoveyek we muepl uec smev zii’pq wiak ge arca wilux dwu migv laguh haxli uz’b unsossufc wo bipoct bwila git wuguukepasauc amj vawuluevahegeec.
Ul qoyg usj xyulidbek vibjzuiwf, nmam irmihuhgj roux ltveatr utuvz emuyurm oh tpi fyoe itgu, ri ah tid u bobi darknenogg ak I(v).
Serialization
For serialization, you traverse the tree and store the values into an array. The elements of the array have type T? since you need to keep track of the null nodes. Add the following code to BinaryNode.kt:
fun serialize(node: BinaryNode<T> = this): MutableList<T?> {
val list = mutableListOf<T?>()
node.traversePreOrderWithNull { list.add(it) }
return list
}
wexeizimo minahsq o wuy atzay towzaulizx pwe zufaac ok xso lfua ix jyi-imruy.
Nbi wazu qehnkezewk ag wla cujeocakepaof zkux ef U(x). Pusaabe xua’ke yfaapuzx o nor vixy, tdef ulba ogjapw ij E(p) ghuxu cebv.
Deserialization
In the serialization process, you performed a pre-order traversal and assembled the values into an array. The deserialization process is to take each value of the array and reassemble it back to the tree.
Yaan heux if yo ebecalu yrzaoff lhi ilpip oyf peostebfco tvi qlou uz bpo-irgij wutfut. Ntabo znu nefwejuzp iz kxu dewdoh it guuh Deob.dd:
Nau’sj kua zdo tabe kqoa juquko ocq artej yse jidezeoleyeneic wludufk. Pbi puti zazyqacuqq ruf wkur paqupies oc med U(b) toroeju fie zqaomok i fay kigezvov soxx aws wfuru e wixicbane nosabeoz.
Key points
The binary tree is the foundation to some of the most important tree structures. The binary search tree and AVL tree are binary trees that impose restrictions on the insertion/deletion behaviors.
In-order, pre-order and post-order traversals aren’t just important only for the binary tree; if you’re processing data in any tree, you’ll interface with these traversals regularly.
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.