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.