cards-deck: 100-199_university::111-120_theoretic_cs::111_algorithms_datastructures

(a,b)- Bäume | bei uns meist -Bäume

anchored to [[111.00_anchor]] proceed from [[111.37_b_trees]]

Wir möchten uns eine weitere Form von Bäumen anschauen und diese definieren.

[!Definition] -Bäume. Was macht sie aus, welche 4 Eigenschaften müssen gelten? #card

Seien folgend >Dann nennen wir jetzt einen Baum -Baum, falls die folgenden vier Eigenschaften gelten:

  1. alle Blätter haben die gleiche Tiefe ( also wenn wir ihn visuell betrachten, sind alle Blätter auf der “gleichen Ebene”)
  2. alle inneren Knoten haben Kinder –> innere Knoten sind die, die auf ihrer Höhe noch nachbarn haben!
  3. alle Knoten (außer der Wurzel) haben Kinder
  4. Die Wurzel selbst hat Kinder

(2,4)-Bäume

Als spezielle bzw wichtige Grundstruktur möchten wir uns jetzt Bäume anschauen. Betrachten wir dabei eine Menge , welche wir jetzt in einem solchen Baum speichern möchten. ^1706629932940

[!Important] Wie gehen wir vor, um diese sortierte Menge entsprechend in den Baum einzufügen Wir speichern in den Blättern des Baumes und dabei immer von links nach rechts sortiert. Also im linkesten Blatt werden wir das kleinste Element haben und vice versa Ein innerer Knoten hat hierbei Kinder und weiterhin auch Schlüssel! Weiterhin nennen wir dann ein Element den Inhalt des rechtestens Blattes im -ten Unterbaum, den wir betrachten können. Wir müssen hier weiterhin das maximal Element von der Menge, also , in der Wurzel speichern

Wir können durch diese Konstruktion eines Baumes jetzt folgende Aussage über die Höhe treffen:

[!Important] Höhe eines -Baumes wie können wir sie einschränken / beschreiben? #card Es gilt jetzt für die Höhe eines -Baumes mit Blättern folgend: ^1706629932948

Suchen / Einfügen

Auch hier sind die Operationen Suchen/Einfügen gleich, wie bei einem B-Baum!

[!Important] Sonderfall beim Einfügen eines Wertes in Knoten was müssen wir beachten bezüglich der Menge von Schlüsseln? Wie ist die Laufzeit davon? #card Wenn wir einen Knoten haben und nach dem Einfügen jetzt Kinder haben ( also ), dann müssen wir den Knoten wieder aufspalten und somit auf dessen Parent zugreifen, das Mittelelement nehmen und dann als Schlüssel deklarieren. Die beiden neuen Knoten und werden dann links und rechts vo n platziert! ![[Pasted image 20240125222132.png]] Die Laufzeit dieser Operation beträgt dabei ^1706629932956

Entfernen

Wenn wir jetzt ein Element aus dem (2,4)-Baum entfernen möchten, müssen wir wieder bestimmte Dinge beachten, wie es auch bei B-Bäumen notwendig war.

[!Definition] Ablauf des Entfernens eines Inhaltes wie müssen wir vorgehen? welche 3 Fälle müssen wir unterscheiden? #card

Auch hier starten wir, indem wir das gesuchte Element mit Suche(a) suchen.

