61
1 Fundamentale datastrukturer

Fundamentale datastrukturer

Embed Size (px)

DESCRIPTION

Fundamentale datastrukturer. Plan. Definitioner : abstrakt datatype, datastruktur Elementære datastrukturer og abstrakte datatyper : arrays, stakke, køer, hægtede lister, træer, hashtabeller, prioritetskøer. - PowerPoint PPT Presentation

Citation preview

Page 1: Fundamentale datastrukturer

1

Fundamentale datastrukturer

Page 2: Fundamentale datastrukturer

2

• Definitioner: abstrakt datatype,

datastruktur

• Elementære datastrukturer og abstrakte datatyper: arrays,

stakke, køer,

hægtede lister,

træer,

hashtabeller,

prioritetskøer

Plan

Page 3: Fundamentale datastrukturer

3

Definitioner

En type er en samling af værdier.

Eks. int betegner i Java den type, der udgøres af heltallene fra -2147483648 til 2147483647.

En abstrakt datatype er en datatype, der udelukkende er specificeret ved hjælp af typen og de tilknyttede operationer.

Kun operationernes input/output-relationer er specificeret - ikke deres konkrete realisering. Hverken datarepræsentation eller algoritmer må medtages i specifikationen.

En abstrakt datatype specificerer “hvad”, men ikke “hvordan”.

En datatype er en type tilknyttet en mængde af operationer på typen.

Eks. int er en datatype. Addition er et eksempel på en tilknyttet operation.

Page 4: Fundamentale datastrukturer

4

Abstrakte datatyper

En ADT skjuler den konkrete implementation fra anvenderen (klienten).

Fordele: (1) Det er lettere at bruge noget, hvis det ikke kræver internt kendskab til virkemåden. Tænk f.eks. på en radio eller en vaskemaskine

(2) Den konkrete implementation kan ændres, uden at klienten behøver at få det at vide.

Realisering i Java: En ADT kan realiseres som en klasse, hvor data er private. Brug“getters” (accessors) and “setters” (mutators) til henholdsvis at tilgå og ændre data.

Page 5: Fundamentale datastrukturer

5

En datastruktur er en samling variable, muligvis af forskellig type, der er indbyrdes forbundet på en eller anden måde.

Realisering i Java: ved simple variable, arrays og klasseobjekter

Datastruktur

Page 6: Fundamentale datastrukturer

6

(1) Datastruktur:

Et array er en sammenhængende blok af lagerceller, hvor hver lagercelle indeholder et dataelement af en fast længde.

Arrays(to perspektiver)

cellearray

Page 7: Fundamentale datastrukturer

7

(2) Abstrakt datatype:

Et array er en samling af dataelementer af samme type, hvor hvert dataelement kan identificeres med et heltal, kaldet indeks.

Med dette perspektiv kan et array implementeres på mange måder.

Page 8: Fundamentale datastrukturer

8

Datastrukturen array

Realisering i Java:

Oprettelse:

int a[] = new int[100];

eller

int[] a = new int[100];

opretter et array med 100 elementer af typen int:

a[0], a[1], ..., a[99]

Tilgang til et element: a[27] Aflæsning af arrayets længde: a.length (= 100)

Page 9: Fundamentale datastrukturer

9

Vigtig egenskab

Tilgangstiden til ethvert element er konstant.

a[0]a[1]a[2]a[3]a[4]a[5]a[6]

0:1:2:3:4:5:6:

adresse(a[i]) = adresse(a[0]) + i*længde(type)

adresse(a[0])

Page 10: Fundamentale datastrukturer

10

Eratosthenes si(cirka 200 f. Kr.)

Et primtal er et positivt heltal ≥ 2, som ikke er deleligt med andre tal end 1 og sig selv, f.eks. 2, 3, 5, 7, 11.

Ide til algoritme: Opret et boolean array isPrime med alle elementer sat til true. Gennemløb herefter alle tal mellem 2 og N, som er produktet af to tal (begge > 1), og sæt isPrime til false for disse. Til sidst udskrives de tal, i, hvor isPrime[i] stadig er true.

Problem: udskriv alle primtal ≤ N

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50

