Lausnum skal skilað til viðkomandi dæmakennara (sjá lista yfir dæmatíma).
Reynið að skrifa stutt, skýr og hnitmiðuð forrit. Notið réttan inndrátt og gætið þess að forritin ykkar séu læsileg.
Skiladagur: þriðjudaginn 6. mars fyrir kl 16:00
Útfærið quickselect í beinagrindinni með hjálp partition fallsins úr quicksort. Útfærið síðan fallið median í beinagrindinni með hjálp quickselect. Median fallið á að finna miðgildi í óröðuðu fylki heiltalna á O(N) tíma.
Ef stærð fylkisins er jöfn tala þá er miðgildið meðaltal miðjustakanna tveggja. Þið getið skoðað nokkur dæmi á Wikipedia.
import java.util.Random; public class Quickselect { // Notkun: select(f,i,j,k) // Fyrir: f[i..j-1] er svæði í fylkinu f, i <= k < j // f[i..j-1] má vera óraðað. // Eftir: Búið er að víxla gildunum í f[i..j-1] þannig // að f[i..k-1] <= f[k] <= f[k+1..j-1]. public static void select(int[] f, int i, int j, int k) { // ... } // Notkun: m = median(f); // Fyrir: Ekkert // Eftir: m er miðgildi f public static double median(int[] f) { // ... } // Notkun: k = partition(f,i,j,s); // Fyrir: f[i..j-1] er löglegt svæði í f, // i <= s < j // f[s] er pivot gildi til að skipta eftir // Eftir: Búið er að víxla gildum í svæðinu þ.a. // f[i..k-1] <= f[k] <= f[k+1..j-1] // Gildið í sæti k var upphaflega í sæti s. public static int partition(int[] f, int i, int j, int s) { // Úr V7 } // Notkun: b = is_partitioned(f,i,j,k) // Fyrir: f[i..j-1] er svæði í fylkinu f, i <= k < j // f[i..j-1] má vera óraðað. // Eftir: b er satt þþaa. að // f[i..k-1] <= f[k] <= f[k+1..j-1]. static boolean is_partitioned(int[] f, int i, int j, int k) { for (int n = i; n != k; n++) if (f[n] > f[k]) return false; for (int n = k; n != j; n++) if (f[n] < f[k]) return false; return true; } private static void swap(int[] f, int a, int b) { int tmp = f[a]; f[a] = f[b]; f[b] = tmp; } public static void main(String[] args) { Random g = new Random(); for (int n = 0; n < 5; n++) { int[] f = {62,88,59,94,4,71,26,17,44,28,37,44,64,25,73,21,23,35,49,23}; int k = g.nextInt(f.length); select(f, 0, f.length, k); if (is_partitioned(f, 0, f.length, k)) System.out.printf("Test 1-%d: Passed\n", n); else System.out.printf("Test 1-%d: Failed\n", n); } int[] a = {8, 2, 7, 5, 1}; if (median(a) == 5.0) System.out.println("Test 2: Passed"); else System.out.println("Test 2: Failed"); int[] b = {6, 2, 8, 2, 7, 1}; if (median(b) == 4.0) System.out.println("Test 3: Passed"); else System.out.println("Test 3: Failed"); int[] c = {62,88,59,94,4,71,26,17,44,28,37,44,64,25,73,21,23,35,49,23}; if (median(c) == 40.5) System.out.println("Test 4: Passed"); else System.out.println("Test 4: Failed"); } }
#include <stdio.h> #include <time.h> #define NELEMS(x) (sizeof(x) / sizeof(x[0])) // Notkun: select(f,i,j,k) // Fyrir: f[i..j-1] er svæði í fylkinu f, i <= k < j // f[i..j-1] má vera óraðað. // Eftir: Búið er að víxla gildunum í f[i..j-1] þannig // að f[i..k-1] <= f[k] <= f[k+1..j-1]. void select(int *f, int i, int j, int k) { // ... } // Notkun: m = median(f); // Fyrir: Ekkert // Eftir: m er miðgildi f double median(int *f, int n) { // ... } void swap(int *f, int a, int b) { int tmp = f[a]; f[a] = f[b]; f[b] = tmp; } // Notkun: k = partition(f,i,j,s); // Fyrir: f[i..j-1] er löglegt svæði í f, // i <= s < j // f[s] er pivot gildi til að skipta eftir // Eftir: Búið er að víxla gildum í svæðinu þ.a. // f[i..k-1] <= f[k] <= f[k+1..j-1] // Gildið í sæti k var upphaflega í sæti s. int partition(int *f, int i, int j, int s) { int p = f[s]; int n = i+1; int t; swap(f, i, s); for (t = i+1; t != j; t++) { // [p| < p | >= p | ??? ] // i n t j if (f[t] < p) { swap(f, t, n); n++; } } // [p| < p | >= p ] // i n j n--; swap(f, i, n); // [ < p |p| >=p ] // i n j return n; } // Notkun: b = is_partitioned(f,i,j,k) // Fyrir: f[i..j-1] er svæði í fylkinu f, i <= k < j // f[i..j-1] má vera óraðað. // Eftir: b er satt þþaa. að // f[i..k-1] <= f[k] <= f[k+1..j-1]. int is_partitioned(int *f, int i, int j, int k) { int n; for (n = i; n != k; n++) if (f[n] > f[k]) return 0; for (n = k; n != j; n++) if (f[n] < f[k]) return 0; return 1; } int main(void) { int n; srand(time(NULL)); for (n = 0; n < 5; n++) { int f[] = {62,88,59,94,4,71,26,17,44,28,37,44,64,25,73,21,23,35,49,23}; int k = rand() % NELEMS(f); select(f, 0, NELEMS(f), k); if (is_partitioned(f, 0, NELEMS(f), k)) printf("Test 1-%d: Passed\n", n); else printf("Test 1-%d: Failed\n", n); } int a[] = {8, 2, 7, 5, 1}; if (median(a, NELEMS(a)) == 5.0) printf("Test 2: Passed\n"); else printf("Test 2: Failed\n"); int b[] = {6, 2, 8, 2, 7, 1}; if (median(b, NELEMS(b)) == 4.0) printf("Test 3: Passed\n"); else printf("Test 3: Failed\n"); int c[] = {62,88,59,94,4,71,26,17,44,28,37,44,64,25,73,21,23,35,49,23}; if (median(c, NELEMS(c)) == 40.5) printf("Test 4: Passed\n"); else printf("Test 4: Failed\n"); }
Í partition fallinu hér fyrir neðan leynist ein villa. Finnið og lagið hana. Skrifið jafnframt fastayrðingu og eftirskilyrði fyrir lykkjuna ásamt tveimur öðrum stöðulýsingum innan í lykkjunni á þar til gerðum stöðum.
Notið ospd-random.txt til að prófa forritið. Forritið ætti að prenta út "SUCCESS".
hhg@hhg:~/t2/V8/d2$ java Quicksort < ospd-random.txt SUCCESS hhg@hhg:~/t2/V8/d2$
import java.util.Scanner; import java.util.ArrayList; public class Quicksort { // Notkun: sort(a, lo, hi); // Fyrir: a[lo..hi] er löglegt svæði // Eftir: Búið er að raða a[lo..hi] private static void sort(String[] a, int lo, int hi) { if (hi <= lo) return; int j = partition(a, lo, hi); sort(a, lo, j-1); sort(a, j+1, hi); } // Notkun: k = partition(a, lo, hi); // Fyrir: a[lo..hi] er löglegt svæði // Eftir: Búið er að víxla gildunum í f[lo..hi] þannig // að f[lo..k-1] <= f[k] <= f[k+1..hi]. private static int partition(String[] a, int lo, int hi) { int i = lo + 1; int j = hi; String v = a[lo]; while (true) { // Skrifið fastayrðingu lykkju hér while (i < hi && less(a[i], v)) i++; // Skrifið stöðulýsingu fyrir i og a[i] hér while (j > lo && less(v, a[j])) j--; // Skrifið stöðulýsingu fyrir j og a[j] hér if (i >= j) break; swap(a, i, j); i++; j++; } // Hér vantar eftirskilyrði (post-condition) swap(a, lo, j); return j; } // er v < w ? private static boolean less(String v, String w) { return v.compareTo(w) < 0; } // víxlar a[i] og a[j] private static void swap(Object[] a, int i, int j) { Object swap = a[i]; a[i] = a[j]; a[j] = swap; } private static boolean isSorted(String[] a, int lo, int hi) { for (int i = lo + 1; i <= hi; i++) if (less(a[i], a[i-1])) return false; return true; } public static void main(String[] args) { ArrayList<String> q = new ArrayList<String>(); Scanner scan = new Scanner(System.in); while (scan.hasNext()) { q.add(scan.nextLine()); } String[] f = q.toArray(new String[0]); sort(f, 0, f.length - 1); if (isSorted(f, 0, f.length - 1)) System.out.println("SUCCESS"); else System.out.println("FAILED"); } }
#include <stdio.h> #include <stdlib.h> #include <string.h> // Notkun: sort(a, lo, hi); // Fyrir: a[lo..hi] er löglegt svæði // Eftir: Búið er að raða a[lo..hi] void sort(char *a[], int lo, int hi) { if (hi <= lo) return; int j = partition(a, lo, hi); sort(a, lo, j-1); sort(a, j+1, hi); } void swap(char *a[], int i, int j) { char *swap = a[i]; a[i] = a[j]; a[j] = swap; } // Notkun: k = partition(a, lo, hi); // Fyrir: a[lo..hi] er löglegt svæði // Eftir: Búið er að víxla gildunum í f[lo..hi] þannig // að f[lo..k-1] <= f[k] <= f[k+1..hi]. int partition(char *a[], int lo, int hi) { int i = lo + 1; int j = hi; char *v = a[lo]; while (1) { // Skrifið fastayrðingu lykkju hér while (i < hi && strcmp(a[i], v) < 0) i++; // Skrifið stöðulýsingu fyrir i og a[i] hér while (j > lo && strcmp(v, a[j]) < 0) j--; // Skrifið stöðulýsingu fyrir j og a[j] hér if (i >= j) break; swap(a, i, j); i++; j++; } // Hér vantar eftirskilyrði (post-condition) swap(a, lo, j); return j; } int isSorted(char *a[], int lo, int hi) { int i; for (i = lo + 1; i <= hi; i++) if (strcmp(a[i], a[i-1]) < 0) return 0; return 1; } char *readline(FILE *fp) { size_t size = 0; size_t len = 0; size_t last = 0; char *buf = NULL; while (!feof(fp)) { size += BUFSIZ; buf = realloc(buf, size); if (fgets(buf+last, size-last, fp) == NULL) break; len = strlen(buf); last = len - 1; if (buf[last] == '\n') break; } if (len == 0) { free(buf); return NULL; } return buf; } int main(void) { char **lines; size_t size = 65536; int n = 0; lines = malloc(sizeof(char *) * size); while (1) { if (n >= size) { size *= 2; lines = realloc(lines, sizeof(char *) * size); } char *line = readline(stdin); if (line == NULL) break; lines[n++] = line; } sort(lines, 0, n-1); if (isSorted(lines, 0, n - 1)) printf("SUCCESS\n"); else printf("FAILED\n"); }
Klárið að útfæra beinagrindina samkvæmt þeirri gagnaskipan sem er lýst efst í klasanum.
Skrifið fastayrðingu fyrir sérhverja lykkju sem þið notið. Greinið frá tímaflækju deleteMin og Put.
public class PriQueue { // Gildin í forgangsbiðröðinni eru í f[0..n-1] // í engri sérstakri röð. private String[] f; private int n; private int max; // Notkun: pq = new PriQueue(max); // Fyrir: max > 0 // Eftir: pq er ný tóm forgangsbiðröð með pláss fyrir // max gildi public PriQueue(int max) { // ... } // Notkun: s = pq.deleteMin(); // Fyrir: pq er ekki tóm. // Eftir: s er eitt minnsta stakið sem var // í pq. s hefur verið fjarlægt úr pq. public String deleteMin() { // ... } // Notkun: pq.Put(s); // Fyrir: pq er ekki full. // Eftir: s hefur verið bætt við pq. public void Put(String s) { // ... } // Notkun: n = pq.Count(); // Eftir: n er fjöldi staka í pq. public int Count() { // ... } // Notkun: b = pq.isFull(); // Eftir: b er satt þþaa. pq sé full. public boolean isFull() { // ... } public static void main(String[] args) { PriQueue pq = new PriQueue(100); pq.Put("F"); pq.Put("B"); pq.Put("D"); pq.Put("C"); pq.Put("A"); pq.Put("H"); while (pq.Count() > 0) System.out.println(pq.deleteMin()); } }
Klárið að útfæra beinagrindina samkvæmt þeirri gagnaskipan sem er lýst efst í klasanum. Útfærið Put aðferðina með samskonar innsetningu og við notuðum í insertion sort.
Skrifið fastayrðingu fyrir sérhverja lykkju sem þið notið. Greinið frá tímaflækju deleteMin og Put.
public class PriQueue { // Gildin í forgangsbiðröðinni eru í f[0..n-1] // í minnkandi röð frá f[0] til f[n-1], þ.e. // f[0] er stærsta gildið og f[n-1] er minnsta gildið. private String[] f; private int n; private int max; // Notkun: pq = new PriQueue(max); // Fyrir: max > 0 // Eftir: pq er ný tóm forgangsbiðröð með pláss fyrir // max gildi public PriQueue(int max) { // ... } // Notkun: s = pq.deleteMin(); // Fyrir: pq er ekki tóm. // Eftir: s er eitt minnsta stakið sem var // í pq. s hefur verið fjarlægt úr pq. public String deleteMin() { // ... } // Notkun: pq.Put(s); // Fyrir: pq er ekki full. // Eftir: s hefur verið bætt við pq. public void Put(String s) { // ... } // Notkun: n = pq.Count(); // Eftir: n er fjöldi staka í pq. public int Count() { // ... } // Notkun: b = pq.isFull(); // Eftir: b er satt þþaa. pq sé full. public boolean isFull() { // ... } public static void main(String[] args) { PriQueue pq = new PriQueue(100); pq.Put("F"); pq.Put("B"); pq.Put("D"); pq.Put("C"); pq.Put("A"); pq.Put("H"); while (pq.Count() > 0) System.out.println(pq.deleteMin()); } }
Gefum okkur eftirfarandi tré þar sem hver hnútur í trénu hefur að hámarki tvö börn:
Við skulum geyma tréið í fylki með eftirfarandi sætaskipan:
Almennt þá gilda eftirfarandi reglur um slíka sætaskipan:
Í beinagrindinni hér fyrir neðan eru gefin ýmis föll sem vinna með tréð. Þar eru m.a. föll sem ferðast um tréð og prenta út gildi í in-order, pre-order eða post-order röð. Þið getið lesið ykkur til um slíkar raðanir á Wikipedia eða Google. Þið eigið að skrifa tvö auka föll:
public class BinaryTreeArray { public static void printInOrder(int[] f, int n) { if (n >= f.length) return; printInOrder(f, 2*n+1); System.out.printf("%d ", f[n]); printInOrder(f, 2*n+2); } public static void printPreOrder(int[] f, int n) { if (n >= f.length) return; System.out.printf("%d ", f[n]); printPreOrder(f, 2*n+1); printPreOrder(f, 2*n+2); } public static void printPostOrder(int[] f, int n) { if (n >= f.length) return; printPostOrder(f, 2*n+1); printPostOrder(f, 2*n+2); System.out.printf("%d ", f[n]); } // Notkun: printLeaves(f, n); // Eftir: Búið er að prenta öll lauf // í trénu sem hefur rótina f[n] public static void printLeaves(int[] f, int n) { // ... } // Notkun: printRootPath(f, n); // Fyrir: f inniheldur tvítré // 0 <= n < f.length // Eftir: Búið er að prenta út öll gildin í trénu // f á veginum frá f[n] upp að rótinni f[0] public static void printRootPath(int[] f, int n) { // ... } public static void printSubtree(int level, int[] f, int n) { if (level > 0) System.out.print(" "); int leaves = f.length/2; if (n < leaves) { System.out.print("(" + f[n]); printSubtree(level+1, f, 2*n+1); printSubtree(level+1, f, 2*n+2); System.out.print(")"); } else if (n < f.length) { System.out.printf("%d", f[n]); } else { System.out.print("()"); } } public static void main(String[] args) { /* * tree in f: * 2 * / \ * / \ * / \ * 7 5 * / \ / \ * 1 6 8 9 * / \ * 5 11 * * rules: * f[i] has left child f[2*i + 1] * f[i] has right child f[2*i + 2] * f[i] has parent f[(n-1)/2] */ int[] f = {2,7,5,1,6,8,9,5,11}; System.out.println("Items:"); for (int i = 0; i < f.length; i++) System.out.printf("f[%d] = %d\n", i, f[i]); System.out.println(); System.out.println("List representation of tree:"); printSubtree(0, f, 0); System.out.println(); System.out.println(); System.out.println("In-order traversal:"); printInOrder(f, 0); System.out.println(); System.out.println(); System.out.println("Pre-order traversal:"); printPreOrder(f, 0); System.out.println(); System.out.println(); System.out.println("Post-order traversal:"); printPostOrder(f, 0); System.out.println(); System.out.println(); System.out.println("Pre-order traversal of the leaf nodes:"); printLeaves(f, 0); System.out.println(); System.out.println("Expecting:"); System.out.println("5 11 6 8 9"); System.out.println(); System.out.println("Path to root from leaf f[8] == 11"); printRootPath(f, 8); System.out.println("Expecting:"); System.out.println("11 1 7 2"); System.out.println(); System.out.println("Path to root from leaf f[4] == 6"); printRootPath(f, 4); System.out.println("Expecting:"); System.out.println("6 7 2"); System.out.println(); System.out.println("Path to root from leaf f[5] == 8"); printRootPath(f, 5); System.out.println("Expecting:"); System.out.println("8 5 2"); } }
#include <stdio.h> #define NELEMS(x) (sizeof(x) / sizeof(x[0])) static void printInOrder(int *f, int size, int n) { if (n >= size) return; printInOrder(f, size, 2*n+1); printf("%d ", f[n]); printInOrder(f, size, 2*n+2); } static void printPreOrder(int *f, int size, int n) { if (n >= size) return; printf("%d ", f[n]); printPreOrder(f, size, 2*n+1); printPreOrder(f, size, 2*n+2); } static void printPostOrder(int *f, int size, int n) { if (n >= size) return; printPostOrder(f, size, 2*n+1); printPostOrder(f, size, 2*n+2); printf("%d ", f[n]); } static void printLeaves(int *f, int size, int n) { // ... } static void printRootPath(int *f, int size, int n) { // ... } static void printSubtree(int level, int *f, int size, int n) { if (level > 0) printf(" "); int leaves = size/2; if (n < leaves) { printf("(%d", f[n]); printSubtree(level+1, f, size, 2*n+1); printSubtree(level+1, f, size, 2*n+2); printf(")"); } else if (n < size) { printf("%d", f[n]); } else { printf("()"); } } int main(void) { /* * tree in f: * 2 * / \ * / \ * / \ * 7 5 * / \ / \ * 1 6 8 9 * / \ * 5 11 * * rules: * f[i] has left child f[2*i + 1] * f[i] has right child f[2*i + 1] * f[i] has parent f[(n-1)/2] */ int f[] = {2,7,5,1,6,8,9,5,11}; int i; printf("Items:\n"); for (i = 0; i < NELEMS(f); i++) printf("f[%d] = %d\n", i, f[i]); printf("\n"); printf("List representation of tree:\n"); printSubtree(0, f, NELEMS(f), 0); printf("\n"); printf("\n"); printf("In-order traversal:\n"); printInOrder(f, NELEMS(f), 0); printf("\n"); printf("\n"); printf("Pre-order traversal:\n"); printPreOrder(f, NELEMS(f), 0); printf("\n"); printf("\n"); printf("Post-order traversal:\n"); printPostOrder(f, NELEMS(f), 0); printf("\n"); printf("\n"); printf("Pre-order traversal of the leaf nodes:\n"); printLeaves(f, NELEMS(f), 0); printf("\n"); printf("Expecting:\n"); printf("5 11 6 8 9\n"); printf("\n"); printf("Path to root from leaf f[8] == 11\n"); printRootPath(f, NELEMS(f), 8); printf("Expecting:\n"); printf("11 1 7 2\n"); printf("\n"); printf("Path to root from leaf f[4] == 6\n"); printRootPath(f, NELEMS(f), 4); printf("Expecting:\n"); printf("6 7 2\n"); printf("\n"); printf("Path to root from leaf f[5] == 8\n"); printRootPath(f, NELEMS(f), 5); printf("Expecting:\n"); printf("8 5 2\n"); }