===== Lösungsversuch SS 19 ===== ==== Aufgabe 1 (Wissensfragen)==== === a) === - Falsch, der gierige Algorithmus findet 25,10,5 was 40 ergibt - Richtig - Richtig - Falsch, Horner Schema ist iterativ möglich === b) === - Richtig - Falsch - Falsch - Falsch === c) === - Richtig - Falsch - Falsch - Richtig === d) === - Richtig, offene Adressierung ist z.B. lineares Sondieren - Richtig, O(log(n)) wenn contains mit binärerSuche sucht - Falsch, O(n) - falsch, andersrum === e) === - Richtig, die anzahl der Bänder verändert die Laufzeit nicht - Richtig, auch wenn Θ(n2) und nicht O(n2) angegeben ist, müsste das stimmen - Falsch, man brauch 50 Fächer - Falsch, Verallgemeinertes Sortieren = Radixsort. Radixsort hat eine Laufzeit von O(n*k), Bucketsort hat O(n+K) === f) === - Richtig - Richtig - Falsch, man muss ja trotzdem die Knoten mit in Betracht ziehen - Falsch, Dijkstra bestimmt nicht den minimalen Spannbaum, sondern die küzesten Wege === g) === - Richtig, Skript 15, Seite 15 - Richtig - Falsch, immer O(n²) - Falsch, Definition für zerteilen / divisiv ==== Aufgabe 2 (Dynamische Programmierung)==== === a) === verschachtelte Rekursion === b) === public static int g(int n) { if (n == 1) return 1; return 1 + g(n - g(g(n - 1))); } } === c) === public static int gMem(int n) { assert n >= 1; return gMH(n, new int[n + 1]); // m[0] is left unused } public static int gMH(int n, int[] m) { assert n >= 1; if (n <= 1) { return 1; } else if (m[n] == 0) { m[n] = 1 + gMH(n - gMH(gMH(n - 1, m), m), m); } else { return m[n]; } return m[n]; } === d) === static int gDP(int n) { assert n >= 1; int[] m = new int[n + 1]; // m[0] is left unused m[1] = 1; for (int k = 2; k <= n; k++) { m[k] = 1 + m[k - m[m[k - 1]]]; } return m[n]; } ==== Aufgabe 3 (Bäume)==== === a) === * 7 einfügen: 8,6,10,4,7,null,12 * 11 einfügen: 10,8,12,4,null,11,14 * 5 einfügen: 8,5,10,4,6,null,null === b) === 1,2,6,8,4,12,14,16,19,10 === c) === 4,12,6,14,16,10,8,22,20,18 === d) === * preorder: 2,4,8,10,6,12,14 * inorder: 8,4,10,2,12,6,14 * postorder: 8,10,4,12,14,6,2 === e) === * DFS: 0,1,4,5,2,7,6,3,8 (diese Reihenfolge entsteht nur, wenn man in die Stack immer den hösten Wert der Nachfolger zu erst einfügt, so ist sicher gestellt, dass man das niedrigste Kind als erstes von dem aktuellen Knoten besucht) * BFS: 0,1,2,3,4,5,7,8,6 (hier wird immer das kleinste Kind in die Queue eingefügt um so sicherzugehen, dass man zuerst kleine kinder besucht) ==== Aufgabe 4 (Maximale Teilsummen in Listen mit Sortierung)==== === a,b,c,d im unteren Code === public class Aufgabe3 { public static void main(String[] args) { LinkedList l1 = new LinkedList(Arrays.asList(2, 3, 30, 90, 120, 240, 511)); LinkedList l2 = new LinkedList(Arrays.asList(1, 3, 12, 32, 90, 125, 240)); List e = maxPartSumList(l1, l2); System.out.println(e); } // a static List subListHead(List one, int lastIndex) { // falls LastIndex letzter Eintrag ist draf man nicht mit +1 die Liste erstellen return (lastIndex < one.size() ? one.subList(0, lastIndex + 1) : one.subList(0, lastIndex)); } // returns the values from ’one’ starting immediately after lastIndex static List subListTail(List one, int lastIndex) { return one.subList(lastIndex + 1, one.size()); } // b private static int partSum(List part) { int r = 0; for (int i : part) { r += i; } return r; } // c private static List> split(List one, List two) { List> r = new LinkedList<>(); for (int v : two) { int idx = one.indexOf(v); if (idx != -1) { r.add(subListHead(one, idx)); List neu = subListTail(one, idx); one = new LinkedList<>(neu); } } // falls one groesser two, ist one noch nicht komplett leer und muss noch an r // angefügt werden if (!one.isEmpty()) { r.add(one); } return r; } // d private static List maxPartSumList(List one, List two) { List r = new LinkedList<>(), oneP, twoP; Iterator> oneI = split(one, two).iterator(); Iterator> twoI = split(two, one).iterator(); while (oneI.hasNext() && twoI.hasNext()) { // wichtig .next nur einmal in while schleife aufrufen, // da mit jedem .next der Iterator eins weiter geht!!! List eins = oneI.next(); List zwei = twoI.next(); int sumOne = partSum(eins); int sumTwo = partSum(zwei); if (sumOne >= sumTwo) { r.addAll(eins); } else { r.addAll(zwei); } } // falls noch letzter Abschnitt hinzugefügt werden muss if (oneI.hasNext()) { r.addAll(oneI.next()); } if (twoI.hasNext()) { r.addAll(twoI.next()); } return r; } } ==== Aufgabe 5 (Graph-Algorithmen)==== === a) === boolean isStronglyConnected(Graph g) { Set allNodes = g.allNodes(); for (E start : allNodes) { LinkedList visited = new LinkedList<>(), queue = new LinkedList<>(); queue.add(start); while (!queue.isEmpty()) { E akt = queue.poll(); // wenn aktueller Knoten quelle ist dann kein zusamenhängender Graph if (preds(akt).isEmpty()) { return false; } if (!visited.contains(akt)) { visited.add(akt); List nachfolger = succs(akt); if (nachfolger.isEmpty()) { // aktueller Knoten ist Senke, kann kein stark zusammenhängeder graph sein return false; } for (E i : nachfolger) { queue.add(i); } }//visitedAbfrage schließt }//while schießt if(visited.size()!=allNodes.size()){ return false; // abfrage ist wichtig, weil es kann sein, dass zwei Knoten gegenseitig auf sich zeigen, damit haben sie beide Vor und Nachfolger aber die anderen Knoten werden nicht erreicht } }//forschleife schließt return true; } === b) === boolean hasEulerCircuit(Graph g) { Set allNodes = g.allNodes(); for(E akt : allNodes) { List vor = preds(akt); List nach = succs(akt); if(vor.size() != nach.size()) { return false; } } return isStronglyConnected(g); } === c) === List euler(Graph g, E node) { LinkedList stack = new LinkedList<>(), euler = new LinkedList<>(); stack.add(node); while(!stack.isEmpty()) { E akt = stack.peek(); //nicht rauslöschen!!! List nach = succs(akt); if(nach.isEmpty()) { //keine nachfolger = Blattknoten -> zu Euler hinzu euler.addFirst(akt); //vorne anfügen stack.pop(); }else { for(E e : nach) { stack.push(e); } //besuchte Kante löschen g.removeEdge(akt,stack.peek()); } } return euler; } ==== Aufgabe 6 (Abstrakte Datentypen)==== === a) === Union(Empty,s2) = s2 Union(add(s2,e),s2)= Add(unision(s1,s2),e) === b) === ContainsAll(s1,Empty) = true containsAll(Empty,s2) = false containsAll(s1,Add(s2,e)) = ContainsAll(s1,s2) && contains (s1,e) === c) === costSum(Create) = 0 CostSum(Append(l,s,c)) = c+costsum(l) === d) === setUnion(Create) = Empty setUnion(Append(l,s,c)) = union(S,setUnion(l)) === f) === sCH(Create, u, accu) = ... ... accu falls containsAll (setUnion(accu), u) ... create sonst sCH(Append(l,s,c),u,accu= ... ... sCH(l,u,better(Append(accu,s,c),accu)) ____________________________________________________________________ ==== Aufgabe 7 (Backtracking)==== === a) === String getVerticalPrefix(int r, int c) { if(r<0 || g[r][c]=='#' || r>= g.length || c >= g[r].length) { return ""; }else { return g[r][c]+getVerticalPrefix(r-1,c); } } === b) === List consumeV(int r, int c, String w) { List wsPV = new LinkedList<>(); // completed by inserting w for (int i = c; i < c + w.length(); i++) { String vp = getVerticalPrefix(r,i); if(wsV.contains(vp)) { wsV.remove(vp); wsPV.add(vp); } } return wsPV; } === c) === boolean solve() { return helper(0, 0); // will always return true } boolean helper(int r, int c) { // traverse g row by row and left to right if(wsH.isEmpty()) { return true; } else if(c>g[r].length) { // ende der Zeile return helper(r+1,0); }else if(g[r][c] =='#') { //worttrenner return helper(r,c+1); } // try placing each remaining w from wsH at g[r][c] boolean[] cpH; // result from placeH List wsPV; // result from consumeV for (int i = 0; i < wsH.size(); i++) { String w = wsH.remove(i); if(canPlace(r,c,w)) { cpH = placeH(r,c,w); wsPV = consumeV(r,c,w); if(helper(r,c+w.length())) { return true; } //keine Lösung, backtrack unPlace(r,c,cpH); wsV.addAll(wsPV); // consume loescht das Wort in wsV und speichert es in wsPV, das muss zurückkoppiert werden wsH.add(w); } } return false; }