Haben wir dieses Element erhalten möchten wir drei Fälle unterscheiden:

  1. steht auch im parent****, also -> dann löschen**** wir und streichen es aus

  2. steht nicht im parent****, also und ist somit das**** rechteste Kind ( weil ja Schlüssel offenbar kleiner sind ( sonst wäre es nicht da drin zu finden) . Sei dann weiter das linkere Kind von mit dem Schlüssel . Wir entfernen dann den Eintrag

![[Pasted image 20240125223642.png]]

  1. Es kann sein, dass w nur noch ein Kind hat. Dann können wir zwei vorgehen betrachten: vereinigen / balancieren mit Nachbarn

  1. verschmelzen****: Dabei muss ein Geschwister von , also genau 2 Kinder haben********. Anschließend kann das einzige Element dann in den knoten übernommen werden****. Visuell folgend:

![[Pasted image 20240125223912.png]]

  2. Ein Geschwister von hat Kinder –> also wir können versuchen aufzuspalten und zu balancieren!. Dann möchten wir jetzt folgendes machen: Wir nehmen das linkeste Element von und fügen es bei ein ( also den Schlüssel und dessen Teilbaum). Visuell folgend:

![[Pasted image 20240125224047.png]]

Die Laufzeit fürs Entfernen lässt sich dann auf zusammenführen. –> Teils müssen wir viel vereinigen / mal weniger!

Amortisierte Laufzeit vom (2,4)-Baum

Wir können aus der obigen Betrachtung heraus erkennen, dass hier die Laufzeit von diversen Operationen immer zwischen schwankt, weil diese vom Zustand des Baumes abhängig sind. Wir möchten daher jetzt eine amortisierte Analyse durchführen, um die Laufzeit entsprechend evaluieren zu können..

mögliches Vorgehen könne hierbei das Betrachten eines Bankkontos sein, in welchem wir bei: • schnellen Operationen einen Euro einzahlen –> somit Zeit gutschreiben!teure Operationen ziehen wieder Geld ab –> etwa –> also sie machen den Gesamtwert wieder schlechter

Aus dieser Betrachtung heraus können wir dann evaluieren, wie schnell / gut unser System in Abhängigkeit von normalen Operationen sein wird / kann Dabei achten wir darauf, dass , dass heist wir möchten upper boundaries finden. Dabei erhoffen wir uns natürlich Aus der obigen Konstruktion können wir jetzt folgende Zeit evaluieren / erzeugen:

[!Definition] amortisierte Kosten für Operationen: einfügen/löschen wie kommen wir auf die amortisierte Laufzeit, welche ist es? #card

Wir können folgern, dass in einem (2,4)-Baum die amortisierten Kosten zum einfügen / löschen in der Zeit von verlaufen!

Beweisen können wir es folgend:

Wir wählen ein Potential ( in obigen Beispiel also einen Bankkonto-Betrag) und definieren es wie folgt:

und bei dieser Konstruktion sehen wir, dass folgende Invarianten eintreten werden!

  • Immer wenn wir die Operation zum spalten/vereinigen/reduzieren auf einen Knoten anwenden, werden dann die anderen Knoten eine Ordnung von 2,3,4 Kindern haben! -> sonst wäre die Operation nicht notwendig gewesen!

  • Vor dem Einfügen/Löschen sind immer alle Knoten in Ordnung ( also die Struktur ist valide!)

Es gilt hierbei jetzt also folgendes:

Die Unter-Operationen Spalten/Vereinigen/verringern werden mit Kosten von beschrieben und es handelt sich dabei um deren tatsächlichen Kosten!

[!Info] betrachten wir die amortisierte Analyse vom Einfügen genauer: wie konstruieren wir die amortisierte Laufzeit, womit schließen wir ab? #card

Beim einfügen brauchen wir unter die Unter-Operationen: Spalten, wofür wir betrachten können, wie es das Potential ( also den Betrag des Bankkontos ) verringern kann.

Beim Spalten betrachten wir folgende Veränderung des Potentials: und weiter beim parent steigt es um –> also ist es doch am sinken, weil die Nachteile die Vorteile überwiegen!

Es folgt weiterhin: amortisierte Laufzeit = tatsächliche Kosten + Potentialerhöhung

Also beim Einfügen:

  1. tatsächliche Kosten 1 + Potentialerhöhung

  2. und für die Spalte-Operation: tatsächliche Kosten + Potentialerhöhung

wodurch wir jetzt folgend: resultieren werden

Für die Entfernen-Operationen verfahren wir hier analog!

Anwendung der Bäume | Sortieren von fast sortierten Listen

Angenommen wir haben jetzt eine fast sortierte Liste, die wir anschließend vollends sortieren möchten. Wie können wir das unter Anwendung von (2,4)-Bäumen sinnvoll machen? #card Prinzipiell möchten wir einen leeren Baum einfach fortlaufend mit den Elementen füllen, und lassen ihn sich selbst balancieren, wodurch wir keinerlei Aufwand haben. Wir werden hierbei logischerweise und weiterhin auch durchfüren. Die Operation der Suche ist relativ langsam, aber wir können sie in ihrer Geschwindigkeit verbessern –> das passiert automatisch, wenn wir eine etwas vorsortierte Menge haben( weil man dann einfacher durch den Baum traversieren bzw cachen kann). Betrachte dafür die Eingaben-Menge welche wir chronologisch einfügen möchten Weiterhin beschreibt die Menge von Inversionen, die innerhalb dieser Folge auftreten kann –> also die Paare innerhalb der Menge, die in ihrer Sortierung verdreht sind Für diese Inversion gilt dann:

[!Definition] Laufzeit vom Sortieren mit einem Baum mit welcher Geschwindigkeit können wir das durchführen? Was sind wichtige Betrachtungen dafür? #card

Das Sortieren von einer fast sortierten Liste von Elementen ist mit (2,4)-Bäumen mit einer Laufzeit von möglich, wobei wir mit die Menge von Inversionen( also falsch herum geordneten Elementen in der Eingabe) meinen

Das bedeutet dann folgende Laufzeiten, je nach !

Beweisen kann man das folgend: Sei dann können wir daraus die gesamte Menge von konstruieren mit

 

-> Wir fügen jetzt die Elemente in umgekehrter Reihenfolge in den Baum ein und fangen so etwa mit an.

Beim einfügen eines jeden Elements dieser Menge sind dann schon enthalten. Wir starten dann ganz links und laufen bis zur Wurzel, anschließend wieder nach unten -> entsprechen der Knoten und deren Schlüssel. Wenn jetzt relativ klein ist, läuft man sich weit bis zur Wurzel!

[!Important] PseudoCode zum sortieren eines (2,4) Baumes wie gehen wir vor? #card Das heißt als PseudoCode können wir das folgend beschreiben:


>traverse-Up(xi): >v = linkestes Blatt >while ( xi > Schlüssel(parent(v))) > v = parent(v) `>````

Wir traversieren also bis wir wissen, dass unser xi kleiner ist, als die betrachteten Schlüssel!

[!Tip] amortisierte Laufzeit zum Einfügen eines Elementes #card Die amortisierte Laufzeit zum Einfügen eines Elementes kostet uns $$\mathcal{O}(1+ \log f_{i})$$ Wir wissen bereits, dass das Einfügen sonst mit $\mathcal{O}(1)$ läuft. Wir müssen uns aber immer noch im Baum zurechtfinden ( also lokalisieren). Das machen wir ja jetzt durch das hochtraversieren, wobei wir dann zu einer Höhe $h$ kommen und anschließend unser neues Elemente nach den $f_{i}$-ten Elementen einfügen. Betrachten wir es visuell: Dafür beschreibt $v$ den Umkehrpunkt und $v’$ das linkeste Kind von $v$. Wir machen folgendes: ![[Pasted image 20240126123632.png]]

Es folgen jetzt Gesamtkosten von $$\sum\limits_{i=1}^{n} \mathcal{O}(1 + \log f_{i}) = \mathcal{O}\left( n + \sum\limits_{i=1}^{n} \log f_{i}\right) = \mathcal{O}\left( n + n \log \frac{F}{n} \right)$$ Was dann unserer Gesamtzeit zum sortieren entspricht! ^1706629932963