Hvorfor er std :: kartet implementert som en rød-svart tre ?
Det er flere balanserte binære søketrær (BSTs) der ute. Hva var konstruksjons trade-offs i å velge en rød-svart tre?
Hvorfor er std :: kartet implementert som en rød-svart tre ?
Det er flere balanserte binære søketrær (BSTs) der ute. Hva var konstruksjons trade-offs i å velge en rød-svart tre?
Sannsynligvis de to vanligste selvbalanserende tre algoritmer er rød-svart trær og AVL trær . For å balansere treet etter en innsetting / oppdatering begge algoritmer bruker begrepet rotasjoner hvor nodene i treet er rotert for å utføre den gjenbalansering.
Mens i begge algoritmer innsats / sletteoperasjoner er O (log n), i tilfelle av rød-svart tre re-balanserings rotasjon er en O (1) drift, samtidig med AVL dette er en O (log n) drift, noe som gjør Red-Black treet mer effektiv i dette aspektet av re-balansere scenen og en av de mulige årsakene til at det er mer vanlig.
Rød-Svart trær brukes i de fleste samling biblioteker, inkludert tilbud fra Java og Microsoft .NET Framework.
Det er bare valget av implementeringen - de kunne gjennomføres som en hvilken som helst balansert tre. De ulike valgene er sammenlignbare med mindre forskjeller. Derfor noen er så god som noen.
AVL trær har en maksimal høyde på 1.44logn, mens RB trær har et maksimum på 2logn. Innsetting av et element i et AVL kan innebære en gjenbalansering på ett punkt i treet. Den rebalansering ferdig innsetting. Etter innsetting av et nytt blad, oppdatere forfedre som blad må gjøres opp til roten, eller opp til et punkt hvor de to undertrær er av lik dybde. Sannsynligheten for å oppdatere k noder er en tredjedel ^ k. Rebalansering er O (1). Fjerning av et element som kan innebære mer enn ett rebalansering (opp til halvparten av dybden av treet).
RB-trær er B-trær fra orden 4 representert som binære søke trær. En 4-node i B-treet resulterer i to nivåer i den tilsvarende BST. I verste fall alle nodene i treet er 2-knutepunkter og det finnes bare én kjede av 3-noder ned til et blad. At bladet vil være i en avstand på 2logn fra roten.
Går ned fra roten til innsettingspunktet, må man endre 4-nodene i 2-noder, for å sørge for at enhver innføring ikke vil mette et blad. Kommer tilbake fra innsettings, alle disse nodene må analyseres for å sikre at de på riktig måte representerer 4-forgreningspunkter. Dette kan også gjøres å gå ned i treet. Den globale kostnadene vil være den samme. Det er ingen gratis lunsj! Fjerning av et element fra treet er av samme størrelsesorden.
Alle disse trærne krever at noder bære informasjon om høyde, vekt, farge, etc. Bare spriker trær er fri fra slike ytterligere info. Men folk flest er redd for spriker trær, på grunn av ramdomness av deres struktur!
Endelig kan trærne også bære vekten informasjon i nodene, tillater vekt balansering. Forskjellige skjemaer kan anvendes. Man bør balansere når et subtre inneholder mer enn 3 ganger antallet av elementer i det andre undertreet. Rebalansering skal igjen gjøres enten throuh en enkelt eller dobbelt rotasjon. Dette betyr et worst case of 2.4logn. Man kan komme unna med 2 ganger i stedet for tre, et mye bedre forhold, men det kan bety å forlate en litt mindre Thant 1% av subtreene ubalansert her og der. Tricky!
Hvilken type tre er best? AVL sikkert. De er de enkleste å kode, og har sin verste høyde nærmest LOGN. For et tre på 1.000.000 elementene, vil en AVL være ved de fleste høyde 29, en RB 40, og en vekt balansert 36 eller 50, avhengig av forholdet.
Det er mange andre variabler: tilfeldig, forholdet legger, sletter, søk, etc.
Det er egentlig avhengig av bruken. AVL treet har vanligvis flere rotasjoner av rebalansering. Så hvis søknaden din ikke har altfor mange innsetting og sletting operasjoner, men vekter seg tungt på å søke, da er AVL treet sannsynligvis et godt valg.
std::map bruker Red-Black treet som det blir en fornuftig avveining mellom hastigheten til noden innsetting / sletting og søking.
Oppdater 2017-06-14: webbertiger redigere svaret etter at jeg kommenterte. Jeg bør påpeke at svaret er nå mye bedre for mine øyne. Men jeg holdt mitt svar like ytterligere informasjon ...
På grunn av det faktum at jeg tror første svaret er galt (rettelse: ikke både lenger) og den tredje har en feil aksept. Jeg føler at jeg måtte avklare ting ...
Den to mest populære treet er AVL og Red svart (RB). Den største forskjellen ligger i utnyttelsen:
Den største forskjellen kommer fra fargelegging. Du har mindre re-balanse handling i RB treet enn AVL fordi farge gjør at du kan noen ganger hoppe eller forkorte re-balanse handlinger som har en relativ hi kostnad. På grunn av fargestoffer, RB tre har også høyere nivå av noder fordi det kunne akseptere røde noder mellom sorte (som har de mulighetene for ~ 2x flere nivåer) gjør søk (lese) litt mindre effektiv ... men fordi det er en konstant (2x), den forbli i O (log n).
Hvis du vurdere ytelse hit for en modifikasjon av et tre (signifikant) VS ytelsen rammet av konsultasjon av et tre (nesten ubetydelig), blir det naturlig å foretrekke RB løpet AVL for en generell sak.
De tidligere svarene bare ta tre alternativer og rød svart trolig bare gjenstår for historiske årsaker.
Hvorfor ikke en hash table?
I et tre, en type som krever bare delvis bestilling (<sammenligning) til å bli brukt som en nøkkel på kartet. Imidlertid nøkkeltabeller krever at hver tast type har en hash-funksjon definert. Å holde disse type krav til et minimum er svært viktig for generisk programmering.
Utforme en god hash table krever intim kjennskap til sammenhengen det som den vil bli brukt. Skulle det bruk av åpen adressering, eller koblet kjeding? Hvilke nivåer av last skal det aksepterer før resizing? Bør den bruke en dyr hasj som unngår kollisjoner, eller en som er grov og rask?
(C ++ 11 gjorde legge hash tabeller med unordered_map. Du kan se fra dokumentasjonen det krever å sette politikk for å konfigurere mange av disse alternativene.)
Siden STL ikke kan forutse noe som er det beste valget for din søknad, må standard til å være mer fleksibel. Trees "bare jobbe" og skalere pent.
Hva med andre trær?
Red Svart treets tilbud raskt oppslag og er selvbalanserende, i motsetning BSTs. En annen bruker påpekt sine fordeler over selvbalanserende AVL treet.
Alexander Stepanov (skaperen av STL) sa at han ville bruke en B * Tre i stedet for en rød-svart tre hvis han skrev std::mapigjen som er mer vennlig for moderne minnebuffere.
En av de største endringene siden da har vært vekst av cacher. Cache målet er svært kostbare, så lokaliteten av referansen er mye viktigere nå. Node-baserte datastrukturer, som har lav lokalitet referanseramme, gjør mye mindre mening. Hvis jeg skulle designe STL i dag, ville jeg ha et annet sett med beholdere. For eksempel er en in-memory B * -tre et langt bedre valg enn en rød-svart tre for å gjennomføre en assosiativ container. - Alexander Stepanov
Du kan lese mer her
Er rød svart tre eller B * alltid det beste?
Ved andre anledninger Alex har uttalt at std::vectorer nesten alltid den beste listen beholder for lignende grunner. Det gjør sjelden fornuftig å bruke std::listeller std::dequeselv for de situasjoner vi ble undervist i skolen (for eksempel å fjerne et element fra midten av listen). std::vectorer så rask at det slår de strukturer for alt, men store n.
Bruk av det samme resonnement hvis du har bare et lite antall elementer (hundrevis?) Med en std::vectorog lineær søker kan være mer effektiv enn treet gjennomføringen av std::map. Avhengig av hvor ofte innsetting, en sorterte std::vectorkombinert med std::binary_searchkan være den raskeste valget.