INF1010 e-ostadresser Ikke-faglige sørsmål til studieinfo@ifi.uio.no. Faglige sørsmål til blogen eller til <brukernavn>@ifi.uio.no: brukernavn navn kristoeb Kristoffer Egil Bonarjee josek Jose Louis Rojas stianf Stian Kjetil Friberg bendiko Bendik Rønning Ostad bmmender Bedeho Mender eseak Esen Angell Kristiansen richar Richard Rørmark daghf Dag Haavi Finstad larsstor Lars Storjord fredva Fredrik Valdmanis Sørsmål om ensum, forelesninger, øvelser og hold i ogaver kan sendes til lenumslærere og forelesere: brukernavn navn steing Stein Gjessing michael Stein Michael Storleer eseak Esen Angell Kristiansen jaasen Jon Petter Åsen Om ekervariable Forskjellen mellom variable og ekere, begreer rimitive tyes reference tyes variabel ekere ekervariable Veldig viktig å vite om det er en variabel eller en eker. Siden en eker også forandrer verdi (hva den eker å) er den variabel, og kalles derfor ofte variabel. Vi kan ikke se av navnet om det er en variabel eller en eker. I eksemelet er.rang en variabel (av tye int), mens.nesteperson er en eker (av tye Person). Dagens forelesning Pekerkjedelister Om ekervariable Innsetting Traversering av hele lista Strenger Sammenligne strenger String.comareToIgnoreCase(String s) Binære trær Litt om effektivitet Liste versus binærtre Et binærtreeksemel Binærtreoerasjoner Fe et ersonobjekt med et gitt navn Om ekervariable Forskjellen mellom variable og ekere En variabel (av 8 forskjellige tyer) har en verdi. De vanliste tyene er: int boolean double char En eker eholder en adresse, eller referanse til et objekt. Variablene med verdier ligger evt. i objektet, og kan bare leses og forandres indirekte. int 0 int i = 0; <fig venstre> i = 561; int 561 Person = new Person(); Person = null; Person Person P = ; Person Person «settes til å eke å det samme som (eker å)» i i
Innsetting Innsetting først 1 void settinnpersonforst( Person ) { 2 \\ Hvis l i s t a er tom, sett objektet i f ( == null ) = ; 4 else { 5 \\ minst et objekt i lista 6. nesteperson = ; = 8 } 9 } Innsetting Et listeobjekt med eker til siste element i lista Person Person Person Person Person nesteperson String navn nesteperson String nesteperson String nesteperson String nesteperson String Jonathan Susanne Imran Nikita Elisabeth navn navn navn navn null Person Person sisteperson Et objekt av klassen Personer Innsetting Innsetting sist (uten «sisteeker») 1 void settinnpersonsist( Person ) { 2 \\ Hvis l i s t a er tom, sett objektet i f ( == null ) = ; 4 else { 5 \\ fe siste element i lista 6 Person = ; while (. nesteperson! = null ) 8 =. nesteperson ; 9 \\ her er. nesteperson==null, altså er siste 10. nesteperson = 11 } 12 } Antar at Person-objektene i lista har et attributtint rang som objektene er sortert etter i stigende rekkefølge. Elementet som skal må da ikke havne så langt ut i lista at det får et element med høyere verdi å rang før seg selv. Vi leter oss fram til en tom nesteeker, eller til nesteperson eker å et objekt med høyere rang. Dette synes ganske enkelt, men vi vil se at når metoden skal virke både for en tom og en ikke-tom liste og for en liste som bare har et objekt, blir det nokså omstendelig. == null objektet skal først objektet skal sist objektet skal hverken først eller sist
1 void settinnpersonetterrang( Person ) { 2 Person = ; Boolean fortsett = true ; 4. nesteperson = null ; // hvorfor? 5 while (!= null && fortsett ) 6 if (. nesteperson!= null &&. nesteperson. rang <. rang ) =. nesteperson; 8 else fortsett = false ; 9 // == null eller. nesteperson. rang >=.rang 10 if ( == null ) { 11 // lista var tom 12 = ; 1 } 14 else if ( == ) { 15 if (.rang <. rang ) { 16. nesteperson =. nesteperson; 1 = ; 18 } 19 else { 20. nesteperson = ; 21 = ; 22 } 2 } 24 else { 25 // skal mellom og. nesteperson 26. nesteperson =. nesteperson; 2. nesteperson = ; 28 } 29 } 12 4 4 = ; 12 4 4 12 4 4 I settinnpersonetterrang linje 16: 12 4 4.nestePerson =.nesteperson; 12 4 4 1 void settinnpersonetterrang( Person ) { 2 Person = ; Boolean fortsett = true ; 4. nesteperson = null ; // hvorfor? 5 while (!= null && fortsett ) 6 if (. nesteperson!= null &&. nesteperson. rang <. rang ) =. nesteperson; 8 else fortsett = false ; 9 // == null eller. nesteperson. rang >=.rang 10 if ( == null ) { 11 // lista var tom 12 = ; 1 } 14 else if ( == ) { 15 if (.rang <. rang ) { 16. nesteperson =. nesteperson; 1. neste = ; 18 } 19 else { 20. nesteperson = ; 21 = ; 22 } 2 } 24 else { 25 // skal mellom og. nesteperson 26. nesteperson =. nesteperson; 2. nesteperson = ; 28 } 29 }
I settinnpersonetterrang linje 16: 12 4 4.nestePerson =.nesteperson; 12 4 4 Legg merke til at følgende fungerer korrekt når objektet som eker å skal mellom objektet som eker å og objektet som.neste eker å, selv når.neste er null ( eker å siste objekt): 1. neste =. neste ; 2. neste = ; Da kan vi dele roblemet i tilfeller etter hvor objektet skal : lista er tom først etter objektet (evt. null) eker å Dette kan vi bruke til å lage en enklere metode. (Ogave som gjennomgås mandag). 12 4 4.nestePerson = ; 12 4 4 12 4 4 Traversering av hele lista Eksemel: skrive ut alle ersonene i lista 1 // I klassen Personer : 2 void skrivalle ( ) { 4 Person = ; 5 while (! = null ) { 6. skriv ( ) ; =. nesteperson ; 8 } 9 } 10 11 // I klassen Person : 12 void skriv ( ) { 1 System. out. rintln (... ) ; 14 }
Traversering av hele lista Fjerne erson med gitt rang (en instans eller ingen) 1 Person fjern ( int rang) { 2 Person = ; Person forrige = null ; 4 while (!= null &&. rang!= rang { 5 forrige = ; 6 =. nesteperson; } 8 // er ersonen som skal fjernes, 9 // forrige er ersonen FØR denne i lis te n 10 if (!= null ) { 11 i f ( == ) { 12 // Sesialtilfelle : som skal fjernes står først 1 =. nesteperson; 14 } else { 15 forrige. nesteperson =. nesteperson; 16 } 1 } 18 return ; 19 } Sammenligne strenger String.comareToIgnoreCase(String s) Gitt to tekststrenger: Stringa,b; > 0 hvisa>b a.comaretoignorecase(b) = 0 hvisa=b < 0 hvisb>a Husketriks: vi tenker oss at vi giraostiv, ogbnegativ «alfabetisk vekt». Så legger vi de to vektene sammen. Er summen null, eraogblike. Er summen negativ erb alfabetisk større enna. Er summen ositiv, er a størst. Pluss kommer først (a), så minus (b). Kortere: +.comaretoignorecase( ) Sammenligne strenger String.comareToIgnoreCase(String s) ublic int comaretoignorecase(string str) Comares two strings lexicograhically, ignoring case differences. This method returns an integer whose sign is that of calling comareto with normalized versions of the strings where case differences have been eliminated by calling Character.toLowerCase(Character.toUerCase(character)) on each character. Parameters str - the String to be comared. Returns: a negative integer, zero, or a ositive integer as the the secified String is greater than, equal to, or less than this String, ignoring case considerations. Ved å sammenligne strenger, kan vi ordne dem leksiografisk (alfabetisk som i en ordbok). Litt om effektivitet I små rogrammer betyr det ikke noe om vi gjør oerasjoner å en ineffektiv måte. Datamaskinen er så rask at det som skal gjøres skjer fort nok. Med mange objekter i datastrukturen, eller med unødvendige reetisjoner av oerasjoner, kan rogrammet fort bli så tidkrevende at det er ubrukelig. Har vi mange elementer i lista og vi forventer ofte å måtte foreta oerajoner som å fe et bestemt element, eller å sortere lista, fes det en langt bedre ekerkjedestruktur med hensyn til effektivitet. Men først noen ord om effektivitet.
Litt om effektivitet Eksemel å liste med mange objekter I folkeregisteret er det registrert nesten 5 millioner ersoner. Hvis disse ligger i en liste slik at vi hver gang vi skal fe en erson må starte å begynnelsen av lista, kan vi forvente å måtte gjøre ca 2,5 millioner sammenligninger før vi fer ersonen vi er ute etter. Hvis det tar ett tusendels sekund å gjøre en sammenligning, kan vi forvente at det tar ca 2.500.000 0, 001s= 2.500s eller ca 2.500s/60=40 minutter å fe en bestemt erson. Litt om effektivitet Når datamengden blir «stor», eller vi må gjøre en oerajon «mange ganger», må vi også tenke å om rogrammet vi lager er effektivt, dvs. ikke tar «for lang» tid. Hva som er store datamengder, mange ganger og lang tid er tema for andre emner enn INF1010, men enkle resonnementer og beregninger rundt et rograms effektivitet er det greit å kunne gjøre. Vi bør også kjenne til at datastrukturer og algoritmers kvalitet i stor grad avhenger av effektivitet (= tidsforbruk). Litt om effektivitet Hvor lang tid vil det ta å sortere lista ved å fe det minste elementet med metoden over, ta det ut å sette det i en ny sortert liste, når vi bare ser å tiden det tar å fe objektene? Svar: Ca 95 år! Fordelen med ekerkjedelister, er at vi ikke trenger å vite noe om hvor mange elementer som skal i lista. Ulemen er at ekeradministrasjon lett fører til feil, og at store datamengder (mange elementer) blir ineffektivt. Liste versus binærtre I folkeregistereksemelet over så vi at en enkeltkjedet liste med en førsteeker er en lite egnet struktur når vi ofte skal fe objekter med et bestemt fødselsnummer. Det fes derimot en ekerstruktur som er like dynamisk som lista, men som gjør at vi kan fe et bestemt objekt mange ganger mer effektivt. Denne datastrukturen er et binært søketre, eller et binærtre.
Liste versus binærtre Hvordan ideen kan ha blitt til Liste versus binærtre Pekerstrukturen i et binærtre Det som gjenstår, er å organisere dataene slik at vi fer søkte element ved å undersøke maksimum 2 elementer. Siden halvarten av objektene ligger i nivå 1-22, må vi i snitt regne med å lete oss fram til nivå 22. Hvis letetiden er den samme som for lista, fer vi et element i løet av 22 0, 001s = 0, 022s omtrent to hundredels sekund. Jørgen Gunnar Stian Eirik Jan Pål Torjus Christian Elisabeth Henrik Jim Nikita Siv Susanne Vivi Andreas Darjan Gry Helge Imran Nguyen Phili Simen Tor Liste versus binærtre I hvert nivå i et binært søketre, er det lass til dobbelt så mange elementer som nivået over. Når vi leter går vi bare fra et nivå til det neste i ett skritt (tilsvarer =.neste for lister). På første nivå er det lass til ett element, å neste 2, det neste 4, 8, 16... På nivå 2, er det lass til 4.194.04 elementer. I nivåene over, er det lass til 4.194.0 elementer, tilsammen over 8 millioner elementer, hvilket er nok for vårt folkeregister. 2 64 128... 104856 209152 419404 Et binærtreeksemel Vi skriver om class Person slik at den nå har to ersonekere: 1 class Person { 2 String navn; Person venstre, // Peker t i l ers. alfabetisk før denne 4 høyre ; // Peker t i l ers. alfabetisk etter denne 5 } tye: String navn: navn "Sokrates" Sokrates tye: Person tye: Person venstre høyre Forenklet objekt Et objekt av klassen Person
Et binærtreeksemel Ideen er å ordne objektene slik at alle ersonene som vi fer ved å følge høyre-ekeren (høyre subtre) er alfabetisk større enn dette objektets navn. Tilsvarende skal alle ersonobjekter vi kan fe ved å følge venstre-ekeren (venstre subtre) være alfabetisk mindre enn dette objektets navn. Merk at siden et objekt der begge ekervariablene er null også er et binært tre (en «sire uten grener»), eker alle ekervariablene å trær av mindre og mindre størrelse etter som vi følger ekerne fra «rota» mot «bladene». Et binærtreeksemel Andreas Christian Darjan Eirik Elisabeth Gry Gunnar Helge Henrik Imran Jan Jim Jørgen Nguyen Nikita Phili Pål Simen Siv Stian Susanne Tor Torjus Vivi Fra sortert liste (øverst) til binært søketre Jørgen Gunnar Stian Eirik Jan Pål Torjus Christian Elisabeth Henrik Jim Nikita Siv Susanne Vivi Andreas Darjan Gry Helge Imran Nguyen Phili Simen Tor Et binærtreeksemel Jasmina Imran Nikita Objekter som har navn som kommer alfabetisk foran (mindre enn) "Imran" Objekter hvor navn er større enn "Nikita" Når vi skal legge til et ersonobjekt, sjekker vi først navnet til førsteperson. Her har vi fire muligheter: 1. førsteperson == null, objektet med avnet er første objekt. 2. navnet er likt, ingenting legges til (i dette eksemlet). avnet er større og ersonen skal legges til (treet) høyre 4. avnet er mindre og ersonen skal legges til (treet) venstre
Metoden vi bruker for å legge til et objekt har en ersoneker som arameter. Personen som ersonekeren eker å skal settes i treet. Metoden er definert i en omgivelse der Person ersontreet eker å ersonobjektet som er roten i hele treet. Metoden kalles med kallet settinnitre(nyerson); I skissen å forrige lysark er vi framme når == null; Vi har da ikke lenger tilgang til objektet som vi skal hekte det nye objektet til. Introduserer en hjeleeker som «henger ett tr etter»: 1 void settinnitre ( Person ) { 2 // Objektet skal s e t t e s i ersontreet i f ( ersontreet == null ) ersontreet = ; 4 else { 5 int smnlgn = 4; 6 Person siste, eker = treet ; while ( eker! = null ) { 8 siste = eker; 9 smnlgn = eker.navn.comaretoignorecase (.navn ) ; 10 if ( smnlgn < 0 ) eker = eker.høyre ; 11 else if ( smnlgn > 0 ) eker = eker. venstre ; 12 } 1 if ( smnlgn < 0 ) siste.høyre = ; 14 else i f ( smnlgn > 0 ) siste. venstre = ; 15 } 16 } Hvordan vi sammenligner tekststrenger er ikke vesentlig her, men i eksemlet har jeg brukt comaretoignorecase (fra String-klassen) som returnerer et heltall (se ovenfor). Vi lager en skisse: 1 void settinnitre ( Person ) { 2 // Objektet ekt å av skal i ersontreet i f ( ersontreet == null ) ersontreet = ; 4 else { 5 Person = treet ; 6 while (! = null ) { int smnlgn =.navn.comaretoignorecase (.navn ) ; 8 if ( smnlgn < 0 ) =.høyre ; 9 else if ( smnlgn > 0 ) =. venstre ; 10 } 11 i f ( smnlgn < 0 ) <sett ersonen i treet ekt å av høy 12 else i f ( smnlgn > 0 ) <sett ersonen i treet ekt å a 1 } 14 } Metoden Forelesning er korrekt i INF1010 hvis 20. januar treet 2009 er tomt. Hvis treet ikke michael@ifi.uio.no er tomt (tre!= null) vandrer vi nedover (sic!) treet til vi Henrik Andreas Gunnar Elisabeth Helge Christian Gry Darjan Eirik Henrik Jørgen Andreas Susanne Gunnar Nikita Jim Vivi Pål Elisabeth Christian Jørgen Darjan Eirik Jan Torjus Tor Gry Simen Stian Imran Nguyen Helge Phili Siv Imran Jan Jørgen Jim Susanne Nguyen Nikita Pål Phili Tor Simen Torjus Stian Vivi Slik blir treet hvis navnene kommer slik lista over er (fra øverst og nedover) Siv
Andreas Christian Darjan Eirik Elisabeth Slik blir treet hvis erson objektene settes i alfabetisk rekkefølge. Gry Gunnar Helge Henrik Resten i stigende orden Fe et ersonobjekt med et gitt navn blir nokså likt det å sette en erson. Metoden skal returnere med en eker til et ersonobjekt i treet som har likt navn med arameteren. Her lar vi treet vi skal lete i også være arameter. Når vi skal lete i hele treet, bruker vi kallet fitre(ersontreet, navnet). Sette et ersonobjekt i treet 1 \ scritsize 2 ublic void settinnitreet ( Person ) { if ( rotperson == null ) rotperson = ; 4 else settinn (, rotperson ) ; 5 } 6 rivate void settinn ( Person, Person tre ) { 8 int smnlgn = tre. sammenlign( ) ; 9 if ( smnlgn < 0 ) { 10 if ( tre.høyre == null ) tre.høyre = ; 11 else settinn (, tre.høyre ) ; 12 } 1 else if ( smnlgn > 0 ) { 14 if ( tre. foran == null ) tre. venstre = ; 15 else settinn (, tre. venstre ) ; 16 } 1 } Fe et ersonobjekt med et gitt navn 1 Person fitre ( Person ersontre, String navn) { 2 Person funnetperson = null ; Person = ersontre; int smnlgn = 5621; 4 while (! = null ) { 5 smnlgn =.navn.comaretoignorecase (navn ) ; 6 if ( smnlgn == 0 ) { // funnet funnetperson = ; 8 = null ; // for å avslutte whileløkka 9 } 10 else if ( smnlgn < 0 ) =.høyre ; 11 else if ( smnlgn > 0 ) =. venstre ; 12 } 1 return funnetperson ; 14 }