Page 11: Fundamentale datastrukturer

11

public class Eratosthenes {final static int N = 100000000;public static void main(String args[]) { boolean isPrime[] = new boolean[N+1];

int i, j; for (i = 2; i <= N; i++)

isPrime[i] = true; for (i = 2; i <= N/2; i++) for (j = 2; j <= N/i; j++)

IsPrime[i*j] = false; for (i = 2; i <= N; i++)

if (IsPrime[i]) System.out.print(i + " ");

System.out.println();}

}

Vi skal gennemløbe alle tal i*j, hvor i ≥ 2, j ≥ 2 og i*j ≤ N.

• Hvis j ≥ 2 og i*j ≤ N, så må i ≤ N/2.

• Hvis i*j ≤ N, så må j ≤ N/i.

Vanskeligheden ligger i gennemløbet.

Page 12: Fundamentale datastrukturer

12

• Koden kan effektiviseres ved at indsætte testen if (isPrime[i]) før j-løkken. Hvorfor?

• I j -løkkens initialiseringsdel kan j = 2 erstattes med j = i. Hvorfor?

• I i-løkkens betingelsesdel kan i <= N/2 erstattes med i*i <= N. Hvorfor?

for (i = 2; i <= N/2; i++) for (j = 2; j <= N/i; j++) IsPrime[i*j] = false;

Effektivisering

for (i = 2; i*i <= N; i++) if (isPrime[i]) for (j = i; j <= N/i; j++) IsPrime[i*j] = false;

Page 13: Fundamentale datastrukturer

13

Version 1: for (i = 2; i <= N/2; i++) for (j = 2; j <= N/i; j++) IsPrime[i*j] = false;

Version 2: for (i = 2; i <= N/2; i++) if (isPrime[i]) for (j = 2; j <= N/i; j++) IsPrime[i*j] = false;

Version 3: for (i = 2; i <= N/2; i++) if (isPrime[i]) for (j = i; j <= N/i; j++) IsPrime[i*j] = false;

Version 4: for (i = 2; i*i <= N; i++) if (isPrime[i]) for (j = i; j <= N/i; j++) IsPrime[i*j] = false;

Version 5: for (i = 2; i*i <= N; i++) if (isPrime[i]) for (p = i*i; p <= N; p += i) IsPrime[p] = false;

Måling af programeffektivitet

Version j/p -iterationer Tid (sek) 1 1,657,511,569 491.0 2 309,275,826 74.2 3 242,570,204 48.2 4 242,570,204 44.7 5 242,570,204 44.7

N = 100,000,000Mac G3, 233 MHz

Page 14: Fundamentale datastrukturer

14

Måling af tidsforbrug i Java

double startTime = System.currentTimeMillis();

Kode

System.out.println("Time: " + (System.currentTimeMillis() - startTime)/1000.0 + "seconds");

Page 15: Fundamentale datastrukturer

15

for (i = 2; i <= N/2; i++) for (j = 2; j <= N/i; j++) IsPrime[i*j] = false;

Kompleksiteten af Eratosthenes si

Hvor mange gange udføres sætningen i den indre løkke?

Da vi blot er interesseret i en øvre grænse (O-notation), udføres beregningerne i stedet på programstumpen:

for (i = 1; i <= N; i++) for (j = 1; j <= N/i; j++) IsPrime[i*j] = false;

Svar: N/1 + N/2 + N/3 + ... + N/N =N(1 + 1/2 + 1/3 + .... + 1/N) = NHN = N(lnN + 0.577) = O(N logN)

Se side 126 i lærebogen

Page 16: Fundamentale datastrukturer

16

for (i = 1; i <= N; i++) if (isPrime[i]) for (j = 1; j <= N/i; j++) IsPrime[i*j] = false;

Hvor mange gange udføres sætningen i den indre løkke?

For antallet af primtal mindre end eller lig med N, P(N), gælder, at

P(N)/N nærmer sig 1/ln N, når N vokser

Kompleksiteten af den optimerede udgave

Sandsynligheden for at påbegynde den indre løkke er 1/lnN.

Kompleksiteten er derfor O(N log N/ln N) = O(N).

