Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* c402.c: ********************************************************************}
- {* Téma: Nerekurzivní implementace operací nad BVS
- ** Implementace: Petr Přikryl, prosinec 1994
- ** Úpravy: Petr Přikryl, listopad 1997
- ** Petr Přikryl, květen 1998
- ** Převod do jazyka C: Martin Tuček, srpen 2005
- ** Úpravy: Bohuslav Křena, listopad 2009
- **
- ** S využitím dynamického přidělování paměti, implementujte NEREKURZIVNĚ
- ** následující operace nad binárním vyhledávacím stromem (předpona BT znamená
- ** Binary Tree a je u identifikátorů uvedena kvůli možné kolizi s ostatními
- ** příklady):
- **
- ** BTInit .......... inicializace stromu
- ** BTInsert ........ nerekurzivní vložení nového uzlu do stromu
- ** BTPreorder ...... nerekurzivní průchod typu pre-order
- ** BTInorder ....... nerekurzivní průchod typu in-order
- ** BTPostorder ..... nerekurzivní průchod typu post-order
- ** BTDisposeTree ... zruš všechny uzly stromu
- **
- ** U všech funkcí, které využívají některý z průchodů stromem, implementujte
- ** pomocnou funkci pro nalezení nejlevějšího uzlu v podstromu.
- **
- ** Přesné definice typů naleznete v souboru c402.h. Uzel stromu je typu tBTNode,
- ** ukazatel na něj je typu tBTNodePtr. Jeden uzel obsahuje položku int Cont,
- ** která současně slouží jako užitečný obsah i jako vyhledávací klíč
- ** a ukazatele na levý a pravý podstrom (LPtr a RPtr).
- **
- ** Příklad slouží zejména k procvičení nerekurzivních zápisů algoritmů
- ** nad stromy. Než začnete tento příklad řešit, prostudujte si důkladně
- ** principy převodu rekurzivních algoritmů na nerekurzivní. Programování
- ** je především inženýrská disciplína, kde opětné objevování Ameriky nemá
- ** místo. Pokud se Vám zdá, že by něco šlo zapsat optimálněji, promyslete
- ** si všechny detaily Vašeho řešení. Povšimněte si typického umístění akcí
- ** pro různé typy průchodů. Zamyslete se nad modifikací řešených algoritmů
- ** například pro výpočet počtu uzlů stromu, počtu listů stromu, výšky stromu
- ** nebo pro vytvoření zrcadlového obrazu stromu (pouze popřehazování ukazatelů
- ** bez vytváření nových uzlů a rušení starých).
- **
- ** Při průchodech stromem použijte ke zpracování uzlu funkci BTWorkOut().
- ** Pro zjednodušení práce máte předem připraveny zásobníky pro hodnoty typu
- ** bool a tBTNodePtr. Pomocnou funkci BTWorkOut ani funkce pro práci
- ** s pomocnými zásobníky neupravujte
- **
- ** Pozor! Je třeba správně rozlišovat, kdy použít dereferenční operátor *
- ** (typicky při modifikaci) a kdy budeme pracovat pouze se samotným ukazatelem
- ** (např. při vyhledávání). V tomto příkladu vám napoví prototypy funkcí.
- ** Pokud pracujeme s ukazatelem na ukazatel, použijeme dereferenci.
- **/
- #include "c402.h"
- int solved;
- void BTWorkOut (tBTNodePtr Ptr) {
- /* ---------
- ** Pomocná funkce, kterou budete volat při průchodech stromem pro zpracování
- ** uzlu určeného ukazatelem Ptr. Tuto funkci neupravujte.
- **/
- if (Ptr==NULL)
- printf("Chyba: Funkce BTWorkOut byla volána s NULL argumentem!\n");
- else
- printf("Výpis hodnoty daného uzlu> %d\n",Ptr->Cont);
- }
- /* -------------------------------------------------------------------------- */
- /*
- ** Funkce pro zásobník hotnot typu tBTNodePtr. Tyto funkce neupravujte.
- **/
- void SInitP (tStackP *S)
- /* ------
- ** Inicializace zásobníku.
- **/
- {
- S->top = 0;
- }
- void SPushP (tStackP *S, tBTNodePtr ptr)
- /* ------
- ** Vloží hodnotu na vrchol zásobníku.
- **/
- {
- /* Při implementaci v poli může dojít k přetečení zásobníku. */
- if (S->top==MAXSTACK)
- printf("Chyba: Došlo k přetečení zásobníku s ukazateli!\n");
- else {
- S->top++;
- S->a[S->top]=ptr;
- }
- }
- tBTNodePtr STopPopP (tStackP *S)
- /* --------
- ** Odstraní prvek z vrcholu zásobníku a současně vrátí jeho hodnotu.
- **/
- {
- /* Operace nad prázdným zásobníkem způsobí chybu. */
- if (S->top==0) {
- printf("Chyba: Došlo k podtečení zásobníku s ukazateli!\n");
- return(NULL);
- }
- else {
- return (S->a[S->top--]);
- }
- }
- bool SEmptyP (tStackP *S)
- /* -------
- ** Je-li zásobník prázdný, vrátí hodnotu true.
- **/
- {
- return(S->top==0);
- }
- /* -------------------------------------------------------------------------- */
- /*
- ** Funkce pro zásobník hotnot typu bool. Tyto funkce neupravujte.
- */
- void SInitB (tStackB *S) {
- /* ------
- ** Inicializace zásobníku.
- **/
- S->top = 0;
- }
- void SPushB (tStackB *S,bool val) {
- /* ------
- ** Vloží hodnotu na vrchol zásobníku.
- **/
- /* Při implementaci v poli může dojít k přetečení zásobníku. */
- if (S->top==MAXSTACK)
- printf("Chyba: Došlo k přetečení zásobníku pro boolean!\n");
- else {
- S->top++;
- S->a[S->top]=val;
- }
- }
- bool STopPopB (tStackB *S) {
- /* --------
- ** Odstraní prvek z vrcholu zásobníku a současně vrátí jeho hodnotu.
- **/
- /* Operace nad prázdným zásobníkem způsobí chybu. */
- if (S->top==0) {
- printf("Chyba: Došlo k podtečení zásobníku pro boolean!\n");
- return(NULL);
- }
- else {
- return(S->a[S->top--]);
- }
- }
- bool SEmptyB (tStackB *S) {
- /* -------
- ** Je-li zásobník prázdný, vrátí hodnotu true.
- **/
- return(S->top==0);
- }
- /* -------------------------------------------------------------------------- */
- /*
- ** Následuje jádro domácí úlohy - funkce, které máte implementovat.
- */
- void BTInit (tBTNodePtr *RootPtr) {
- /* ------
- ** Provede inicializaci binárního vyhledávacího stromu.
- **
- ** Inicializaci smí programátor volat pouze před prvním použitím binárního
- ** stromu, protože neuvolňuje uzly neprázdného stromu (a ani to dělat nemůže,
- ** protože před inicializací jsou hodnoty nedefinované, tedy libovolné).
- ** Ke zrušení binárního stromu slouží procedura BTDisposeTree.
- **
- ** Všimněte si, že zde se poprvé v hlavičce objevuje typ ukazatel na ukazatel,
- ** proto je třeba při práci s RootPtr použít dereferenční operátor *.
- **/
- //nastavim prvni prvek na NULL
- (*RootPtr)=NULL;
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- void BTInsert (tBTNodePtr *RootPtr, int Content) {
- /* --------
- ** Vloží do stromu nový uzel s hodnotou Content.
- **
- ** Z pohledu vkládání chápejte vytvářený strom jako binární vyhledávací strom,
- ** kde uzly s hodnotou menší než má otec leží v levém podstromu a uzly větší
- ** leží vpravo. Pokud vkládaný uzel již existuje, neprovádí se nic (daná hodnota
- ** se ve stromu může vyskytnout nejvýše jednou). Pokud se vytváří nový uzel,
- ** vzniká vždy jako list stromu. Funkci implementujte nerekurzivně.
- **/
- //vytvorim si novy list
- tBTNodePtr item=NULL,item_2=NULL;
- item_2=malloc(sizeof(tBTNodePtr)*sizeof(tBTNodePtr)*sizeof(tBTNodePtr)*sizeof(int));
- item_2->Cont=Content;
- item_2->LPtr=NULL;
- item_2->RPtr=NULL;
- //pokud jsme na zacatku okamzite ho ulozim
- if ((*RootPtr)==NULL)
- (*RootPtr)=item_2;
- //jinak projdu cely strom a ulozim ho na konec
- else
- {
- //pomocna pro pruchod stromu
- item=(*RootPtr);
- //dokud jsme nenrazili na konec stromu
- while (item!=NULL)
- {
- //pokud se tam jiz tato hodnota nachazi, uvolnim novy list a koncim funkci
- if (item->Cont == Content)
- {
- free(item_2);
- return ;
- }
- //pokud je hodnota vetsi nez hledany prvek jdu do leva
- else if (item->Cont > Content)
- {
- //pokud se v levo nic nenachazi, ulozim tam nove vytvoreny list a koncim funkci
- if (item->LPtr==NULL)
- {
- item->LPtr=item_2;
- return;
- }
- //pokud, ale v levo neco je, nactu levy uzel a pokracuji v cyklu
- item=item->LPtr;
- }
- //pokud je hodnota mensi nez hledany prvek jdu do prava
- else if (item->Cont < Content)
- {
- //pokud se v pravo nic nenachazi, ulozim tam nove vytvoreny list a koncim funkci
- if (item->RPtr==NULL)
- {
- item->RPtr=item_2;
- return;
- }
- //pokud, ale v pravo neco je, nactu pravy uzel a pokracuji v cyklu
- item=item->RPtr;
- }
- }
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- /* PREORDER */
- void Leftmost_Preorder (tBTNodePtr ptr, tStackP *Stack) {
- /* -----------------
- ** Jde po levě větvi podstromu, dokud nenarazí na jeho nejlevější uzel.
- **
- ** Při průchodu Preorder navštívené uzly zpracujeme voláním funkce BTWorkOut()
- ** a ukazatele na ně is uložíme do zásobníku.
- **/
- //s pouzitim zasobniku prochazim strom
- tBTNodePtr item;
- item=ptr;
- //dokud nejsme na konci
- while (item!=NULL)
- {
- //tisknu uzel, na kterem se nachazim
- BTWorkOut(item);
- //ulozim uzel pro pozdejsi pruchod do prava na zasobnik
- SPushP(Stack,item);
- //a jdu do leva
- item=item->LPtr;
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- void BTPreorder (tBTNodePtr RootPtr) {
- /* ----------
- ** Průchod stromem typu preorder implementovaný nerekurzivně s využitím funkce
- ** Leftmost_Preorder a zásobníku ukazatelů. Zpracování jednoho uzlu stromu
- ** realizujte jako volání funkce BTWorkOut().
- **/
- //pokud ve strome vubec neco je
- if (RootPtr!=NULL)
- {
- //inicializace zasobniku, pomocneho stromu a prvni pruchod do leva
- //mam radsi dynamicke zasobniky
- tStackP *Stack=NULL;
- Stack=malloc (sizeof (tStackP));
- tBTNodePtr item=RootPtr;
- SInitP(Stack);
- Leftmost_Preorder(item,Stack);
- //dokud enni zasobnik prazdny
- while (!SEmptyP(Stack))
- {
- //do promenne ulozim ukazatel na uzel
- item=STopPopP(Stack);
- //a projfu uzel tentokrat do prava
- Leftmost_Preorder(item->RPtr,Stack);
- }
- free (Stack);
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- /* INORDER */
- void Leftmost_Inorder(tBTNodePtr ptr, tStackP *Stack) {
- /* ----------------
- ** Jde po levě větvi podstromu, dokud nenarazí na jeho nejlevější uzel.
- **
- ** Při průchodu Inorder ukládáme ukazatele na všechny navštívené uzly do
- ** zásobníku.
- **/
- //s pouzitim zasobniku prochazim strom
- tBTNodePtr item;
- item=ptr;
- //dokud nejsme na konci
- while (item!=NULL)
- {
- //dam stavajici uzel na zasobnik pro pozdejsi pruchod do prava
- SPushP(Stack,item);
- //a jdu do leva
- item=item->LPtr;
- }
- // solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- void BTInorder (tBTNodePtr RootPtr) {
- /* ---------
- ** Průchod stromem typu inorder implementovaný nerekurzivně s využitím funkce
- ** Leftmost_Inorder a zásobníku ukazatelů. Zpracování jednoho uzlu stromu
- ** realizujte jako volání funkce BTWorkOut().
- **/
- //pokud vubec neco je ve strome
- if (RootPtr!=NULL)
- {
- //pocatecni inicializace a vytvoreni zasobniku
- //mam radsi dynamicke zasobniky
- tStackP *Stack=NULL;
- Stack=malloc (sizeof (tStackP));
- tBTNodePtr item=RootPtr;
- SInitP(Stack);
- //prvni pruchod do leva
- Leftmost_Inorder(item,Stack);
- while (!SEmptyP(Stack))
- {
- //vytahnu si ze zasobniku prvni uzel
- item=STopPopP(Stack);
- //vytisknu ho
- BTWorkOut(item);
- //a prochazim jeho pravy list do leva
- Leftmost_Inorder(item->RPtr,Stack);
- }
- free (Stack);
- }
- // solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- /* POSTORDER */
- void Leftmost_Postorder (tBTNodePtr ptr, tStackP *StackP, tStackB *StackB) {
- /* --------
- ** Jde po levě větvi podstromu, dokud nenarazí na jeho nejlevější uzel.
- **
- ** Při průchodu Postorder ukládáme ukazatele na navštívené uzly do zásobníku
- ** a současně do zásobníku bool hodnot ukládáme informaci, zda byl uzel
- ** navštíven poprvé a že se tedy ještě nemá zpracovávat.
- **/
- //s pouzitim dvou zasobniku prochazim strom
- tBTNodePtr item;
- item=ptr;
- //dkud nejsme na konci stromu
- while (item!=NULL)
- {
- //na zasobnik ulozim stavajici uzel
- SPushP(StackP,item);
- //a ulozim ze jsem sel do leva
- SPushB(StackB,true);
- item=item->LPtr;
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- void BTPostorder (tBTNodePtr RootPtr) {
- /* -----------
- ** Průchod stromem typu postorder implementovaný nerekurzivně s využitím funkce
- ** Leftmost_Postorder, zásobníku ukazatelů a zásobníku hotdnot typu bool.
- ** Zpracování jednoho uzlu stromu realizujte jako volání funkce BTWorkOut().
- **/
- if (RootPtr!=NULL)
- {
- //ininicializace promennych a zasobniku
- bool left;
- //mam radsi dynamicke zasobniky
- tStackP *StackP=NULL;
- StackP=malloc (sizeof (tStackP));
- tStackB *StackB=NULL;
- StackB=malloc (sizeof (tStackB));
- tBTNodePtr item;
- item = RootPtr;
- SInitP(StackP);
- SInitB(StackB);
- //prvni pruchod do leva
- Leftmost_Postorder(item,StackP,StackB);
- while (!SEmptyP(StackP))
- {
- //vytahnu si prvni uzel ze saboniku
- item=STopPopP(StackP);
- //ale on tam musi zustat
- SPushP(StackP,item);
- //zjistim, jestli jsem posledne sel do leva
- left=STopPopB(StackB);
- //pokud ano
- if (left)
- {
- //pujdu do prava
- SPushB(StackB,false);
- Leftmost_Postorder(item->RPtr,StackP,StackB);
- }
- //pokud jsem sel do prava
- else
- {
- //vytahnu uzel
- item=STopPopP(StackP);
- //a vytisknu ho
- BTWorkOut(item);
- }
- }
- free(StackP);
- free(StackB);
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- void BTDisposeTree (tBTNodePtr *RootPtr) {
- /* -------------
- ** Zruší všechny uzly stromu a korektně uvolní jimi zabranou paměť.
- **
- ** Funkci implementujte nerekurzivně s využitím zásobníku ukazatelů.
- **/
- //pokud ve stromu vubec neco je, tak ho smazu
- if ((*RootPtr)!=NULL)
- {
- //mama radsi dynamicke zasobniky
- tStackP *StackP=NULL;
- StackP=malloc (sizeof (tStackP));
- tBTNodePtr item_2;
- SInitP(StackP);
- //cyklus repeat
- do
- {
- //pokud jsme na konci vetve
- if ((*RootPtr)==NULL)
- {
- //a neni prazdny zasobnik, tak si vytahneme uzel a pokracujeme v cyklu
- if (!SEmptyP(StackP))
- (*RootPtr)=STopPopP(StackP);
- }
- //pokud jsme nekde uprostred vetve
- else
- {
- //pokud ve stavajicim uzlu v pravo neco je, ulozim uzel na zasobnik
- if ((*RootPtr)->RPtr!=NULL)
- SPushP(StackP,(*RootPtr)->RPtr);
- //posunu se do leva a prvek, na kterem jsem doposud byl odsranim
- item_2=(*RootPtr);
- (*RootPtr)=(*RootPtr)->LPtr;
- free(item_2);
- item_2=NULL;
- }
- }
- while (!((*RootPtr)==NULL && SEmptyP(StackP)));
- //cyklus do while, pracuje podobne jako repeat v pascalu, s tim rozdilem ze
- //ukoncovaci podminku musime znegovat
- free(StackP);
- }
- //solved = FALSE; /* V případě řešení smažte tento řádek! */
- }
- /* konec c402.c */
Advertisement
Add Comment
Please, Sign In to add comment