3.1 Symboltabeller

Symboltabeller.

En symboltabel er en datastruktur der knytter en værdi til en entydig nøgle. Datastrukturen understøtter to grundlæggende operationer: at indsætte et nyt nøgle-værdi-par i tabellen og at søge efter en givet nøgle (eller slå nøglen op) og returnere den tilknyttede værdi.

Symboltabeller bruges i mange sammenhænge, nogle eksempler er vist i følgende tabel:

anvendelseformål for søgningnøgleværdi
ordbogfinde definitionopslagsord, lemmadefinition
bogindeksfinde relevante sideremneordliste af sidenumre
kontohåndteringbehandling af transaktionerkontonummertransaktionsdetaljer
websøgemaskinefinde relevante websidersøgeordliste af sidenavne
compilerfinde type og værdivariabelnavntype og værdi

Abstrakt datatype for symboltabeller.

Følgende APG beskriver de grundlæggende opererationer for symboltabeller.

public class ST<Key, Value>
ST()en tom symboltabel
voidput(Key key, Value val)indsæt nøgle-værdi-parret. Fjern nøglen hvis val er null.
Valueget(Key key)værdien knyttet til den givne nøgle key, eller null hvis nøglen ikke findes.
voiddelete(Key key)fjern den givne nøgle (samt den tilknyttete værdi).
booleancontains(Key key)findes den givne nøgle?
booleanisEmpty()er symboltabellen tom?
intsize()antallet af nøgle-værdi-par
Iterable<Key>keys()alle nøgler

For at skikre at vores implementationer er konsistente, kompakte og anvendlige, vedtager vi nogle generelle konventioner for symboltabeller.

Ordnede symboltabeller.

I mange anvendelser er nøgler sammenlignelige, dvs. at nøgleobjekterne opfylder kravene i grænsefladen Comparable. I så fald kan nøglerne a og b. sammenlignes med udtrykket a.compareTo(b). Mange symboltabeller udnytter den implicitte ordning af nøglerne som er givet af grænsefladen Comparable til effektive implementationer af operationerne put() og get(). Når symboltabellens nøgler er ordnene, så kan vi udvide APG’en med en række naturlige og nytte funktioner, der tager udgangspunkt i nøglernes indbyrdes ordning.

public class ST<Key extends Comparable<Key>, Value>
ST()en tom symboltabel
voidput(Key key, Value val)indsæt nøgle-værdi-parret. Fjern nøgle hvis val=null.
Valueget(Key key)værdien knyttet til nøglen key, eller null hvis nøglen ikke findes.
voiddelete(Key key)fjern den givne nøgle (og værdien knyttet til den).
booleancontains(Key key)er den givne nøgle knyttet til en værdi?
booleanisEmpty()er symboltabellen tom?
intsize()antallet af nøgle-værdi-par
Keymin()mindste nøgle
Keymax()største nøgle
Keyfloor(Key key)gulvet, dvs. største nøgle mindre end eller lig med den givne nøgle key
Keyceiling(Key key)loftet, dvs. mindste nøgle større end eller lig med den givne nøgle key
intrank(Key key)rangen, dvs. antallet af nøgler skarpt mindre end den givne nøgle key
Keyselect(int k)nøglen af rang r
voiddeleteMin()fjern mindste element
voiddeleteMax()fjern største element
intsize(Key lo, Key hi)antal nøgler i intervallet [lo..hi]
Iterable<Key>keys(Key lo, Key hi)alle nøgler i intervallet [lo..hi], i sorteret orden
Iterable<Key>keys()alle nøgler
Examples of ordered symbol table operations

Eksempelklienter.

Vi betragter to klienter: en testklient, som skal køre vores implementationer på små indput, og en klient til at måle tidsforbrug.

Sekventiel søgning i en uordnet hægtet liste.

Programmet SequentialSearchST.java implementerer APG’en for (ikke-ordnede) symboltabeller ved brug af en hægtet liste af knuder, der indeholder nøgler og værdier. For at implementere opslaget get(), gennemsøger vi hele listen og bruger equals() til at sammenligne søgenøglen med nøglerne i hver knude. Hvis vi finder en knude men den søgte nøgle, returnerer vi knudens værdi, ellers returnerer vi nulværdien null. For at implementere indsættelsen put(), gennemsøger vi ligeledes hele listen og bruger equals() til at sammenligne den givne med nøglerne i hver knude. Hvis vi finder en knude med den givne nøgle, ændrer vi knudens værdi til den givne værdi. Ellers skaber vi en ny knude med den givne nøgle og værdi og hægter den i starten af listen. Denne metode kaldes sekventiel søgning.

Sequential search

Påstand A.

Betragt en symboltabel implementeret som uordnet hægtet liste af \(N\) elementer. Indsættelse og forgæves søgning bruger \(N\) sammenligninger. Vellykket søgning bruger \(N\) sammenligninger i værste fald. Specielt bruger indsættelse af \(N\) nøgler i en oprindeligt tom datastruktur \(\sim\frac{1}{2}N^2\) sammenligninger.

Binærsøgning i en ordnet række

. Programmet BinarySearchST.java implementerer programmeringsgrænsefladen for ordnede symboltabeller. Datastrukturen består af to korresponderende rækker for henholdvis nøgler og værdier, sådan at nøglerne står i sorteret orden. Implementationen baserer sig på rangeringsmetoden rank(), som giver antallet af nøgler mindre end eller lig med den givne nøgle. For opslaget get() giver rangen præcis den givne nøgles position i den sorterede nøglerække, hvis den findes. For indsættelsen put() finder rangen præcis den værdi i værdirækken, der skal opdateres, hvis nøglen allerede findes. Ellers angiver rangen den position, hvor begge rækker skal udvides med det givne nøgle-værdi-par. Hertil skal alle større rækkeindgange flyttes en position til højre for at give plads; flytningen implementeres lettest fra højre til venstre.

Binary search symbol table

Påstand B.

Binarsøgning i en ordnet række med \(N\) nøgler bruger i værste fald højst \(1+\operatorname{lg} N \) sammenligninger for hver søgning. Indsættelse af en ny nøgle i en ordnet række bruger ~\(2 N\) rækkeadgange i værste fald, så indsættelse af \(N\) nøgler i en oprindeligt tom datastruktur bruger ~\( N^2\) rækkeadgange i værste fald.