28. príklady


Posledná zmena: 4.3.2003

Banner Text spájané zoznamy II.

Procedurálny typ

deklarácie:

Daný je binárny súbor studenti.dat, v ktorom sú uložené vety tohoto typu:

kružok - ročník a kód kombinácie, napr. 3MI1, 1i1, 4bi

type
  veta = record
    meno: string[10];
    priezv: string[15];
    mesto: string[15];
    rok_nar:1980..1990;
    kruzok: string[5];
    znamky: string;    // napr. 'ABAFD'
  end;

urob, ak platí...

Vytvorte triedu TSZoznam (zoznam študentov), ktorá bude môcť uchovávať údaje z daného súboru v spájanom zozname. Zadefinujte jej metódy (použite procedurálne parametre a flitre), pomocou ktorých:

  • Vytvoríte zoznam študentov načítaním ich údajov zo súboru.
  • Prebehnete celý zoznam a každému študentovi, ktorý nemá medzi známkami F, zvýši číslo ročníka o jedna.
type
  proc=procedure(v:TSVrchol);
  filter=function(v:TSVrchol):boolean;

procedure zvysRocnik(v:TSVrchol);
begin
  if v.kruzok[1]<'5' then
    v.kruzok[1]:=chr(ord(v.kruzok[1])+1);
end;

function trebaZvysitRocnik(v:TSVrchol):boolean;
var
  i:integer;
begin
  Result:=true;
  for i:=1 to high(v.znamky) do
    if v.znamky[i]='F' then Result:=false;
end;
   
procedure TSZoznam.urobAKplati(p:proc; f:filter);
var
  pom:TSVrchol;
begin
  if z<>nil then begin
    pom:=z;
    while (pom<>nil) do begin
      if not assigned(f) or f(pom) then p(pom);
      pom:=pom.next;
    end;
  end;
end;

filtrovaný výpis do súboru:

Vypíšete do súboru mená a priezviská všetkých študentov, ktorí sú starší ako 20 rokov, tých, ktorí sú z Bratislavy, ...

type
  filter=function(v:TSVrchol):boolean;

function starsiAko20(v:TSVrchol):boolean;
begin
  Result:=v.rok_nar<1982;
end;

function Blavaci(v:TSVrchol):boolean;
begin
  Result:=v.mesto='Bratislava ';
end;

procedure TSZoznam.vypisDoSuboru(f:filter);
var
  fs:TStream;
  p:TSVrchol;
begin
  if z<> nil then begin
    fs:=TFileStream.Create
           ('Niektoristudenti.dat',fmCreate);
    p:=z;
    while p<>nil do begin
      if not assigned(f) or f(p) then begin
        fs.Write(p.meno,sizeof(p.meno));
        fs.Write(p.priezv,sizeof(p.priezv));
      end;
      p:=p.next;
    end;
    fs.Free;
  end;
end;

filtrovaný podzoznam:

Vytvoríte nový zoznam, ktorý bude obsahovať len tých študentov, ktorých študijný priemer je <1.5. Tento zoznam vráti metóda ako svoju hodnotu.

type
  filter=function(v:TSVrchol):boolean;

function PriemPod1_5(v:TSVrchol):boolean;
begin
  ...
end;

function TSZoznam.vytvorZoz(f:filter):TSZoznam;
var
  p:TSVrchol;
  v:veta;
begin
  Result:=TSZoznam.Create;
  p:=z;
  while p<>nil do begin
    if not assigned(f) or f(p) then Result.pridajK(v);
    p:=p.next;
  end;
end;

// potom mozno vypisovat napr. takto

procedure TForm1.Button1Click(Sender: TObject);
var
  nz:TSZoznam;
begin
  nz:=zoz.vytvorZoz(PriemPod1_5);
  Memo1.Lines.Add(nz.vypis);
  nz.Free;
end;

procedurálny typ ako metóda

malý grafický editor:

v ktorom bude aj skupina 3 radioButtonov:
Ak bude stlačený prvý, budete pri pohybe myšou a stlačenom ľavom tlačidle myši do plochy kresliť krúžky, ak bude stlačený druhý rádiobutton - elipsy, k bude stlačený tretí - šikmé čiarky. Použite procedurálny typ.

kresliKruzky, kresliElipsy a kresliCiarky sú procedury typu proc

type
  proc = procedure(X,Y: Integer);  

var
  p:proc;

procedure TForm1.RadioButton1Click(Sender: TObject);
begin
  p:= kresliKruzky;
end;

procedure TForm1.RadioButton2Click(Sender: TObject);
begin
  p:= kresliElipsy;
end;

