Objektorienterad programmering E Föreläsning 10 Rekursion Länkade datastrukturer Back to Basics Exekvera programmet för hand! public class Param { public static int f(int x) { return x+1; public static void p(int x) { System.out.println("in p"); q(2*x); public static void q(int x) { System.out.println("in q: " + x); public static void main(string[] args) { p(f(3)); Ett enkelt exempel En annan version av printtable Skriv programmet Table > java Table 10 n n^2 n^3 1 1 1 2 4 8 3 9 27 4 16 64 5 25 125 6 36 216 7 49 343 8 64 512 9 81 729 10 100 1000 public static void printtable(int rows) { if (rows==0) System.out.printf("%4s %8s %8s\n","n","n^2","n^3"); else { printtable(rows-1); System.out.printf("%4d %8d %8d\n",rows, rows*rows, rows*rows*rows); Rekursion Definitionen ovan är rekursiv; vi anropar subrutinen själv i dess definition. Hur kan det fungera?
Rekursiva definitioner Tornen i Hanoi Nödvändig restriktion I en rekursiv definition måste det finnas ett basfall; något värde på argumentet där man ger resultatet utan rekursion. varje rekursivt anrop göras med ett argument som är mindre, dvs närmare basfallet. Rekursionsantagande När man gör en definition som ovan kan man i det rekursiva fallet anta att det rekursiva anropet gör vad det ska göra. Jämför med induktionsbevis i matematiken! Ett berömt exempel Uppgift: flytta alla brickor från pinne 1 till pinne 2. Pinne 3 får användas som hjälp. En bricka i taget flyttas och en större bricka får aldrig ligga ovanpå en mindre. Att lösa tornen i Hanoi??? Det är inte alls lätt att beskriva vilken som ska vara nästa flyttning vid ett givet tillstånd mitt i spelet. Ett lättare problem(?) Skriv ett program som skriver ut lösningen (ingen grafik): > java HanoiText 3 Flytta från 1 till 3 Flytta från 2 till 3 Flytta från 3 till 1 Flytta från 3 till 2 > Lösningen: rekursion public class HanoiText { public static void move(int n,int from,int to,int aux) { //Flytta n brickor från pinne from till pinne to // med pinne aux som hjälp.??? public static void main(string[] args) { move (Integer.parseInt(args[0]),1,2,3);
Tornen i Hanoi med animering Hela lösningen räknas ut redan i modellklassens konstruerare och lagras (som en lista av Move-objekt): private void computemoves(int size,int from,int to,int aux){ if (size==0) return; computemoves(size-1,from,aux,to); moves.addlast(new Move(from,to)); computemoves(size-1,aux,to,from); Vid varje Step tas ett element ur listan och denna flyttning görs i modellen. Se kod på kursens webbplats. Insättningssortering, återbesök public static void isort(int[] a, int from, int to) { for (int i=from+1; i < to; i++) { int tmp = a[i]; int j = i-1; while (j >=from && tmp < a[j]) { a[j+1] = a[j]; j--; a[j+1] = tmp; public static void isort(int[] a) { isort(a,0,a.length); Hur kan man sortera ett fält effektivare? Mergesort, idé Om fältet är kort (t ex < 10 element), använd insättningssortering. Annars, sortera första och andra halvan var för sig (rekursivt, med mergesort). Det återstår sedan att sortera ihop de två sorterade halvorna. Detta går fort, men kräver ett extra fält. Förberedelser (skapa och fyll ett hjälpfält) public static void msort(int[] a) { int[] aux = new int[a.length]; for (int i=0; i<a.length; i++) aux[i] = a[i]; msort(aux, a, 0,a.length); Mergesort, huvudalgoritmen I nedanstående rutin innehåller de två fälten src och dest samma tal vid anropet. Det sorterade resultatet ska finnas i dest efteråt. Hjälprutinen merge (nästa slide) samsorterar halvorna från src till dest. public static void msort(int[] src, int[] dest, int from, int to) { if (to-from<8) isort(dest,from,to); else { int mid = (from + to)/2; msort(dest,src,from,mid); msort(dest,src,mid,to); merge(src,dest,from,mid,to);
Till sist: merge Köer public static void merge(int[] src, int[] dest, int from, int mid, int to) { int p = from; int q = mid; for (int i=from; i < to; i++) if (q >= to p < mid && src[p] <= src[q]) { dest[i] = src[p]; p++; else { dest[i] = src[q]; q++; Vanligt i tillämpningar Många program behöver lagra objekt i listor som fungerar som köer: Man kan bara sätta in objekt sist i kön och bara ta ut det element som står först i kön. Man kan använda en klass som LinkedList för detta, men bättre är att ha ett mer begränsat interface: public interface Queue<E> { public void enqueue(e elem); public E dequeue(); public boolean isempty(); En möjlig implementation av Queue Noder import java.util.*; public class SimpleQueue<E> implements Queue<E> { private LinkedList<E> list; public SimpleQueue() {list = new LinkedList<E>(); public void enqueue(e elem) {list.addlast(elem); public E dequeue() {return list.remove(0); public boolean isempty() {return list.isempty(); Delegation På föregående bild implementerade vi Queue genom att ha en tillståndsvariabel av typ LinkedList och delegera allt arbete till denna. Detta är en utmärkt implementation, men vi gör ändå en egen, som samtidigt ger en aning om hur LinkedList är gjord. Hjälpklassen Node public class Node<E> { E elem; Node<E> next;
Länkade strukturer Köer som länkade strukturer Flera objekt av typen Node kan länkas ihop, illustrerat för Node<Integer>: Objekt av typen Integer Vi kan representera en kö som en sådan länkad struktur. Vi sätter in element i slutet på listan; det är då praktiskt att ha en referens också till den sista noden: 5 4 8 6 value Objekt av typen Integer 5 4 8 6 value Node<Integer> list elem null next Node<Integer> first elem Objekt av typen Node<Integer> Node<Integer> last null next Objekt av typen Node<Integer> Klassen LinkedQueue public class LinkedQueue<E> implements Queue<E> { private static class Node<E> { E elem; Node<E> next; private Node<E> first; private Node<E> last; public LinkedQueue() { Metoderna definieras på föreläsningen