Implementace aritmetického stromu pomocí směrníků Úvod Aritmetický strom je binární strom, který má ve vnitřních uzlech matematické operátory (+, -, /, *) a v listech (vrcholech) má operandy (např. čísla alebo identifikátory proměnných). Každý jeho uzel má buď dva nebo žádný podřízený uzel (nemůže mít jeden podřízený uzel). Každému aritmetickému výrazu (zcela uzávorkovanému) odpovídá právě jeden aritmetický strom a naopak. Např. strom na obrázku
má následující infixový zápis: (( 7 * 3 ) - ( 15 / 3 )) Aplikace Zpracovaná aplikace ukazuje tři příklady vytvoření aritmetického stromu pomocí směrníků, jeho vykreslení, výpočet hodnoty aritmetického výrazu, zjištění hloubky stromu a funkce pro infixový, prefixový a postfixový zápis. Všechny metody (kromě VYTVORIT a PRIDATUZEL) jsou zároveň ukázkou průchodu stromem. Deklarace type PUzel = ^TUzel; TUzel = record operator:char; // ' ',+,-,*,/ hodnota:integer; levy, pravy:puzel; Metody Procedura VYCISTIT(U) Uvolní paměť obsazenou uzlem U stromu Při volání kořenovým uzlem vyčistí celý strom. procedure Vycistit(Uzel:PUzel); while Uzel^.pravy<>nil do Vycistit(Uzel.pravy); Uzel^.pravy:=nil; while Uzel^.levy<>nil do 2
Vycistit(Uzel.levy); Uzel^.levy:=nil; Dispose(Uzel); Funkce PRIDATUZEL(U,o,h) Vytvoří nový uzel stromu pod uzlem U Funkce vytvoří nový uzel vlevo nebo vpravo podle toho, který směr je ještě volný a nastaví atributy uzlu. function PridatUzel(Otec:PUzel; Operator:Char; Hodnota:Integer):PUzel; New(Result); Result^.operator:=Operator; Result^.hodnota:=Hodnota; Result^.levy:=nil; Result^.pravy:=nil; if Otec<>nil then if Otec^.levy=nil then Otec^.levy:=Result Otec^.pravy:=Result; end Funkce VYPOCITAT(U) Vrátí výsledek výrazu definovaného aritmetickým stromem (počínaje uzlem U) function Vypocitat(Uzel:PUzel):Integer; case Uzel.operator of '+':Result:=Vypocitat(Uzel^.levy)+Vypocitat(Uzel^.pravy); '-':Result:=Vypocitat(Uzel^.levy)-Vypocitat(Uzel^.pravy); '*':Result:=Vypocitat(Uzel^.levy)*Vypocitat(Uzel^.pravy); '/':Result:=Vypocitat(Uzel^.levy) div Vypocitat(Uzel^.pravy); Result:=Uzel^.hodnota; Funkce HLOUBKA(U,h) Vrátí hloubku aritmetického stromu (počínaje uzlem U) v proměnné h. procedure Hloubka(U:PUzel; H:Integer); 3
l,p:integer; l:=1; p:=1; if U^.levy<>nil then Hloubka(U^.levy,l); if U^.pravy<>nil then Hloubka(U^.pravy,p); if l>p then H:=H+l H:=H+p; Funkce PREFIX(U) Vrátí prefixový zápis aritmetického stromu (počínaje uzlem U) function Prefix(Uzel:PUzel):string; if Uzel^.operator=' ' then Result:=IntToStr(Uzel^.Hodnota)+' ' Result:=Uzel^.operator+' '; if Uzel^.levy<>nil then Result:=Result+Prefix(Uzel^.levy); if Uzel^.pravy<>nil then Result:=Result+Prefix(Uzel^.pravy); Funkce POSTFIX(U) Vrátí postfixový zápis aritmetického stromu (počínaje uzlem U) function Postfix(Uzel:PUzel):string; if Uzel^.operator=' ' then Result:=' '+IntToStr(Uzel^.Hodnota) Result:=' '+Uzel^.operator+' '; if Uzel^.pravy<>nil then Result:=Postfix(Uzel^.pravy)+Result; if Uzel^.levy<>nil then Result:=Postfix(Uzel^.levy)+Result; 4
Funkce INFIX(U) Vrátí infixový zápis aritmetického stromu (počínaje uzlem U) function Infix(Uzel:PUzel):string; l,p:string; if Uzel^.operator=' ' then Result:=' '+IntToStr(Uzel^.Hodnota) if Uzel^.levy<>nil then l:=infix(uzel^.levy); if Uzel^.pravy<>nil then p:=infix(uzel^.pravy); Result:='('+l+' '+Uzel^.operator+' '+p+')'; Procedura KRESLI(U,x,y,uroven) Tato procedura je v aplikaci zapouzdřena do metody Vykreslit. Metoda Vykreslit připraví objekty nutné pro vykreslení v rámci programovacího prostředí Borland Delphi a procedura Kresli vykreslí aritmetický strom (počínaje uzlem U). Vstupní parametry x a y udávají počáteční souřadnice pro vykreslování, parametr uroven pak udává kolik uzlů stromu ještě zbývá vykreslit (z důvodů výpočtu souřadnic jednotlivých uzlů v rámci vykreslované úrovně). Konstanta r_uzel udává rozměr vykreslovaného uzlu. procedure TForm1.Vykreslit(Uzel:PUzel); const r_uzel = 24; Bmp:TBitmap; w:integer; procedure Kresli(U:PUzel;x,y,uroven:Integer); R:TRect; xc,yc:integer; with Bmp.Canvas do R:=Rect(x,y,x+r_uzel,y+r_uzel); xc:=x+(r_uzel div 2); yc:=y+(r_uzel div 2); uroven:=uroven div 2; if U^.levy<>nil then MoveTo(xc,yc); LineTo(xc-r_uzel*uroven,yc+r_uzel*2); Kresli(U^.levy,x-r_uzel*uroven,y+r_uzel*2,uroven); 5
if U^.pravy<>nil then MoveTo(xc,yc); LineTo(xc+r_uzel*uroven,yc+r_uzel*2); Kresli(U^.pravy,x+r_uzel*uroven,y+r_uzel*2,uroven); if U^.operator=' ' then Rectangle(R); DrawText(Bmp.Canvas.Handle,PChar(IntToStr(U^.Hodnota)),Length(IntToStr(U^.Hodnota)),R,DT_VCENTER or DT_CENTER or DT_SINGLELINE) end Ellipse(R); DrawText(Bmp.Canvas.Handle,@U^.Operator,1,R,DT_VCENTER or DT_CENTER or DT_SINGLELINE); Image1.Picture.Assign(nil); Bmp:=TBitmap.Create; try w:=0; Hloubka(Uzel,w); Bmp.Height:=w*r_uzel*2; Bmp.Width:=Trunc(Power(2,w-1)*r_uzel)*2; with Bmp.Canvas do Brush.Color:=clWhite; Pen.Color:=clBlack; Pen.Width:=2; Font.Name:='Arial'; Font.Size:=10; Font.color:=clBlack; Font.Style:=[fsBold]; Kresli(Uzel,(Bmp.Width div 2)-(r_uzel div 2),8,Trunc(Power(2,w-1))); Image1.Picture.Assign(Bmp); finally Bmp.Free; 6
Procedura VYTVORITSTROM testovací metoda pro vytvoření, výpočet a vyčištění stromu. Aplikace obsahuje i další dvě ianty jako ukázky dalších aritmetických stromů. procedure TForm1.VytvoritStromClick(Sender: TObject); Strom,U1,U2:PUzel; h:integer; Strom:=PridatUzel(nil,'-',0); U1:=PridatUzel(Strom,'*',0); U2:=PridatUzel(U1,' ',7); U2:=PridatUzel(U1,' ',3); U1:=PridatUzel(Strom,'/',0); U2:=PridatUzel(U1,' ',15); U2:=PridatUzel(U1,' ',3); h:=0; Hloubka(Strom,h); edhloubka.text:=inttostr(h); Vykreslit(Strom); edhodnota.text:=inttostr(vypocitat(strom)); edinfix.text:=infix(strom); edprefix.text:=prefix(strom); edpostfix.text:=postfix(strom); Vycistit(Strom); 7