Grundbegrepp • Algoritm Ett ändligt set instruktioner för att lösa en specifik väldefinierad uppgift • Datatyp Värdemängd med operationer. (Objekt + operationer) • Datastruktur En datastruktur är en abstrakt beskrivning till skillnad från ‘datatyper’. En datatyp kan vara exempelvis Integer, String eller boolean. Det har en fast betydelse medan en datastruktur beskriver något odefinierbart, till exempel en lista eller array. • Diskret linjärt ordnad Ändligt antal ordnade element, med före och efter-relation. • Hanterbara problem Kan lösas praktiskt, t.ex. O(n), O(log n) – komplexiteten är begränsad av ett polynom.. Ohanterliga problem har tidskomplexitet 2^n, n!, n^n, komplexiteten är superpolynom. • Traversering Att besöka varje element exakt en gång, naturlig för datatypen. • (Mental) modell för en datatyp Vardaglig modell som kan representera en datatyp. T.ex schackbräde för datatypen fält. • Struktur hos dataobjekt Det man har kvar om man bortser från alla elementvärden. • Informell specifikation av en gränsyta Informell beskrivning av operationernas funktion. • Beräkningsbar En funktion är beräkningsbar om och endast om det finns någon turingmaskin som löser och beräknar funktionen. En turingmaskin är en tänkt, abstrakt maskin med en begränsad repertoar av operationer till sitt förfogande. • (O-)hanterlig ((in-)tractable) Kan inte lösas praktiskt tex O(2^n), O(n^n). Faktorisera tal till primtal. • Hashfunktion En hashfunktion avbildar en indextyp A till en mindre indextyp B, till exempel strängar till heltal • Fyllnadsgrad Andel upptagna platser i en statisk datatyp. • Position • Index Referens till en position • Element Har en position och ett värde. • Elementvärde Värdet på ett element Egenskaper/principer • FIFO (first-in-first-out). • LIFO (last-in-first-out). • Komplett träd Ofta bra balans, fyller på trädet från vänster till höger, en nivå i taget. • Fullt träd Ofta dålig balans, varje nod är antingen ett löv eller har två barn. • Lövnod En nod som inte har några barn • Rotnod Toppen av trädet, ursprungsnoden. • Sammanhängande graf Varje nod har en väg till varje annan nod. Om antalet bågar <antal noder -1 är grafen inte sammanhängande. Attribut för datatyper • Abstrakt datatyp Datatyp som endast definieras via sin gränsyta. En term som används när man beskriver, diskuterar eller använder en datatyp utan att ta hänsyn till om eller hur den är realiserad i ett programspråk/hårdvara. • Fysisk datatyp En datatyp som finns tillgänglig i en given maskinvara eller programspråk. Int, float, char • Enkel datatyp En datatyp vars objekt inte består av element som i sin tur är datatypsobjekt. Tex heltal. • Konstruerad datatyp En datatyp där man beskrivit både representationen för objekten och utförandet av operationerna. • Sammansatt datatyp En datatyp vars objekt består av element som i sin tur är datatypsobjekt. Elementen är organiserade på ett bestämt sätt. Tex lista av heltal. • Implementerad datatyp En datatyp som är komplett konstruerad från grunden där grunden är fysiska datatyper och klar att användas i ett program. • Homogen vs. heterogen datatyp Homogen – en sammansatt datatyp där elementen är av en och samma datatyp. Fält och Lista. Heterogen – inte samma datatyp • Dynamisk vs. statisk datatyp För en statisk datatyp kan storleken inte ändras efter den bestäms (vid ex konstruktion). För en dynamisk datatyp kan storleken förändras med operationer. • Sorterad vs. ordnad datatyp Ordnad innebär att det finns en viss ordning i datatypen, ett förstaelement, ett andra element osv. Inbördes ordning. Exempelvis: stack lista osv. Tabell, mängd är ej ordnade. I en sorterad datatyp bestäms ordningen av elementens värden. De strukturförändrande operationerna i gränsytan upprätthåller en sorteringsordning vid operationer. • Riktad datatyp Det finns en riktning, till exempel att man bara kan traversera framåt. • Hierarkiskt ordnad Pyramid? Strikt partiell ordning med linjärt ordnade förfäder samt ett minimum. Komplexitetsanalys • Asymptotisk komplexitetsanalys (prio 1) Räkna operationer, kom fram till en funktion T(n) baserad på operationerna. Hitta sedan C och n0. C hittas med lim n->oändligheten T(n)/g(n) +1. Finn n där T(n)<=ng(n) börjar gälla. Gör detta genom att sätta upp olikheten och lösa för n. Det kan då sägas att T(n) är O(g(n)) med c=.. och n0=… • Förenklad asymptotisk komplexitetsanalys En så kallad grovanalys av koden, nästlade loopar räknas. Ingnorera allt förutom dominerande termer. • Experimentell komplexitetsanalys Testkörningar som mäter tiden det tar för operationen. Implementera algoritmen och ta tid. • Absolut komplexitet Ett idealiserat mått på tidskomplexiteten i en fullständig implementation. Tittar under ytan. T(n). Antag att vi har en datatyp ADT1 som är skapad med hjälp av en annan datatyp ADT2. Hur benämner man komplexiteten när man inte bara tittar på "ytan" och ser hur många ADT2operationer som behövs i en ADT1-operation utan även undersöker komplexiteten hos ADT2-operationerna och komplexiteten hos de ADT-operationer de eventuellt bygger på? – Absolut komplexitet. • Relativ komplexitet Komplexitet beräknad i termer av en given abstrakt datatyps operationer, betraktade som elementära operationer. Tittar bara på ytan. Räknar operationer. Antag att vi har en datatyp ADT1 som är skapad med hjälp av en annan datatyp ADT2. Hur benämner man komplexiteten när man enbart tittar på "ytan" och ser hur många ADT2operationer som behövs i en ADT1-operation? – Relativ komplexitet • Definitionen av stora Ordo Vi definierar T(n) att vara O(g(n)) (“stort ordo av g av n”) om och endast om det existerar konstanter n0, c > 0 sådana att |T(n)|≤cg(n), ∀n ≥n0 Operationstyper Konstruktorer – skapar och bygger upp objekt. Finns grundkonstruktorer, konstruktorer som vidareutvecklar objekt och konstruktorer som kombinerar objekt. Exempel är Empty, create, list-insert, array-setvalue, stack-push, set-union. Inspektorer – används för att undersöka ett objekts inre på något sätt. Exempel är: inpectvalue, lookup, stack-top, set-choose, isempty, hasValue, set-memberof, has-left-child. Modifikatorer – ändrar ett objekts struktur och /eller elementvärden. Ex: array-set-value, table-remove, stack-pop, stack-push, set-insert, set-remove. Navigatorer – används för att ta sig fram genom objektets struktur. Förflyttningar eller traverseringar. Ex: list-first, list-end, list-next, list-previous, left-child. Komparatorer – jämför objekt av den aktuella typen med varandra. Ex: equal, set-subset. Grundläggande problemlösningsstrategier Brute force - Algoritm som testar alla möjliga kombinationer. Att tex testa 0000, 0001 … 9999 för att låsa upp en telefon. Rättfram ansats där man utgår direkt från problemställningen och de definitioner som finns där. Greedy algoritm - ta det, för tillfället, bästa alternativet. I varje steg titta på alla möjliga steg och välja den för tillfället bästa. Divide and conquer algoritm - Dela upp i mindre delar och löser rekursivt. Sen gör man en slutlösning för delarna. Söndra: dela upp problemet i flera delar som löses rekursivt. Härska: skapa slutlösning med hjälp av de andra. Dynamisk programmering – använder lite minne för att undvika att lösa samma delproblem flera gånger. Håller reda på redan kända lösningar. Ex. huffmankodning Metoder att traversera Bredden-först Man undersöker en nivå i taget, med början i roten, sedan rotens barn, därefter rotens barnbarn osv - tills alla noder undersökts. En Kö används för att hålla reda på noder man stött på men vars barn man inte undersökt. Djupet-först: Man följer varje gren i trädet från roten ut till lövet. Så länge man kan gå djupare till en obesökt nod fortsätter man nedåt. Stack används för att hålla reda på vägen från roten till den nod man undersöker. Preorder Man besöker först (därav pre) roten, sedan traverseras vart och ett av delträden också i preorder. Inorder Besöker först det första delträdet i (i ordning) inorder, sedan roten och sist resten av delträden i inorder. Postorder Besöker vart och ett av delträden i postorder - efteråt besöks roten. Kunna gränsytan utantill Ni ska kunna den informella specifikationen utantill, med namn, (in- och ut-) parametrar och beskrivning i ord av hur den fungerar. Ni behöver inte kunna den formella specifikationen. Lista: Empty() → tom lista Insert(val, pos, list) → sätter in efter pos, returnerar positionen för val. Ej def för sista pos Isempty(list) → Bool Inspect(pos, list) → returnerar värdet på pos, ej def för sista pos First(list) → pos End(list) → pos Next(pos, list) → pos efter pos Previous(pos, list) → pos före pos Remove(pos, list) → returnerar nya pos för elementet efter borttagna värdet. Riktad lista Empty() Insert(val, pos, list) Isempty(list) Inspect(pos, list) First(list) Isend(pos, list) → Bool Next(pos, list) Remove(pos, list) Stack s-stack, v-value Empty() → tom stack Isempty(s) → Bool Push(v, s) – lägger v överst Pop(s) – tar bort översta elementet Top(s) → returnerar värdet överst Kö q-queue, v-value Empty() → tom lista Isempty(q) → bool Front(q) → värdet på första elementet Enqueue(v, q) – sätter in v sist i kön Dequeue(v, q) – tar bort första elementet. Känna till funktionen för Ni ska veta hur nedanstående datatyper fungerar, men inte det exakta innehållet i deras gränsyta: Länk Enkel datatyp, referera till ett annat objekt. Pekare. n-Cell: En tippel av ett värde och ett fixt antal länkar som refererar till cellen. Tabell En ändlig avbildning av argument på värden. Uppslagsbok, nycke och värden. Array/Fält Statisk datatyp. Index. Homogen datatyp. Prioritetskö Vanliga köer bevarar tidsordningen, medan en prioritetskö används när man vill välja någon annan ordning på elementen. Har en grundmängd vars mängd ordnas av en prioritetsordning. Stack och kö är specialfall av prioritetskö. De har speciala prioritetsordningar. Kö: FIFO, prioritet alltid falsk. Stack: LIFO prioritet alltid sann (läggs överst i stacken). Hashtabell Data sparas tillsammans med en nyckel. Positionen i strukturen beräknas med en hashfunktion. Sluten hashning – linjär eller kvadratisk teknik Öppen hashning – hashvärdena läggs in i en tabell i stället (lista av listor). I lista nummer i ligger alla element med hashvärde i. Binärt sökträd Sorterat träd där vänster barn alltid är mindre än förälder, medan höger alltid är större. Insättning börjar i roten, sedan letas platsen där värdet passar in upp. Alltså görs jämförelser om> rot, då fortsätt i höger delträd, annars vänster. Fortsätt så tills rätt plats är hittad. Sökning i binärt sökträd sker genom att det sökta värdet jämförs med den aktuella nodens etikett, om inte lika, sker sökning rekursivt nedåt i vänster eller höger delträd allteftersom det sökta värdet går före respektive efter i sorteringordningen. Borttagning av noder i binära sökträd: Lövnod – bara att ta bort Nod mitt i: Om noden bara har ett delträd, flyya upp det. Om noden har två delträd (både höger och vänster) ersätt noden med noden med minsta värdet i höger delträd, alternativt noden med störst värde i vänster delträd. Lexikon Förenklad variant av mängd. Används för att hålla rätt på en samling objekt: sätta in, ta bort och undersöka medlemskap. Som en tabell utan tabellvärden. Träd Noder som är hierarkiskt ordnade genom en förfäder-relation som har ett minimielement, en rot. Ordnat träd – barnen bildar en lista, regemente. Oordnat träd – barnen bildar en mängd. Höjden för en nod = ”hur många steg behöver vi gå tills vi når längst ner i trädet” Nivån – 1 Djupet hos en nod är antalet bågar från noden till roten. Homogen datatyp. Binärt träd Ett binärt träd kan ha högst två barn. Höger och vänsterbarn är inte samma. Kan jämföras med en antavla över förfäder med yngst i roten. Heap Binärt partiellt sorterat träd där borttagning endast kan göras i roten. Finns max och min heap, min har minsta elementet i roten. Insättning sker på nedersta nivån på första lediga platsen till vänster. Elementet flyttas sedan för att passa in i ordningen. Borttagning sker i roten, och noden längst ner till höger flyttas till rotens plats för att sedan flyttas runt till rätt plats i ordningen. Trie En variant av ett träd. Barnen är organiserade som tabellvärden i en tabell som tillhör noden. Noderna har namn. Mängd En mängd är en fullständigt oordnad samling element eller medlemmar som alla är av en bestämd datatyp. Graf En variant av binär relation på en mängd. Består av en mängd noder och bågar. Gradtalet = Antalet bågar till grannar (inklusive sig själv). En ögla räknas 2 ggr. I riktade grafer är ingradtalet = antalet bågar som går till noden. Utgradtalet = antalet bågar som startar i noden och går till en annan nod. Komplett graf – alla noder är grannar till alla andra. Om antalet bågar < antal noder -1 är grafen inte sammanhängande. Sortering Stabil sortering Den inbördes relationen mellan två objekt med samma nyckel bibehålls vid sortering. Sorteringsalgoritmer Bubblesort O(n^2) - stabil Algoritmen innebär att det område av en lista som skall sorteras gås igenom upprepade gånger och parvisa jämförelser görs av intilliggande element. Om elementen är i fel ordning kastas elementens ordning om. Efter varje genomgång av ett område kommer det sista talet att ha hamnat på rätt plats. Vid nästa genomgång reduceras därför det område som gås igenom med ett. Efter hand som sorteringen fortlöper kommer den korrekt sorterade delen i botten av listan att växa med ett element per genomgång av den ännu osorterade delen av listan och de ännu osorterade elementen "bubblar" uppåt, därav namnet på sorteringsalgoritmen. Insertion sort O(n^2) - stabil Väljer ett element och jämför mot de andra för att se var i sorteringen det ska sättas in. 1. Jämför det nya talet med det sista talet i listan 2. Om det nya är större, lägg det sist i listan 3. Flytta annars det sista talet ett steg bakåt och jämför igen 4. Flytta så många tal som behövs ett steg bakåt för att sätta in det nya talet på rätt plats 5. Upprepa för varje nytt tal Selection sort O(n^2) - instabil Går igenom listan och plockar ut det minsta elementet, upprepar och sätter in i en ny lista som blir den sorterade listan. Mergesort o(nlog(n)) - stabil Merge Sort-algoritmen är av typen söndra och härska, och har konceptuellt följande steg för att sortera en lista med storlek n: 1. Dela upp listan i n lika stora dellistor (alla med längd 1) 2. Slå samman dellistorna parvis i sorterad ordning 3. Upprepa steg två, tills det bara finns en lista kvar Den slutgiltiga listan är original-listan i sorterad ordning. Quicksort O(nlog(n)) - instabil Quicksortalgoritmen är av typen söndra och härska, och består av följande steg: 1. Välj ett element, kallat pivot-element, ur listan. 2. Ordna om listan så att alla element som är mindre eller lika med pivot-elementet kommer före detta, och så att alla element som är större än pivot-elementet kommer efter detta. Pivot-elementet har nu fått sin rätta plats. 3. Sortera rekursivt de två dellistorna, det vill säga med just denna algoritm. Val av pivot är kritiskt! Man vill ha ett som är i mitten av sorteringsordningen eftersom listan delas in i två listor, en större och en mindre än pivot. Om man exempelvis väljer minst elementet kommer komplexiteten öka till O(n^2). Det blir så som instickssortering eller urvalssortering vid sned fördelning. Pivot-element Elementet som väljs först av en algoritm. För en del sorteringsalgoritmer finns två versionerbeskrivna: (En version som tar en in-lista och bygger upp en ut-lista. En version som jobbar direkt i in-listan, s.k. in-place. För algoritmer som finns i bägge versionerna räcker det med att ni kan en av versionerna. Bubblesort är in-place, övriga är "en lista in, en lista ut". Dessutom ska ni kunna använda en heap och ett binärt sökträd för att sortera. Ni ska kunna avgöra/veta vilka sorteringsalgoritmer som är stabila.) Sortering med en heap - instabil Heapsort får man genom att först stoppa in den osorterade sekvensen i en heap och sedan ta ut alla element igen. O(nlogn) Sortering med binärt sökträd Sortering med binärt sökträd görs genom att stoppa in den osorterade sekvensen i ett BST och sedan göra en in-order traversering av det. Sökning Linjärsökning O(n), Starta från början och sök tills elementet hittat eller sekvensen slut. Elementet finns: i medel gå igenom halva listan Elementet finns inte: i medel gå igenom hela listan Om listan är sorterad och elementet saknas: räcker att i medel leta genom halva listan. Binärsökning Om sekvensen har index (tex i en array eller numrerad lista) kan man söka binärt. Successiv halvering av sökintervallet. Jämför med elementet närmast mitten i intervallet. Om likhet – klart! Om det sökta värdet kommer före i sorteringsordningen fortsätt sökningen rekursivt i det vänstra delintervallet. Om det kommer efter i sorteringsordningen fortsätt sökningen rekursivt i det högra delintervallet. Vi får värsta-falls och medelkomplexitet O(logn). Djupet-först sökning Finns tre olika. Pre-, in-, och postorder. Bredden-först sökning Uppifrån vänster till höger, går igenom en hel nivå innan nästa. Övriga algoritmer Floyd Bygger på matrisrepresentation av en graf, denna innehåller vikterna på bågarna mellan noderna. Vill hitta kortaste vägen genom grafen. Från alla noder till alla noder. Algoritmen tar fram avståndet mellan två noder, för att sedan jämföra det med andra avstånd mellan samma noder via andra noder. Den sparar sedan kortaste vägen. Dijkstra Att finna kortaste vägen från en nod till alla andra noder. Använder en prioritetskö. Fungerar enbart på grafer med positiva vikter. Färgar noderna allt eftersom Man kollar på en nod, tar ut alla grannar, sparar avståndet till alla dessa och lägger bågarna i en prioritetskö. Sedan plockar man ut första ur kön, alltså närmsta granne, tar fram dens grannar och kollar alla avstånd. Om en granne redan är besökt, kollas om det nya avståndet via den aktuella noden är kortare, i så fall uppdateras avståndet med det nya och läggs in i kön. Prim Navigeringsorienterad specifikation. Söker ett uppspännande träd i grafen med minsta möjliga totala längd. Går ut på att bygga upp ett allt större träd som till slut spänner upp grafen. Man väljer i varje steg en båge med minimal vikt. Använd en prioritetskö för att hålla reda på vilka bågar som kan vara aktuella. Kruskal Utgå från en prioritetskö av alla bådar. Mängdorienterad specifikation. Huffman (bygga optimalt Trie, kodning, dekodning) Koder för sekvenser Gör en tabell över frekvenserna, välj sedan de två minsta frekvenserna och sätt ihop, summan står i föräldernoden. Kolla på nästa nod, vilken kan den kopplas ihop med för att få lägsta siffran? Är det den till vänster eller toppen av trädet till höger? Om det ger samma så välj det trädet som ger lägst höjd. Lägg sedan till nästa frekvens till det träd som har topnod där summan genererar minsta värdet. Pseudokod – programkonstruktion används för tilldelning = används för likhetsrelation Funktionsdeklaration: Algoritm name(param1, param2) Under Algoritm name(param1, param2) ska in-, och output specificeras. Input: beskrivning av parametrar Output: beskrivning av returvärden Minsta uppspännande träd Man söker ett uppspännande träd med minsta möjliga totala längd (höjd). Det minsta uppspännande trädet i en graf är en samling av kanter som kopplar ihop alla noder i grafen. Denna samling av kanter har tillsammans den minsta möjliga vikten. Alla noder måste alltså ingå. Navigerings- och mängdorienterad specifikation Navigering – bygger på grannarna till en nod. En grannskapsmängd. Ger mer effektiva navigeringsoperationer. Prims och dijkstras. Mängd – bygger på en mängd av bågar mellan noder. Kruskals och floyds. Vilka problem kan uppstå när man använder linjär teknik? Ett stort problem kan bli den sk. Klustringen som kan uppkomma, dvs att delar av hashtabellen fylls igen och kan leda till en lång kedja med kollisioner innan tom plats påträffas och söktiderna blir mer ordo(n) än den ordo(1) som man strävar efter. Val av mod vid sluten hashning görs utifrån att fyllnadsgraden inte bör vara mer än 75% och ett primtal för att få bättre spridning. Om vi då har 13 element passar mod 17 exempelvis. Vid öppe hashning räcker det att ha mod som är halva antalet element.