Page 17: Fundamentale datastrukturer

17

Empirisk undersøgelse af den optimerede udgave

Page 18: Fundamentale datastrukturer

18

Realisering i Java:

Oprettelse:

int a[][] = new int[5][4];

opretter et array med 5*4 = 20 heltallige elementer:

a[0][0] a[0][1] a[0][2] a[0][3]

a[1][0] a[1][1] a[1][2] a[1][3]

a[2][0] a[2][1] a[2][2] a[2][3]

a[3][0] a[3][1] a[3][2] a[3][3]

a[4][0] a[4][1] a[4][2] a[4][3]

2-dimensionale arrays

Tilgang til et element: a[3][2]Aflæsning af antal rækker: a.length (= 5)Aflæsning af antal søjler: a[0].length (= 4)

Page 19: Fundamentale datastrukturer

19

En hægtet liste er en mængde at dataelementer, der er organiseret sekventielt, således at hvert element (kaldet en knude) indeholder en peger (kaldet en hægte) til det næste element.

Hægtede lister

Bemærk: enhver knude indeholder en hægte, også listens sidste.

A L I S T

A L I S T

first

Page 20: Fundamentale datastrukturer

20

Operationer på hægtede lister

Tidsforbrug: Konstant. Kun 2 hægter skal ændres (uafhængigt af listens længde)

Tidsforbrug: Konstant. Kun 1 hægte skal ændres (uafhængigt af listens længde)

(1) Indsættelse

(2) Sletning

X

A L I S T

first

A L I S T

first

X

Page 21: Fundamentale datastrukturer

21

Implementering af hægtede lister i Java

class ListNode {

Object data;

ListNode next; }

Page 22: Fundamentale datastrukturer

22

Gennemløb af hægtet liste

ListNode current = firstListNode;

while (current != null) {

process(current.data);

current = current.next;

}

for (ListNode current = firstListNode;

current != null; current = current.next)

process(current.data);

eller

Page 23: Fundamentale datastrukturer

23

class ListNode {

ListNode next;

Object data;

public void insertAfter(ListNode t) {

next = t.next;

t.next = this;

}

public void deleteAfter(ListNode t) {

t.next = next;

}

}

Implementering af indsættelse og sletning

t t.next

this

Page 24: Fundamentale datastrukturer

24

En hægtet liste kan også opfattes som en abstrakt datatype

public interface List {

boolean isEmpty();

void makeEmpty();

}

package DataStructures;import Exceptions.*;

Listens elementer tilgås ved hjælp af en iterator.

Page 25: Fundamentale datastrukturer

25

Ved at tilgå en datastrukturs elementer igennem en iterator opnås:

(1) dataabstraktion

(2) sikkerhed mod forkert brug

Den underliggende repræsentation afsløres ikke.

Iterator- et designmønster -

Iterator current position

Object

ListNode

Page 26: Fundamentale datastrukturer

26

En iterator for hægtede lister

public interface ListItr {

void insert(Object x) throws ItemNotFound;

void remove(Object x) throws ItemNotFound;

boolean find(Object x);

void zeroth();

void first();

void advance();

boolean isInList();

Object retrieve();

}

ListItr itr = new LinkedListItr(theList);for (itr.first(); itr.isInList(); itr.advance())

System.out.println(itr.retrieve());

Page 27: Fundamentale datastrukturer

27

Et simpelt testprogramimport DataStructures.*;

import Exceptions.*;

public class TestList {

public static void main(String[] args) {

List theList = new LinkedList();

ListItr itr = new LinkedListItr(theList);

for (int i = 0; i < 5; i++) {

try {

itr.insert(new Integer(i));

} catch (ItemNotFound e) {} // Cannot happen

itr.zeroth(); // Reset itr to the start

}

for (itr.first(); itr.isInList(); itr.advance())

System.out.print(" " + itr.retrieve());

System.out.println(" end");

}

}

Page 28: Fundamentale datastrukturer

28

Dobbelthægtede lister

Problemer med enkelthægtede lister:

• En knude kan ikke fjernes effektivt fra en liste, med mindre forgængerknuden er kendt.

• Listen kan kun gennemløbes i én retning.