procedure TForm1.RadioButton3Click(Sender: TObject);
begin
  p:=kresliCiarky;
end;

procedure TForm1.Image1MouseMove(...);
begin
  if ssLeft in Shift then p(X,Y);
end;

inak:

metóda kruzky musí byť rovnakého typu ako Image1MouseMove

procedure TForm1.kruzky(Sender:TObject;
                     Shift:TShiftState; X,Y:Integer);
begin
  if ssLeft in Shift then
    Image1.Canvas.Ellipse(x-3,y-3,x+3,y+3);
end;

procedure TForm1.Button16Click(Sender: TObject);
begin
  Image1.OnMouseMove:=kruzky;
end;

Dvojsmerný spájaný zoznam

deklarácie:

Máme dané deklarácie pre dvojsmerného spájaného zoznamu

napíšte metódy pre dvojsmerný spájaný zoznam

type
  prvok = integer;
  TVrchol = class
    info:prvok;
    prev,next:TVrchol;
    constructor Create(i:prvok; p,n:TVrchol);
    function vypis:string; virtual;
  end;

  TZoznam = class
  private
    z:TVrchol;
    k:TVrchol;
  public
    constructor Create;
    destructor Destroy; override;
    ...
  end;

metóda dvoj - opraví zoznam:

z dvojsmerného spájaného zoznamu, ktorý má zatiaľ všetkých prev = nil, vytvorí "pospájaný" dvojsmerný spájaný zoznam

procedure TZoznam.dvoj;
var
  p:TVrchol;
begin
  if z<>nil then begin
    p:=z;
    while p.next<>nil do begin
      p.next.prev:=p;
      p:=p.next;
    end;
  end;
end;

pridaj vrchol na začiatok:

 

procedure TZoznam.pridajZ(v:TVrchol);
begin
  v.prev:=nil; v.next:=z;
  if k=nil then k:=v
  else z.prev:=v;
  z:=v;
end;

pridaj vrchol pred iný vrchol:

 

procedure TZoznam.vsunPred(pred,v:TVrchol);
begin
  if pred=z then pridajZ(v)
  else if pred=nil then pridajK(v)
  else begin
    v.prev:=pred.prev; pred.prev:=v; v.next:=pred;
    v.prev.next:=v;
  end;
end;

vyhoď vrchol:

 

procedure TZoznam.vyhod(v:TVrchol);
begin
  if v=nil then           // nič
  else if z=v then begin
    z:=z.next;
    if z<>nil then z.prev:=nil else k:=nil;
    v.Free;
  end
  else begin
    v.prev.next:=v.next;
    if v.next<>nil then v.next.prev:=v.prev
    else k:=v.prev;
    v.Free;
  end;
end;

otočí zoznam:

(vyskúšajte to aj rekurzívne)

procedure TZoznam.otoc;
var
  p,q:TVrchol;
begin
  if z<>nil then begin
    k:=z;
    p:=z;
    while p<>nil do begin
      q:=p.next;
      p.next:=p.prev;
      p.prev:=q;
      z:=p;
      p:=q;
    end;
  end;
end;

vráti minimálny vrchol:

test z=k znamená, že je zoznam prázdny alebo jednoprvkový

function TZoznam.min:TVrchol;
var
  p:TVrchol;
begin
  Result:=z; if z=k then exit;
  p:=z.next;
  while p<>nil do begin
    if p.info<Result.info then Result:=p;
    p:=p.next;
  end;
end;

minsort:

  • nájde minimálny prvok a presťahuje ho na začiatok
  • v zvyšku zoznamu nájde minimálny a zaradí ho za minimálny
  • atď.
function TZoznam.minsort:TZoznam;
begin
end;

Cyklický zoznam

deklarácie:

Zadefinujme triedu pre cyklický zoznam, v ktorom

  • posledný prvok odkazuje na prvý
  • ak je zoznam neprázdny, tak neobsahuje nil
  • treba správne rozpoznať koniec zoznamu (napr. pre výpis, všetky, hľadanie hodnoty a pod.)
  • môžeme použiť len jediný smerník a to na posledný prvok (premenná k - nasledovníkom k je začiatok zoznamu), teda netreba potom udržiavať dva smerníky
type 
  TCyklZoznam = class
  private
    k:TVrchol;  // koniec zoznamu, k.next je začiatok

  public
    constructor Create;
    destructor Destroy; override;
    function vypis:string;
    procedure pridajZ(i:prvok);
    procedure pridajK(i:prvok);
    procedure vsun(pred:integer; i:prvok);
    function pocet:integer;
    function ity(i:integer):TVrchol;
    procedure ZrusZ;
    procedure ZrusK;
    procedure vyhod(p:TVrchol);
  end;

