TDDC30 Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 7 Jonas Lindgren, Institutionen för Datavetenskap, LiU På denna föreläsning: • Bubblesort, Shakersort, Mergesort • Strömmar, Datum, Formatering 1 Hittills, och vidare! • Objektorientering och Java • Objekt, klasser, instanser • Arv, polymorfism • Dölj implementationen • Private/Public (inkapsling) • Abstrakta datatyper (ADT) • Datastruktur med algoritmer • Listor, köer och stackar • Gränssnitt vs implementation • Analys av algoritmer • Komplexitet: tids-, rums• Fallstudie: Sorteringsalgoritmer 100 50 2 0 0 10 20 Bubblesort • Jämför med närliggande element • Start från höga index void bubbleSort(int[] a) { for (int i = 0; i < a.length; ++i) { for (int j = a.length-1; j > i; --j){ n if (a[j] < a[j-1]) { byt plats på a[j] och a[j-1]; } } } } O(n2) • Asymptotisk (tids)komplexitet? Ja • Stabil? Låg ”overhead” n2 3 Shakersort • Tvåvägs bubblesort • • • • Små element bubblar upp Stora element sjunker ner Fram och tillbaka Jämför med närliggande element • Bubbla ner eller bubbla upp • Gör om för varje element • Tshakersort? • Stabil? O(n2) Ja O(n) O(n) 4 Kort om rekursion Ibland när man delar upp ett problem, uppstår samma problem igen! T.ex. N! = 1 * 2 * 3 * … kan även skrivas: N! = 1 då N = 0, N * (N-1)! annars I java kan en metod anropa sig själv! int fak(int N) { N : 2 if (N == 0) { return 1; } return N * fak(N-1) 2=2*1 } svar = fak(2); int fak(int N) {N : 1 if (N == 0) { return 1; } return N * fak(N-1) 1=1*1 } int fak(int N) { N : 0 if (N == 0) { 5 return 1; } return N * fak(N-1) } ”Divide and Conquer” (sv. söndra och härska) En rekursiv strategi: 1. Om datamängden är liten, lös direkt (härska). 2. Annars: dela upp datamängden (söndra) och applicera söndra och härska (rekursivt) på varje del. 3. Sammanfoga lösningarna för varje del till lösningen för hela. 6 Mergesort Strategi: ”Divide and conquer”: Tag två sorterade delmängder och sortera ihop dessa. Algoritm: Då mängden innehåller 0 eller 1 element: • Redan sorterad => Klart! Annars: • Dela upp mängden i två lika stora delar och sortera dessa • Med mergesort! (rekursion) • Sortera ihop dessa två delar (en. Merge) 7 Mergesort(2) • Utnyttjar att det krävs få jämförelser att sortera små mängder • Vanlig optimering: Byt ut sorteringsalgoritmen när delmängden är tillräckligt liten! Komplexitet: • Antal uppdelningar av arrayen • Mergar på varje nivå O(log(n)) O(n) Tmergesort = O(log(n)) * O(n) = O(nlog(n)) Rmergesort = O(n) Stabil? (O(1) möjlig men impl. ger dålig prestanda) Ja (om sammanslagningsalgoritmen är väl vald) 8 Sammanfattning • Kvadratiska algoritmer • • • • Insertionsort Selectionsort Bubblesort Shakersort • Mer avancerade algoritmer • Shellsort • Mergesort • Quicksort Enkla, långsamma Komplexa, snabba 9 In- och utmatning Definition: En ström (en. Stream) är en sekvens av data från någon källa och/eller till något mål. • In och utmating i Java utförs av strömmar Vanliga exempel • System.out (av klassen PrintStream) • Vanligen kopplad till terminalen • System.err (av klassen PrintStream) • Vanligen kopplad till terminalen • System.in (av klassen InputStream) • Vanligen ihopkopplad med annan ström som formaterar indatat, ex. BufferedReader, BufferedLineReader, Scanner 10 In- och utmatning(2) Exempel // connect reader to inputstream BufferedReader input = new BufferedReader( new InputStreamReader(System.in)); // read a line and convert to int int num = Integer.parseInt(input.readLine()); … // open file for reading FileInputStream stream = new FileInputStream( new File("test.txt")); Scanner scanner = new Scanner(stream); // read from file System.out.println(scanner.nextInt()); 11 Formatering • Ofta vill man skriva ut på en viss form double d = 10.0/3.0; System.out.println(d); // 3.3333333333333335 • Metoden format i PrintStream kan hjälpa till: System.out.format("Värdet %1.2f är bra grejor.\n", d); // 3,33 Locale.setDefault(Locale.UK); // format adjust itself by looking at default locale! System.out.format("The value %1.2f is indeed good\n.", d); // 3.33 12 Formatering(2) • Formatspecificerare är på följande form: %[flagga][bredd]typ Typ Förklaring Flaggor Förklaring d Heltal, decimal form - Vänsterjustering x Heltal, hexadecimal form + Talets tecken skrivs ut f Reellt tal, decimalform blankt Positivt tal inleder med blank e Reellt tal, exponentform , Siffrorna grupperas tre och tre Fler specificerare finns, se Javas API-dokumentation 13 Datum och tid • Klassen Date sparar en tid // num = number of milliseconds since Jan 1, 1970 Date date = new Date(num); • Klassen Calendar är mer användbar, och kan ta fram nuvarande tid som ett Date. Calendar cal = Calendar.getInstance(); System.out.format("The time is: %tc \n", cal.getTime()); int minute = cal.get(Calendar.MINUTE); 14 Datum och tid(2) • DateFormat: en (abstrakt) klass som formaterar datum åt oss DateFormat time = DateFormat.getTimeInstance(); DateFormat date = DateFormat.getDateInstance(); Date now = Calendar.getInstance().getTime(); // note that this is locale dependent System.out.println(time.format(now)); // 13:37:00 System.out.println(date.format(now)); // 2012-nov-14 (Mer kontroll? SimpleDateFormat!) 15 Scanner • Scanner: En klass som hjälper oss att hantera inmatning • Kopplas mot en InputStream // example usage of scanner Scanner keyboard = new Scanner(System.in); while (keyboard.hasNextInt()) { myList.add(keyboard.nextInt()); } 16 Hantering av strömmar • Filer läses som strömmar • För textfiler, använd FileReader resp. FileWriter String filename = "myfile.txt"; FileReader reader = new FileReader(new File(filename)); Scanner scanner = new Scanner(reader); //... scanner.close(); // always close your streams! scanner.nextInt(); // error, since scanner + stream is now closed 17 Hantering av (binära) datafiler • För binära filer, använd DataInputStream resp. DataOutputStream String filename = "myfile.dat"; DataInputStream input; try { input = new DataInputStream(new FileInputStream(filename)); while (true) { System.out.println(input.readInt()); } } catch (EOFException e) { System.out.println("Something went wrong while reading:"); e.printStackTrace(); // print the details! } finally { input.close(); // always close! } 18 Serialisering • Det går att spara objekt på fil mellan körningar • Java kan ”platta till” ett objekt till en sekvens av bytes • Perfekt för att spara • Använd strömmarna ObjectInputStream och ObjectOutputStream • Kräver att klassen implementerar interfacet Serializable Object readObject(); // casting is needed to be usable void writeObject(Object o); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(new File("test.dat"))); out.writeObject(new Cat("Isaac", 11)); // if Serializable! out.writeObject("Hejsan"); // String is Serializable 19