Løses med en dobbelthægtet liste.

I det følgende præsenteres en Java-pakke til håndtering af tovejslister. Pakken svarer helt til Simulas indbyggede pakke SIMSET.

A L I S T

Page 29: Fundamentale datastrukturer

29

Klasserne Link og Head fra en klients synspunkt

• Head: Listehovedet

• Link: Et listeelement

first()

last()

suc()null null

suc() suc()

pred() pred() pred()

Head

LinkLink Link

Page 30: Fundamentale datastrukturer

30

public class Link { public Link pred(); public Link suc(); public void out(); public void into(Head h); public void follow(Link_eller_Head p); public void precede(Link_eller_Head p);}

Pakken simset

package simset;

public class Head {

public Link first();

public Link last();

public boolean empty();

public int cardinal();

public void clear();

}

Page 31: Fundamentale datastrukturer

31

Arrays og hægtede lister(pro et contra)

• Visse operationer er mere effektive for en liste end for et array, f.eks. indsættelse og sletning (flytning undgås). Men visse operationer er mere effektive for et array, f.eks. bestemmelse af det k´te element.

• En array-repræsentation fylder mindre (der spares plads til hægterne).

Page 32: Fundamentale datastrukturer

32

En stak(LIFO = LastInFirstOut)

En stak er en sekvens af dataelementer af samme type, som muliggør følgende to operationer:

push(x): Læg dataelementet x øverst på stakken

pop: Fjern det øverste element på stakken

Stak

poppush

Kun stakkens øverste element (top) er tilgængeligt

Page 33: Fundamentale datastrukturer

33

En stak er en abstrakt datatype

public interface Stack {

void push(Object x);

void pop() throws Underflow;

Object top() throws Underflow;

Object topAndPop() throws Underflow;

boolean isEmpty();

void makeEmpty();

}

package DataStructures;import Exceptions.*;

Page 34: Fundamentale datastrukturer

34

Et simpelt testprogram

import DataStructures.*;

import Exceptions.*;

public class TestStack {

public static void main(String[] args) {

Stack s = new StackAr();

for(int i = 0; i < 5; i++)

s.push(new Integer(i));

try {

for (;;)

System.out.print(" " + s.topAndPop());

} catch (Underflow e) {}

System.out.println();

}

}

Page 35: Fundamentale datastrukturer

35

Anvendelser af en stak

En stak kan bl.a. bruges til

• at vende om på en given rækkefølge (at gøre noget baglæns)

• at gemme mellemresultater f.eks. ved beregning af udtrykket 3*4 + 5*6 +7

• at behandle parentetiske strukturer f.eks. ved kontrol af, om parenteserne stemmer i sekvensen

{ [ ( { } ( ) ] ) }

Page 36: Fundamentale datastrukturer

36

En kø(FIFO = FirstInFirstOut)

En kø er en sekvens af dataelementer af samme type, som muliggør følgende to operationer:

enqueue(x): Sæt dataelementet x bagest i køen

dequeue: Fjern det forreste element fra køen

dequeue enqueue

Page 37: Fundamentale datastrukturer

37

En kø er en abstrakt datatype

public interface Queue {

void enqueue(Object x);

Object dequeue() throws Underflow;

Object getFront() throws Underflow;

boolean isEmpty();

void makeEmpty(); }

package DataStructures;import Exceptions.*;

Page 38: Fundamentale datastrukturer

38

Et simpelt testprogram

import DataStructures.*;

import Exceptions.*;

public class TestQueue {

public static void main(String[] args) {

Queue q = new QueueAr();

for(int i = 0; i < 5; i++)

q.enqueue(new Integer(i));

try {

for (;;)

System.out.print(" " + q.dequeue());

} catch(Underflow e) {}

System.out.println();

}

}

Page 39: Fundamentale datastrukturer

39

Java-klassen Vector(en klasse til håndtering af dynamiske tabeller)

class Vector { Object elementAt(int index); void setElementAt(Object obj, int index);

void insertElementAt(Object obj, int index); void removeElementAt(int index); void addElement(Object obj); void removeElement(Object obj);

boolean contains(Object obj); boolean isEmpty(); int size(); int indexOf(Object obj);

Object firstElement(); Object lastElement();}