constructor TCyklZoznam.Create;
begin
  k:=nil;
end;

deštruktor:

 

destructor TCyklZoznam.Destroy;
var
  p:TVrchol;
begin
  if k<>nil then begin
    while k.next<>k do begin
      p:=k.next; k.next:=p.next; p.Free;
    end;
    k.Free;
  end;
end;

výpis:

 

function TCyklZoznam.vypis:string;
var
  p:TVrchol;
begin
  Result:='';
  if k<>nil then begin
    p:=k;
    repeat
      p:=p.next;
      Result:=Result+p.vypis;
    until p=k;
  end;
end;

pridaj na začiatok:

 

procedure TCyklZoznam.pridajZ(i:prvok);
begin
  if k=nil then begin
    k:=TVrchol.Create(i,nil);
    k.next:=k;
  end
  else k.next:=TVrchol.Create(i,k.next);
end;

pridaj na koniec:

 

procedure TCyklZoznam.pridajK(i:prvok);
begin
  if k=nil then begin
    k:=TVrchol.Create(i,nil);
    k.next:=k;
  end
  else begin
    k.next:=TVrchol.Create(i,k.next);
    k:=k.next;
  end;
end;

pridaj na koniec - inak, pomocou pridajZ:

 

procedure TCyklZoznam.pridajK(i:prvok);
begin
  pridajZ(i);
  k:=k.next;
end;

počet prvkov:

 

function TCyklZoznam.pocet:integer;
var
  p:TVrchol;
begin
  Result:=0;
  if k<>nil then begin
    p:=k;
    repeat
      p:=p.next;
      inc(Result);
    until p=k;
  end;
end;

í-ty prvok zoznamu:

 

function TCyklZoznam.ity(i:integer):TVrchol;
begin
  if k=nil then Result:=nil
  else begin
    Result:=k.next;
    while (Result<>k) and (i>1) do begin
      Result:=Result.next;
      dec(i);
    end;
  end;
end;

vsuň pred prvok:

 

procedure TCyklZoznam.vsun(pred:integer; i:prvok);
var
  p,q:TVrchol;
begin
  if (pred<2) or (k=nil) then pridajZ(i)
  else begin
    q:=k.next;
    while (q<>k) and (pred>2) do begin
      dec(pred);
      q:=q.next;
    end;
    if q=k then pridajK(i)
    else q.next:=TVrchol.Create(i,q.next);
  end;
end;

zruš prvý prvok:

 

procedure TCyklZoznam.zrusZ;
var
  p:TVrchol;
begin
  if k<>nil then
    if k=k.next then begin k.free; k:=nil; end
    else begin
      p:=k.next; k.next:=p.next; p.Free;
    end;
end;

zruš posledný prvok:

aby sme mohli zrušiť posledný, musíme nájsť predposledný

procedure TCyklZoznam.zrusK;
var
  p:TVrchol;
begin
  if k<>nil then
    if k=k.next then begin k.free; k:=nil; end
    else begin
      p:=k.next; while p.next<>k do p:=p.next;
      p.next:=k.next; k.Free; k:=p;
    end;
end;

vyhoď prvok zoznamu:

treba dať pozor na to, že ak p nie je prvkom zoznamu, aby sa to nezacyklilo hľadaním p

procedure TCyklZoznam.vyhod(p:TVrchol);
var
  q:TVrchol;
begin
  if k=nil then exit;
  if p=k.next then ZrusZ
  else begin
    q:=k.next;
    while (q<>k) and (q.next<>p) do q:=q.next;
    if q<>k then begin
      q.next:=p.next;
      if k=p then k:=q;
      p.Free;
    end;
  end;
end;

NDÚ:

  • Zadefinujte ďalšie metódy triedy cyklický zoznam. Porozmýšľajte nad dvojsmerným cykl. zoznamom.

Zoznam s fiktívnym vrcholom

  • už pri inicializácii zoznamu (konštruktor Create) sa vytvorí jeden vrchol, ktorý bude fiktívny (nebude nás zaujímať hodnota vo vrchole)
  • takýto zoznam nikdy nebude prázdny! => zjednodušia sa všetky metódy, lebo nemusia testovať na prázdny zoznam
  • preprogramujte pridajZ a iné metódy
  • rozmyslite si, ako sa zmení cyklický zoznam s fiktívnym vrcholom, dvojsmerný zoznam s fiktívnym vrcholom, cyklický dvojsmerný, ...


© 2003 AB, KVI
blaho@fmph.uniba.sk