Uurige binaarsete otsingupuude (BST) aluseid ja Ôppige neid JavaScriptis implementeerima. Juhend katab BST struktuuri, operatsioone ja praktilisi nÀiteid.
Binaarsed otsingupuud: pÔhjalik implementeerimisjuhend JavaScriptis
Binaarsed otsingupuud (BST-d) on informaatika fundamentaalne andmestruktuur, mida kasutatakse laialdaselt andmete tĂ”husaks otsimiseks, sortimiseks ja hankimiseks. Nende hierarhiline struktuur vĂ”imaldab paljudes operatsioonides logaritmilist ajakomplekssust, mis teeb neist vĂ”imsa tööriista suurte andmehulkade haldamiseks. See juhend annab pĂ”hjaliku ĂŒlevaate BST-dest ja demonstreerib nende implementeerimist JavaScriptis, olles suunatud arendajatele ĂŒle maailma.
Binaarsete otsingupuude mÔistmine
Mis on binaarne otsingupuu?
Binaarne otsingupuu on puupÔhine andmestruktuur, kus igal tipul on maksimaalselt kaks last, mida nimetatakse vasakuks ja paremaks lapseks. BST pÔhiomadus on see, et iga antud tipu puhul:
- KÔigil vasakpoolse alampuu tippudel on vÔtmed, mis on vÀiksemad kui tipu vÔti.
- KÔigil parempoolse alampuu tippudel on vÔtmed, mis on suuremad kui tipu vÔti.
See omadus tagab, et BST elemendid on alati jÀrjestatud, vÔimaldades tÔhusat otsimist ja andmete leidmist.
PÔhimÔisted
- Tipp: Puu pĂ”hiĂŒhik, mis sisaldab vĂ”tit (andmeid) ja viiteid oma vasakule ja paremale lapsele.
- Juur: Puu kĂ”ige ĂŒlemine tipp.
- Leht: Tipp, millel ei ole lapsi.
- Alampuu: Puu osa, mille juureks on konkreetne tipp.
- KÔrgus: Pikima tee pikkus juurest leheni.
- SĂŒgavus: Tee pikkus juurest konkreetse tipuni.
Binaarse otsingupuu implementeerimine JavaScriptis
Klassi Node defineerimine
Esmalt defineerime klassi `Node`, et esindada iga tippu BST-s. Iga tipp sisaldab `key` (vÔtit) andmete hoidmiseks ning `left` ja `right` viiteid oma lastele.
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
Klassi BinarySearchTree defineerimine
JÀrgmisena defineerime klassi `BinarySearchTree`. See klass sisaldab juurtippu ning meetodeid puu lisamiseks, otsimiseks, kustutamiseks ja lÀbimiseks.
class BinarySearchTree {
constructor() {
this.root = null;
}
// Meetodid lisatakse siia
}
Lisamine
Meetod `insert` lisab uue tipu antud vÔtmega BST-sse. Lisamisprotsess sÀilitab BST omaduse, paigutades uue tipu olemasolevate tippude suhtes sobivasse kohta.
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
NÀide: VÀÀrtuste lisamine BST-sse
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
Otsimine
Meetod `search` kontrollib, kas BST-s eksisteerib antud vÔtmega tipp. See lÀbib puu, vÔrreldes vÔtit praeguse tipu vÔtmega ja liikudes vastavalt vasakule vÔi paremale alampuule.
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
NÀide: VÀÀrtuse otsimine BST-st
console.log(bst.search(9)); // VĂ€ljund: true
console.log(bst.search(2)); // VĂ€ljund: false
Kustutamine
Meetod `remove` kustutab antud vÔtmega tipu BST-st. See on kÔige keerulisem operatsioon, kuna see peab tipu eemaldamisel sÀilitama BST omaduse. Arvesse tuleb vÔtta kolme juhtu:
- 1. juhtum: Kustutatav tipp on lehttipp. Lihtsalt eemaldage see.
- 2. juhtum: Kustutataval tipul on ĂŒks laps. Asendage tipp selle lapsega.
- 3. juhtum: Kustutataval tipul on kaks last. Leidke jÀrjestikune jÀrglane (parema alampuu vÀikseim tipp), asendage tipp jÀrglasega ja seejÀrel kustutage jÀrglane.
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// vÔti on vÔrdne tipu vÔtmega
// 1. juhtum - lehttipp
if (node.left === null && node.right === null) {
node = null;
return node;
}
// 2. juhtum - tipul on ainult 1 laps
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// 3. juhtum - tipul on 2 last
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
NÀide: VÀÀrtuse eemaldamine BST-st
bst.remove(7);
console.log(bst.search(7)); // VĂ€ljund: false
Puu lÀbimine
Puu lĂ€bimine hĂ”lmab iga tipu kĂŒlastamist kindlas jĂ€rjekorras. On mitu levinud lĂ€bimisviisi:
- SisejĂ€rjestus (In-order): KĂŒlastab vasakut alampuud, seejĂ€rel tippu, seejĂ€rel paremat alampuud. Tulemuseks on tippude kĂŒlastamine kasvavas jĂ€rjekorras.
- EesjĂ€rjestus (Pre-order): KĂŒlastab tippu, seejĂ€rel vasakut alampuud, seejĂ€rel paremat alampuud.
- TagajĂ€rjestus (Post-order): KĂŒlastab vasakut alampuud, seejĂ€rel paremat alampuud, seejĂ€rel tippu.
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
NÀide: BST lÀbimine
const printNode = (value) => console.log(value);
bst.inOrderTraverse(printNode); // VĂ€ljund: 3 5 8 9 10 11 12 13 14 15 18 20 25
bst.preOrderTraverse(printNode); // VĂ€ljund: 11 5 3 9 8 10 15 13 12 14 20 18 25
bst.postOrderTraverse(printNode); // VĂ€ljund: 3 8 10 9 12 14 13 18 25 20 15 11
Minimaalsed ja maksimaalsed vÀÀrtused
Minimaalse ja maksimaalse vÀÀrtuse leidmine BST-s on selle jÀrjestatud olemuse tÔttu lihtne.
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
NÀide: Minimaalse ja maksimaalse vÀÀrtuse leidmine
console.log(bst.min().key); // VĂ€ljund: 3
console.log(bst.max().key); // VĂ€ljund: 25
Binaarsete otsingupuude praktilised rakendused
Binaarseid otsingupuid kasutatakse mitmesugustes rakendustes, sealhulgas:
- Andmebaasid: Andmete indekseerimine ja otsimine. NĂ€iteks kasutavad paljud andmebaasisĂŒsteemid BST-de variatsioone, nagu B-puud, kirjete tĂ”husaks leidmiseks. MĂ”elge rahvusvaheliste korporatsioonide kasutatavate andmebaaside globaalsele mastaabile; tĂ”hus andmete hankimine on esmatĂ€htis.
- Kompilaatorid: SĂŒmbolitabelid, mis salvestavad teavet muutujate ja funktsioonide kohta.
- OperatsioonisĂŒsteemid: Protsesside planeerimine ja mĂ€luhaldus.
- Otsingumootorid: Veebilehtede indekseerimine ja otsingutulemuste jÀrjestamine.
- FailisĂŒsteemid: Failide organiseerimine ja neile juurdepÀÀs. Kujutage ette failisĂŒsteemi globaalselt kasutataval serveril, mis hostib veebisaite; hĂ€sti organiseeritud BST-pĂ”hine struktuur aitab sisu kiiresti edastada.
JÔudlusega seotud kaalutlused
BST jÔudlus sÔltub selle struktuurist. Parimal juhul vÔimaldab tasakaalustatud BST logaritmilist ajakomplekssust lisamis-, otsimis- ja kustutamisoperatsioonide jaoks. Halvimal juhul (nt viltune puu) vÔib ajakomplekssus aga langeda lineaarsele ajale.
Tasakaalustatud vs tasakaalustamata puud
Tasakaalustatud BST on selline, kus iga tipu vasaku ja parema alampuu kĂ”rguste erinevus on maksimaalselt ĂŒks. Isetasakaalustuvad algoritmid, nagu AVL-puud ja puna-mustad puud, tagavad, et puu jÀÀb tasakaalustatuks, pakkudes stabiilset jĂ”udlust. Erinevad piirkonnad vĂ”ivad vajada erinevaid optimeerimistasemeid sĂ”ltuvalt serveri koormusest; tasakaalustamine aitab sĂ€ilitada jĂ”udlust suure globaalse kasutuse korral.
Ajakomplekssus
- Lisamine: O(log n) keskmiselt, O(n) halvimal juhul.
- Otsing: O(log n) keskmiselt, O(n) halvimal juhul.
- Kustutamine: O(log n) keskmiselt, O(n) halvimal juhul.
- LĂ€bimine: O(n), kus n on tippude arv puus.
TĂ€iustatud BST kontseptsioonid
Isetasakaalustuvad puud
Isetasakaalustuvad puud on BST-d, mis kohandavad automaatselt oma struktuuri tasakaalu sÀilitamiseks. See tagab, et puu kÔrgus jÀÀb logaritmiliseks, pakkudes stabiilset jÔudlust kÔigi operatsioonide jaoks. Levinud isetasakaalustuvad puud hÔlmavad AVL-puid ja puna-musti puid.
AVL-puud
AVL-puud sĂ€ilitavad tasakaalu, tagades, et iga tipu vasaku ja parema alampuu kĂ”rguste erinevus on maksimaalselt ĂŒks. Kui see tasakaal on hĂ€iritud, tehakse tasakaalu taastamiseks pöördeid.
Puna-mustad puud
Puna-mustad puud kasutavad tasakaalu sÀilitamiseks vÀrviomadusi (punane vÔi must). Need on keerulisemad kui AVL-puud, kuid pakuvad teatud stsenaariumides paremat jÔudlust.
JavaScripti koodinÀide: tÀielik binaarse otsingupuu implementatsioon
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(key) {
const newNode = new Node(key);
if (this.root === null) {
this.root = newNode;
} else {
this.insertNode(this.root, newNode);
}
}
insertNode(node, newNode) {
if (newNode.key < node.key) {
if (node.left === null) {
node.left = newNode;
} else {
this.insertNode(node.left, newNode);
}
} else {
if (node.right === null) {
node.right = newNode;
} else {
this.insertNode(node.right, newNode);
}
}
}
search(key) {
return this.searchNode(this.root, key);
}
searchNode(node, key) {
if (node === null) {
return false;
}
if (key < node.key) {
return this.searchNode(node.left, key);
} else if (key > node.key) {
return this.searchNode(node.right, key);
} else {
return true;
}
}
remove(key) {
this.root = this.removeNode(this.root, key);
}
removeNode(node, key) {
if (node === null) {
return null;
}
if (key < node.key) {
node.left = this.removeNode(node.left, key);
return node;
} else if (key > node.key) {
node.right = this.removeNode(node.right, key);
return node;
} else {
// vÔti on vÔrdne tipu vÔtmega
// 1. juhtum - lehttipp
if (node.left === null && node.right === null) {
node = null;
return node;
}
// 2. juhtum - tipul on ainult 1 laps
if (node.left === null) {
node = node.right;
return node;
} else if (node.right === null) {
node = node.left;
return node;
}
// 3. juhtum - tipul on 2 last
const aux = this.findMinNode(node.right);
node.key = aux.key;
node.right = this.removeNode(node.right, aux.key);
return node;
}
}
findMinNode(node) {
let current = node;
while (current != null && current.left != null) {
current = current.left;
}
return current;
}
min() {
return this.minNode(this.root);
}
minNode(node) {
let current = node;
while (current !== null && current.left !== null) {
current = current.left;
}
return current;
}
max() {
return this.maxNode(this.root);
}
maxNode(node) {
let current = node;
while (current !== null && current.right !== null) {
current = current.right;
}
return current;
}
inOrderTraverse(callback) {
this.inOrderTraverseNode(this.root, callback);
}
inOrderTraverseNode(node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.key);
this.inOrderTraverseNode(node.right, callback);
}
}
preOrderTraverse(callback) {
this.preOrderTraverseNode(this.root, callback);
}
preOrderTraverseNode(node, callback) {
if (node !== null) {
callback(node.key);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
}
postOrderTraverse(callback) {
this.postOrderTraverseNode(this.root, callback);
}
postOrderTraverseNode(node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
}
// KasutusnÀide
const bst = new BinarySearchTree();
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(3);
bst.insert(9);
bst.insert(8);
bst.insert(10);
bst.insert(13);
bst.insert(12);
bst.insert(14);
bst.insert(20);
bst.insert(18);
bst.insert(25);
const printNode = (value) => console.log(value);
console.log("SisejÀrjestuslik lÀbimine:");
bst.inOrderTraverse(printNode);
console.log("EesjÀrjestuslik lÀbimine:");
bst.preOrderTraverse(printNode);
console.log("TagajÀrjestuslik lÀbimine:");
bst.postOrderTraverse(printNode);
console.log("Minimaalne vÀÀrtus:", bst.min().key);
console.log("Maksimaalne vÀÀrtus:", bst.max().key);
console.log("Otsi 9:", bst.search(9));
console.log("Otsi 2:", bst.search(2));
bst.remove(7);
console.log("Otsi 7 pÀrast eemaldamist:", bst.search(7));
KokkuvÔte
Binaarsed otsingupuud on vĂ”imas ja mitmekĂŒlgne andmestruktuur, millel on arvukalt rakendusi. See juhend on andnud pĂ”hjaliku ĂŒlevaate BST-dest, hĂ”lmates nende struktuuri, operatsioone ja implementeerimist JavaScriptis. MĂ”istes selles juhendis kĂ€sitletud pĂ”himĂ”tteid ja tehnikaid, saavad arendajad ĂŒle maailma tĂ”husalt kasutada BST-sid tarkvaraarenduses laia probleemide ringi lahendamiseks. Alates globaalsete andmebaaside haldamisest kuni otsingualgoritmide optimeerimiseni on BST-de tundmine iga programmeerija jaoks hindamatu vÀÀrtus.
JÀtkates oma teekonda informaatikas, aitab tÀiustatud kontseptsioonide, nagu isetasakaalustuvate puude ja nende erinevate implementatsioonide uurimine, teie arusaamist ja vÔimeid veelgi parandada. JÀtkake harjutamist ja katsetamist erinevate stsenaariumidega, et omandada binaarsete otsingupuude tÔhusa kasutamise kunst.