Page 40: Fundamentale datastrukturer

40

Java-klassen Stack(implementeret ved hjælp af class Vector)

class Stack extends Vector {

Object push(Object obj) {

addElement(obj);

return obj;

}

Object pop() {

Object obj = peek();

removeElementAt(size() - 1);

return obj;

}

Object peek() {

int len = size();

if (len == 0) throw new EmptyStackException();

return elementAt(len - 1);

}

boolean empty() { return size() == 0; }}

Page 41: Fundamentale datastrukturer

41

Et træ er en samling af knuder og kanter, (V, E), som opfylder visse krav:

En knude, v, er et simpelt dataobjekt, der kan have et navn og en tilknyttet information. En af knuderne er udpeget som rod i træet.

En kant, (v1,v2), er en forbindelse imellem to knuder, v1 og v2.

En vej er en liste af knuder, (v1,v2, ... ,vk), hvor alle successive knuder, vi og vi+1, er indbyrdes forbundne (dvs. tilhører E).

For at udgøre et træ skal der mellem roden og enhver anden knude findes præcis én vej.

Træer

Page 42: Fundamentale datastrukturer

42

Terminologi

Rod: RX er far til Y Y er søn til X (Y er barn af X)U, V og W er børn af TS er bedstefar til ZS er forgænger til Y (S er over Y)Y er efterkommer af S (Y er under S)Blade: Y, Z, U, V, W Indre knuder : R, S, X, T

RodR

S

X

Y Z

T

U V W

Blad

Indre knude

Niveau 0

Niveau 1

Niveau 2

Niveau 3

Page 43: Fundamentale datastrukturer

43

Eksempel på anvendelse af træer(et filsystem)

Et filsystem kan beskrives ved et træ:

• Knuderne repræsenterer kataloger og filer.

• Træets blade indeholder filer (eller tomme kataloger).

Page 44: Fundamentale datastrukturer

44

Terminologi(fortsat)

En knudes niveau er antallet af knuder på vejen fra knuden til roden (minus knuden selv).

Et træs højde er det maksimale niveau for alle knuder i træet.

Page 45: Fundamentale datastrukturer

45

Terminologi(fortsat)

Enhver knude i et træ er rod for et undertræ bestående af knuden selv og alle knuder under den.

Et træ kaldes ordnet, hvis rækkefølgen af sønnerne for enhver knude er specificeret.

En mængde af træer kaldes en skov.

Page 46: Fundamentale datastrukturer

46

• Et træ er sammenhængende, dvs. der er en vej fra enhver knude til enhver anden knude.

• Et træ har ingen cykler, dvs. enhver vej indeholder en knude højst én gang.

Nyttig definition: Et tomt træ er et træ uden kanter og knuder.

Egenskaber ved træer

Page 47: Fundamentale datastrukturer

47

Binære træer

Rekursiv definition: Et binært træ er enten et tomt træ, eller en knude, som har et venstre og et højre binært undertræ.

Et binært træ er et ordnet træ, hvor hver knude har højst 2 sønner.

Page 48: Fundamentale datastrukturer

48

Eksempel på et binært træ

P’s venstre søn er MP’s højre søn er L

P

M

S

A A

L

E

R

E

E

T

Page 49: Fundamentale datastrukturer

49

class BinaryNode { Object data;

BinaryNode left, right; }

Repræsentation af binære træer

En null-reference angiver et tomt (under)træ

Page 50: Fundamentale datastrukturer

50

*

A +

F*

*

ED

+

B C

Træ for udtrykket A * ( ( ( B + C ) * ( D * E ) ) + F)

Eksempel på anvendelse af binære træer(et udtrykstræ)

operator (i indre knude)

operand (i blad)

Page 51: Fundamentale datastrukturer

51

Repræsentation af generelle træer (1)

(1) Far-referencer

class Node { Node dad; }

Page 52: Fundamentale datastrukturer

52

Repræsentation af generelle træer (2)

(2) Binært træ

class Node { Node firstSon, brother; }

firstS

on

brother brother

Page 53: Fundamentale datastrukturer

