TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK Lehrstuhl für Sprachen und Beschreibungsstrukturen Einführung in die Informatik I Prof. Dr. Helmut Seidl, A. Lehmann, A. Herz, Dr. M. Petter SS 2011 Übungsblatt 7 16.06.11 Abgabe: 26.06.11 (vor 12 Uhr) Aufgabe 7.1 (P) Rationale Zahlen Implementieren Sie die Klasse Rational zur Repräsentation rationaler Zahlen aus der Vorlesung. Definieren Sie neben einem geeigneten Konstruktor für die Klasse auch ObjektMethoden für folgende Operationen: Anmerkung: Die Bruchzahl soll stets in gekürzter Form gespeichert werden. a) Addition: Rational add(Rational r) b) Subtraktion: Rational sub(Rational r) c) Multiplikation: Rational mul(Rational r) d) Division: Rational div(Rational r) e) Dezimalwert: double decimal() Der Aufruf r.decimal() soll den dezimalen Wert von r berechnen und zurückliefern. f) Vergleich: int compareTo(Rational r) Der Aufruf r1.compareTo(r2) soll −1 zurückliefern falls x1 < x2 , 0 falls x1 = x2 und schließlich 1 falls x1 > x2 . Dabei sollen x1 und x2 die Werte sein, die durch die Objekte r1 und r2 repräsentiert werden. g) Duplizieren: Rational clone() Der Aufruf r.clone() soll eine Kopie von r erstellen und zurückliefern. Lösungsvorschlag 7.1 public c l a s s R a t i o n a l { // D e k l a r a t i o n d e r Komponenten e i n e r r a t i o n a l e n Z a h l private int z a e h l e r , nenner ; /∗ Bestimmung d e s g r o e s s t e n gemeinsamen T e i l e r s von n und m mit dem e u k l i d s c h e n A l g o r i t h m u s ∗/ private s t a t i c int ggT ( int n , int m) { i f ( ( n == 0 ) | | (m == 0 ) ) { // Abbruch b e i Eingabe ( m i n d e s t e n s ) e i n e r 0 => t e i l e r f r e i return 0 ; } else { while ( n != m) { // annaehern d e s ggT durch s u k z e s s i v e s a b z i e h e n i f ( n > m) { n = n−m; } else { m = m−n ; } } } // r u e c k g a b e d e s ggT durch n return n ; } 2 // E r s t e l l e n e i n e r r a t i o n a l e n z a h l aus z w e i ganzen Zahlen public R a t i o n a l ( int z , int n ) { i f ( n == 0 ) // Eingabe u e b e r p r u e f u n g throw new I l l e g a l A r g u m e n t E x c e p t i o n ( ” D i v i s i o n by z e r o ”) ; // z u w e i s e n d e r Parameter zaehler = z ; nenner = n ; // E n t f e r n e n u n n o e t i g e r V o r z e i c h e n und p o s i t i v e n z a e h l e r s i c h e r s t e l l e n int s i g n = I n t e g e r . signum ( nenner ) ; zaehler = sign ∗ zaehler ; nenner = s i g n ∗ nenner ; k u e r z e n ( ) ; // s i c h e r s t e l l e n , d a s s d i e Z a h l g e k u e r z t gespeichert ist } // E r s t e l l e n e i n e r r a t i o n a l e n z a h l aus e i n e r ganzen Zahlen public R a t i o n a l ( int z ) { t h i s ( z , 1 ) ; // A u f r u f d e s o b i g e n K o n s t r u k t o r s mit Zaehler 1 } // a d d i e r n von r z u r a k t u e l l e n Z a h l public R a t i o n a l add ( R a t i o n a l r ) { return new R a t i o n a l ( // r u e c k g a b e d e s E r g e b n i s s e s a l s neue r a t i o n a l e z a h l z a e h l e r ∗ r . nenner + r . z a e h l e r ∗ nenner , nenner ∗ r . nenner ) ; // e r w e i t e r n und a d d i e r e n } // k u e r z e n f i n d e t im k o n s t r u c t o r s t a t t // m u l t i p l i z i e r e n von r mit d e r a k t u e l l e n Z a h l public R a t i o n a l mul ( R a t i o n a l r ) { return new R a t i o n a l ( z a e h l e r ∗ r . z a e h l e r , nenner ∗ r . nenner ) ; } // s u b t r a h i e r e n von r von d e r a k t u e l l e n Z a h l durch A d d i t i o n von −r public R a t i o n a l sub ( R a t i o n a l r ) { return add ( r . mul (new R a t i o n a l ( −1) ) ) ; } // H i l f s f u n k t i o n f u e r D i v i s i o n . bestimmt Kehrwert d e r a k t u e l l e n Z a h l public R a t i o n a l r e c i p r o c a l ( ) { return new R a t i o n a l ( nenner , z a e h l e r ) ; } // D i v i s i o n d e r a k t u e l l e n Z a h l durch r durch Mult . mit Kehrwert von r public R a t i o n a l d i v ( R a t i o n a l r ) { return mul ( r . r e c i p r o c a l ( ) ) ; } // Umwandlung von z a e h l e r und nenner i n e i n e Gleitkommazahl public double d e c i m a l ( ) { return ( double ) z a e h l e r / ( double ) nenner ; } public int compareTo ( R a t i o n a l r ) { // V o r a u s s e t z u n g : Nenner s i n d p o s i t i v 3 int z1 = z a e h l e r ∗ r . nenner ; // i m p l i z i t e s e r w e i t e r n d e r aktuellen zahl int z2 = r . z a e h l e r ∗ nenner ; // i m p l i z i t e s e r w e i t e r n von r i f ( z1 < z2 ) { // v e r g l e i c h d e r e r w e i t e r t e n Z a e h l e r return −1; } e l s e i f ( z1 > z2 ) { return 1 ; } return 0 ; // weder k l e i n e r noch g r o e s s e r } // G l e i c h h e i t s t e s t m i t t e l s compareTo public boolean e q u a l s ( R a t i o n a l r ) { return compareTo ( r ) == 0 ; } // e r z e u g e n e i n e r neuen r a t i o n a l e n z a h l mit dem z a e h l e r und nenner d e r a k t u e l l e n z a h l public R a t i o n a l c l o n e ( ) { return new R a t i o n a l ( z a e h l e r , nenner ) ; } // k u e r z e n d e r a k t u e l l e n Z a h l durch D i v i s i o n durch den ggT von Z a e h l e r und Nenner public void k u e r z e n ( ) { // ggT a u f r u f mit dem B e t r a g d e s z a e h l e r s und dem ( ohnehin p o s i t i v e n ) Nenner int ggT = ggT ( I n t e g e r . signum ( z a e h l e r ) ∗ z a e h l e r , nenner ) ; i f ( ggT > 1 ) { // k u e r z e n f a l l s n i c h t t r i v i a l e r ggT vorhanden z a e h l e r = z a e h l e r / ggT ; nenner = nenner / ggT ; } } // Umwandlung d e r Z a h l i n e i n e n public S t r i n g t o S t r i n g ( ) i f ( nenner == 1 ) return z a e h l e r + } ( formatierte ) Stringdarstellung { return ” ” + z a e h l e r ; ”/ ” + nenner ; } Aufgabe 7.2 (P) Einfach verkettete Liste Einfach verkettete Integer-Listen bestehen aus einer Kette von Elementen. Jedes Element besteht aus einer Zahl (info) und der Referenz (next) auf den Rest der Liste. Die leere Liste wird durch null repräsentiert. a) Erstellen Sie eine Klasse IntList. Implementieren Sie einen passenden Konstruktor, der die Attribute info und next initialisiert. b) Implementieren Sie eine Methode append(int info), die eine neue Liste zurückgibt, die eine Kopie der aktuellen Liste ist, an deren Ende ein neues Element mit Zahl info, angefügt wurde. c) Implementieren Sie eine Methode public String toString(), die alle Elemente der Liste als String zurückgibt. d) Implementieren Sie eine Methode sum(), die die Summe aller Listenelemente zurück- 4 gibt. e) Implementieren Sie eine Methode reverse(), die eine neue Liste zurückgibt, welche die Zahlen der aktuellen Liste in umgekehrter Reihenfolge enthält. Lösungsvorschlag 7.2 public c l a s s I n t L i s t { private int info ; private I n t L i s t next ; // t h e i n t d a t a o f t h i s l i s t e l e m e n t // t h e r e s t o f t h e l i s t /∗ ∗ ∗ S e t s up a new i n s t a n c e o f I n t L i s t c o r r e s p o n d i n g t o t h e g i v e n i n f o and ∗ next . ∗ @param i n f o t h e i n t d a t a o f t h i s l i s t e l e m e n t ∗ @param n e x t t h e r e s t o f t h e l i s t ∗/ public I n t L i s t ( int i n f o , I n t L i s t next ) { this . i n f o = i n f o ; t h i s . next = next ; } /∗ ∗ ∗ A new l i s t where t h e g i v e n i n f o has been appended . ∗ @param i n f o t h e i n t d a t a o f t h e new l i s t e l e m e n t ∗ @return a new i n s t a n c e o f I n t L i s t ∗/ public I n t L i s t append ( int i n f o ) { i f ( next == null ) return new I n t L i s t ( t h i s . i n f o , new I n t L i s t ( i n f o , null ) ) ; else return new I n t L i s t ( t h i s . i n f o , next . append ( i n f o ) ) ; } /∗ ∗ ∗ Computes t h e sum o f a l l e l e m e n t s o f t h i s l i s t . ∗ @return t h e sum o f a l l e l e m e n t s ∗/ public int sum ( ) { i f ( next == null ) return i n f o ; else return i n f o + next . sum ( ) ; } /∗ ∗ ∗ Auxiliary function for the reversal of t h i s l i s t . ∗ @param acc t h e l i s t e l e m e n t s a c c u m u l a t e d so f a r ∗ @return a new i n s t a n c e o f I n t L i s t ∗/ private I n t L i s t r e v e r s e A u x ( I n t L i s t a c c ) { i f ( next == null ) return new I n t L i s t ( i n f o , a c c ) ; else 5 return next . r e v e r s e A u x (new I n t L i s t ( i n f o , a c c ) ) ; } /∗ ∗ ∗ A new l i s t w i t h t h e e l e m e n t s o f t h i s l i s t i n r e v e r s e o r d e r . ∗ @return a new i n s t a n c e o f I n t L i s t ∗/ public I n t L i s t r e v e r s e ( ) { return r e v e r s e A u x ( null ) ; } /∗ ∗ ∗ String representation of t h i s l i s t . ∗/ @Override public S t r i n g t o S t r i n g ( ) { i f ( next == null ) return ” ” + i n f o ; else return i n f o + ” , ” + next ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { I n t L i s t l s t = new I n t L i s t ( 1 , null ) ; fo r ( int i = 2 ; i < 1 0 ; i ++) l s t = l s t . append ( i ) ; System . out . p r i n t l n ( l s t ) ; System . out . p r i n t l n ( l s t . r e v e r s e ( ) ) ; System . out . p r i n t l n ( l s t . sum ( ) ) ; } } 6 Aufgabe 7.3 (P) Binärer Suchbaum Ein Binärbaum ist ein Baum, in dem alle Knoten einen Wert und 2 Teilbäume haben. Bei einem binären Suchbaum (BSB) gilt für jeden Knoten des Baumes, dass sein Wert: • grösser ist als die Werte aller Knoten in seinem linken Teilbaum; • kleiner ist als die Werte aller Knoten in seinem rechten Teilbaum; • maximal einmal im ganzen BSB vorkommt. Beispiel: In der obigen Zeichnung finden Sie einen BSB für die Zahlen 1, 3, 6, 7, 8, 10, 13, 14. Entwerfen und realisieren Sie die Datenstrukturen und Methoden für binäre Suchbäume in Java! a) Erstellen Sie eine Klasse BSB, mit der Sie binäre Suchbäume darstellen können. Ergänzen Sie die Klasse BSB um folgende Methoden: b) boolean contains(int n), die zurückgibt, ob die Zahl n im BSB enthalten ist. c) void insert(int n), die – unter Erhalt der obigen Eigenschaften – einen neuen Knoten mit dem Wert n in den BSB einfügt, falls dieser Wert im BSB nicht schon vorhanden ist. Beispiel: Die folgende Zeichnung illustriert einen Aufruf von insert(4): insert(4) −→ d) String toString(), die eine String-Darstellung der Baumstruktur zurückgibt. Beispiel: toString() liefert für den obigen BSB den String: [8 [3 [1] [6 [7]]] [10 [14 [13]]]] Lösungsvorschlag 7.3 public c l a s s BSB { private BSB l e f t ; private BSB r i g h t ; private int v a l u e ; public BSB( int v ) { value = v ; } /∗ p u b l i c boolean contains ( in t n) { // . . . Aufgabe a ) } p u b l i c void i n s e r t ( in t n) { 7 // . . . Aufgabe b ) } } ∗/ /∗ ∗ ∗ checks i f element n i s in t r e e ∗/ public boolean c o n t a i n s ( int n ) { i f ( n == v a l u e ) { return true ; } e l s e i f ( n > v a l u e && r i g h t != null ) { return r i g h t . c o n t a i n s ( n ) ; } e l s e i f ( n < v a l u e && l e f t != null ) { return l e f t . c o n t a i n s ( n ) ; } return f a l s e ; } /∗ ∗ ∗ I n s e r t s a new node w i t h v a l u e n i n t o t h e BSB ∗/ public void i n s e r t ( int n ) { i f ( ! contains (n) ){ i f (n > value ) { i f ( r i g h t == null ) { r i g h t = new BSB( n ) ; } else { right . insert (n) ; } } else { i f ( l e f t == null ) { l e f t = new BSB( n ) ; } else { l e f t . insert (n) ; } } } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { BSB b = new BSB( 5 ) ; b . insert (7) ; b . insert (2) ; b . insert (8) ; System . out . p r i n t l n ( b . c o n t a i n s ( 8 ) ) ; System . out . p r i n t l n ( b ) ; } @Override public S t r i n g t o S t r i n g ( ) { return ”( ” + l e f t + ”) ” + v a l u e+ ” ( ” + r i g h t+ ”) ” ; } } 8 Aufgabe 7.4 [4 Punkte] (H) Binärer Suchbaum - Fortsetzung Laden sie sich die Lösung zu Aufgabe 7.3 von der Homepage und erweitern Sie die Datenstrukturen und Methoden für binäre Suchbäume um a) eine Objektmethode depth, die die Tiefe eines Suchbaumes berechnet und zurückgibt. Hinweis: Die Tiefe eines Suchbaums ist die Anzahl der Kanten entlang des längsten Pfades, der in Richtung der Pfeile gegangen werden kann . b) eine Objektmethode size, die die Anzahl der Knoten in diesem Suchbaum berechnet und zurückgibt. c) eine Objektmethode getSortedArray, die ein Array zurückgibt, dass die Zahlen aus den Knoten dieses Baums in aufsteigender Reihenfolge enthält. Lösungsvorschlag 7.4 public int depth ( ) { int depth = 1 ; i f ( r i g h t != null ) depth+=r i g h t . depth ( ) ; i f ( l e f t==null ) return depth ; return Math . max( depth , l e f t . depth ( ) +1) ; } public int s i z e ( ) { int s i z e = 1 ; i f ( r i g h t != null ) s i z e+=r i g h t . s i z e ( ) ; i f ( l e f t==null ) return s i z e ; return s i z e+ l e f t . s i z e ( ) ; } public int [ ] g e t S o r t e d A r r a y ( ) { int [ ] l=new int [ 0 ] , r=new int [ 0 ] ; i f ( l e f t != null ) l= l e f t . g e t S o r t e d A r r a y ( ) ; i f ( r i g h t != null ) r = r i g h t . g e t S o r t e d A r r a y ( ) ; int [ ] r e t = new int [1+ l . l e n g t h+r . l e n g t h ] ; for ( int i =0; i <l . l e n g t h ; i ++){ r e t [ i ]= l [ i ] ; } r e t [ l . l e n g t h ]= v a l u e ; for ( int i =0; i <r . l e n g t h ; i ++){ r e t [ i+l . l e n g t h +1]= r [ i ] ; } return r e t ; } 9 Aufgabe 7.5 [6 Punkte] (H) Einfache Listenfunktionen Laden sie sich die Lösung zu Aufgabe 7.2 von der Homepage und erweitern Sie die Datenstrukturen und Methoden für einfach verkettete Integer-Listen. Implementieren Sie a) eine statische Methode IntList fromArray(int [] values), die eine neue Liste mit den im Array enthaltenen Werten erstellt. b) eine Methode int length(), die die Länge der Liste zurückgibt. c) eine Methode IntList get(int i), die eine Referenz auf das i-te Element der aktuellen Liste zurückgibt (Indizierung beginnt bei 0). d) eine Methode IntList copyRange(int start,int end), die eine neue Liste zurückgibt, die die Elemente zwischen start und end (inklusive) enthält. e) eine Methode IntList concat(IntList list), die eine neue Liste zurückgibt, in der die Elemente von list an die Elemente der aktuellen Liste angehängt wurden. f) eine Methode IntList remove(int i), die eine neue Liste zurückgibt, die bis auf das i-te Element alle Elemente der aktuellen Liste enthält. g) eine Methode IntList map(IntList list), die eine neue Liste zurückgibt, deren ites Element die paarweise Summe der i-ten Elemente der aktuellen Liste und der Liste list ist. Lösungsvorschlag 7.5 public c l a s s I n t L i s t { private int info ; private I n t L i s t next ; // t h e i n t i n f o o f t h i s l i s t e l e m e n t // t h e r e s t o f t h e l i s t public I n t L i s t ( int i n f o , I n t L i s t next ) { this . i n f o = i n f o ; t h i s . next = next ; } @Override public S t r i n g t o S t r i n g ( ) { i f ( next == null ) { return ” ” + i n f o ; } else { return i n f o + ” , ” + next ; } } public s t a t i c I n t L i s t fromArray ( int [ ] v a l u e s ) { i f ( v a l u e s . l e n g t h == 0 ) { return null ; } I n t L i s t r e s = new I n t L i s t ( v a l u e s [ 0 ] , null ) ; I n t L i s t cur = r e s ; fo r ( int i = 1 ; i < v a l u e s . l e n g t h ; i ++) { c u r . next = new I n t L i s t ( v a l u e s [ i ] , null ) ; c u r = c u r . next ; } return r e s ; } 10 public int l e n g t h ( ) { i f ( next == null ) { return 1 ; } else { return 1 + next . l e n g t h ( ) ; } } public I n t L i s t g e t ( int pos ) { i f ( pos == 0 ) { return t h i s ; } e l s e i f ( next != null ) { return next . g e t ( pos −1) ; } return null ; } public I n t L i s t copyRange ( int posa , int posb ) { I n t L i s t s t a r t = g e t ( posa ) ; I n t L i s t t o = new I n t L i s t ( s t a r t . i n f o , null ) ; I n t L i s t r e s = to ; I n t L i s t from = s t a r t . next ; while ( from != null && posb >= posa ) { t o . next = new I n t L i s t ( from . i n f o , null ) ; t o = t o . next ; from = from . next ; posa++; } return r e s ; } public I n t L i s t copy ( ) { return copyRange ( 0 , l e n g t h ( ) − 1 ) ; } public I n t L i s t l a s t ( ) { i f ( next == null ) { return t h i s ; } else { return next . l a s t ( ) ; } } public I n t L i s t c o n c a t ( I n t L i s t l ) { i f ( next == null ) { return new I n t L i s t ( i n f o , l ) ; } else { return new I n t L i s t ( i n f o , next . c o n c a t ( l ) ) ; } } public I n t L i s t remove ( int i ) { 11 I n t L i s t cp = copy ( ) ; i f ( i == 0 ) { return cp . next ; } else { I n t L i s t prev = cp . g e t ( i − 1 ) ; i f ( prev == null | | prev . next == null ) { return null ; } else { prev . next = prev . next . next ; } return cp ; } } public I n t L i s t map( I n t L i s t b ) { f i n a l int sum = i n f o + b . i n f o ; i f ( next == null ) { return new I n t L i s t ( sum , null ) ; } else { return new I n t L i s t ( sum , next . map( b . next ) ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { int [ ] v a l s 1 = { 1 , 4 , 3 , 5 , 6 , 8 , 9 , 3 , 4 5 } ; I n t L i s t nums1 = I n t L i s t . fromArray ( v a l s 1 ) ; int [ ] v a l s 2 = { 1 0 , 4 0 , 3 0 , 5 0 , 6 0 , 8 0 , 9 0 , 3 0 , 4 5 0 } ; I n t L i s t nums2 = I n t L i s t . fromArray ( v a l s 2 ) ; System . out . p r i n t l n ( nums1 ) ; System . out . p r i n t l n ( nums2 ) ; I n t L i s t cp = nums1 . copy ( ) ; System . out . p r i n t l n ( cp ) ; I n t L i s t nums3 = nums1 . c o n c a t ( nums2 ) ; nums3 . g e t ( 5 ) . i n f o = −5; i f ( nums1 . g e t ( 5 ) . i n f o == −5) { System . out . p r i n t l n ( ” e r r o r , r e f e r e n c e i n s t e a d o f copy g e n e r a t e d ”) ; } System . out . p r i n t l n ( nums3 ) ; I n t L i s t nums4 = nums3 . remove ( 1 2 ) ; System . out . p r i n t l n ( nums4 ) ; I n t L i s t nums5 = nums3 . map( nums3 ) ; System . out . p r i n t l n ( nums5 ) ; } }