53

Binære søgetræer(muliggør søgning i logaritmisk tid)

public interface SearchTree { void insert(Comparable x) throws DuplicateItem; void remove(Comparable x) throws ItemNotFound; void removeMin() throws ItemNotFound; Comparable findMin() throws ItemNotFound; Comparable findMax() throws ItemNotFound; Comparable find(Comparable x) throws ItemNotFound; boolean isEmpty(); void makeEmpty(); void printTree(); }

Page 54: Fundamentale datastrukturer

54

class MyString

public final class MyString implements Comparable, Hashable { private String value;

public MyString(String x) { value = x; }

public String toString() { return value; }

public int compares(Comparable rhs) { return value.compareTo(((MyString) rhs).value); }

public boolean lessThan(Comparable rhs) { return compares(rhs) < 0; }

public boolean equals(Object rhs) { return value.equals(((MyString) rhs).value); }

public int hash(int tableSize) { return DataStructures.QuadraticProbingTable. hash(value, tableSize); } }

Page 55: Fundamentale datastrukturer

55

Et simpelt testprogram

public class TestSearchTree {

public static void main(String[] args) {

SearchTree t = new BinarySearchTree();

try {

t.insert(new MyString("Becky")); }

catch(DuplicateItem e) {} // Cannot happen

MyString result = null;

try {

result = (MyString) t.find(new MyString("Becky"));

System.out.print("Found " + result + ";");

} catch(ItemNotFound e)

{ System.out.print("Becky not found;");}

try {

result = (MyString) t.find(new MyString("Mark"));

System.out.print(" Found " + result + "; ");

} catch(ItemNotFound e)

{ System.out.print(" Mark not found;" ); }

}

}

Page 56: Fundamentale datastrukturer

56

Hashtabeller(muliggør søgning i konstant tid)

public interface HashTable { void insert(Hashable x); void remove(Hashable x) throws ItemNotFound; Hashable find(Hashable x) throws ItemNotFound; boolean isEmpty(); void makeEmpty();}

public interface Hashable { int hash(int tableSize);}

Page 57: Fundamentale datastrukturer

57

Et simpelt testprogram

public final class TestHashTable {

public static void main(String[] args) {

HashTable h = new QuadraticProbingTable();

h.insert(new MyString("Becky"));

MyString result = null;

try {

result = (MyString) h.find(new MyString("Becky"));

System.out.println("Found " + result);

}

catch(ItemNotFound e)

{ System.out.println("Becky not found"); }

}

}

Page 58: Fundamentale datastrukturer

58

Prioritetskøer(muliggør behandling af elementer i prioriteret rækkefølge)

public interface PriorityQueue { void insert(Comparable x); void deleteMin() throws Underflow; Comparable findMin() throws Underflow; boolean isEmpty(); void makeEmpty();}

Page 59: Fundamentale datastrukturer

59

Et simpelt testprogram

public final class TestPriorityQueue {

public static void main(String[] args) {

PriorityQueue pq = new PairHeap();

pq.insert(new MyInteger(4));

pq.insert(new MyInteger(2));

pq.insert(new MyInteger(1));

pq.insert(new MyInteger(3));

pq.insert(new MyInteger(0));

try {

for (;;)

System.out.print(" " + pq.deleteMin());

} catch(Underflow e) {}

}

}

Page 60: Fundamentale datastrukturer

60

Sammenfatning af datastrukturerne

Datastruktur TilgangStak Kun det seneste element, pop: O(1)Kø Kun det tidligste element, dequeue: O(1)

Hægtet liste Ethvert elementSøgetræ Ethvert element, O(logN)Hashtabel Ethvert element, O(1)Prioritetskø Kun elementet med laveste prioritet,

findMin: O(1), deleteMin: O(log N)

Page 61: Fundamentale datastrukturer

61

Ugeseddel 218. september - 25. september

• Læs kapitel 7 i lærebogen (side 173-221)

• Løs følgende opgaver

2-1. Opgave 6.5.

2-2. Opgave 6.7

2-3. Opgave 6.11

2-4. Opgave 6.12

2-5. Opgave 6.13

2-6. Opgave 6.14