CS 2110 prelim 2

¡Supera tus tareas y exámenes ahora con Quizwiz!

General vs. binary trees

- nodes have exactly one parent and are reachable from root - no loops allowed general: any # children, can be unordered binary: at most 2 children per node, right and left

Recursive algorithm patterns: counting vs. searching- check A4 count number of leaves find prolific mentor (has at least `minAdvisees` advisees, if one exists.* Otherwise, throws `NotFound`.)

// Base case: this node is a leaf if (numAdvisees() == 0) {return 1;} // This is a counting method int totalLeaves = 0; for (PhDTree advisee : advisees) { totalLeaves += advisee.numLeaves(); } return totalLeaves; public Professor findProlificMentor(int minAdvisees) throws NotFound { // This is a searching method // Base case: this node qualifies if (numAdvisees() >= minAdvisees) {return professor; } // Recursive case: search each child for (PhDTree advisee : advisees) { try { return advisee.findProlificMentor(minAdvisees); } catch (NotFound exc) { // Continue (search the next child) } } // Not found under any child; throw an exception ourselves. throw new NotFound();}

Termination and the "loop variant"

4. does it make progress? •Every repetition of body must do something to get closer to making the guard false •Typically by strictly decreasing some quantity to zero, at which point guard becomes false •That quantity is called the loop variant

Anonymous functions and functional interfaces

= lamda expressions •Syntax: (params) -> { body; } (ActionEvent e) -> { print("Action handled by " + name); } Functional interfaces: •If an interface only declares a single method, then a lambda expression can be used wherever that interface is expected Example: Comparator<T> ex: •ActionListener is an interface with only 1 method •Can satisfy the interface with an anonymous function some listener interfaces have multiple abstract methods, Can use anonymous adapter classes b.addActionListener(e -> print("Got " + e.getActionCommand()) );

Match each of these complexity classes to its common name: O(1), O(log n), O(n), O(n^2), O(2^n)

constant, logarithmic, linear, quadratic, exponential

order the complexity class common names from smallest to biggest

constant, logarithmic, linear, quadratic, exponential

Events, event sources, and event handlers

events: •Button pressed •Menu item selected •Key pressed •Mouse moved •Timer expired •Property changed When an event occurs, the source of the event notifies registered listeners •A listener is an object with a method appropriate for responding to the event (event handler) •Listeners must be registered with event sources (added to their list of listeners), often separately for each event type •Buttons and menu items trigger ActionEvents when selected

Activation records and the call stack

every function call is pushes an activation record onto stack, bottom of the stack is main(), function is popped off the stack when it returns & execution resumes for the function underneath activation record = call frame = stack frame is an element of the call stack, created when a method is called. it's a chunk of memory containing •A function's parameters, local variables, and The instruction the function is currently executing (like line number)

evaluate a hashCode() method

•Should depend on all of an object's state •Should depend on ordering of any sequential state (e.g. arrays) (avoid commutative operations like adding) •Should span whole range of integers

Recursive methods and recursive static functions

•method's inputs include the object it was invoked on as well as its arguments •Can answer "smaller" data structure problem by invoking same method on "later" node

Consistency of hashCode() and equals()

•must ensure hash code is consistent with equality •If overriding equals(), must override hashCode() too

Control flow of painting know which painting-related method should be called or overridden.

•repaint(): Request that this component be redrawn at the next opportunity •paint(): "Event handler" for "it's time to draw yourself" events •paintComponent(): Preferred method to override when extending Swing components, Components wait for system to tell them to draw (don't call paint component)

The loop checklist, aka the "four loopy questions"

1. does it start right (loop invariant true just before the loop, aka initialization/ establishment, from assigning loop variables) prove: plug in initializations and precondition to see if inv is true 2. does it maintain the invariant (preservation, invariant must be true before and after body) prove: show that when first (invariant && guard are true) after executing the loop body , the invariant is still true 3. does it end right (Loop invariant must be true just after loop And postcondition must be established by loop invariant being true and guard becoming false) prove: plug false guard into invariant and postcondition must be true 4. does it make progress toward termination prove: show that something iterates, etc until the loop guard is false

Hash codes and index derivation

1.Hash a key into an int ("hash code") 2.Turn a hash code into an array index ("index derivation") Depends on array length! Object defines a hashCode() method •Keys should be immutable

write the code to cause a component to react in a specified way. For example, make a button change the text of a label when clicked.

1.To specify what should happen when a button is clicked, implement ActionListener and override actionPerformed() 2.To be informed when a button is clicked, add your listener to the button ***************

Big O Notation Out of scope: Big Omega and Big Theta notation

A way of expressing the worst-case run-time of an algorithm. - ignore low order terms and constant factor of the high order term definition: A function f(n) is "of order at most g(n)" - f(n) is O(g(n)) if f(n) ≤ c∙g(n) for all n such that n≥N where c and N are positive. In other words, c∙g(n) is an upper bound on f(n) when n is sufficiently large. f(n) is the given function f(n) is O(g(n)) Big Oh is an asymptotic upper bound The c is what lets us ignore constant factors. The N is what lets us ignore small inputs.

Inversion of control

Code is executed in response to mouse clicks, keyboard input, etc •Client writes a function to respond to an event, but they don't call it themselves •give someone else a function that they can call later (lambda expression)

Comparable and Comparator

Comparable<T> has one method: int compareTo(T other) •Negative if this < other •Positive if this > other •Zero if this and other compare as "equal" Instead of: if (a < b) write: if (a.compareTo(b) < 0) need to say class String implements Comparable<String> or class BstNode<T extends Comparable<T>> { // ... }

write a lamda expression for a Comparator to order objects by name when sorting them

Comparator<T> cmp = (T o1, T o2)-> o1.getName().compareTo(o2.getName());

Best case vs. worst case

In terms of time: Best case: minimum time Worst case: maximum time Tight: both best and worst coincide

Swing components what is Swing, add a component, write component hierarchy, swing related to threads and event dispatch thread

Swing is a java GUI framework , •Drawing and events all handled by Java ("lightweight") •javax.swing.JFrame: a primary application window frame.add( new JLabel("Hello"), BorderLayout.NORTH) •Swing uses thread confinement •Components are not thread-safe •May only be access from Event Dispatch Thread (Swing event handling code runs on this thread to safely invoke the methods) •Computations on EDT block new events! (event handlers must be quick)

Array diagrams; array range notation

[..3] is all elements from beginning to 3 [..3) not inclusive [2..2] is just 2 [2..1] is empty

Write a JUnit assertion to check that a given expression results in an exception being thrown

assertThrows(Exception.class, () -> t.contains(prof3));

Termination and base cases

base case is where you can compute the answer on your own, terminates the method

Collision resolution: chaining vs. linear probing

chaining: treat array elements as buckets, •Finding the right bucket is O(1), but searching it will be slower linear probing: •Array elements point directly to entries •If desired element is occupied, pick the next element to try according to a probing sequence

Stability list the stable sorting algorithms & why

insertion sort •because elements only move right-to-left and stop when they hit a duplicate merge sort is stable because merging is left to right * Selection sort is not stable because long-range swaps can change order

describe Algorithms and write their loop invariants: insertion sort, selection sort, merge sort, quicksort

insertion: on the left are already sorted, take next index from the right side and insert where it belongs on the left by pushing all bigger objects out of the way (swap to the left until in position) fast for small N and arrays that are mostly sorted, can be used to sort a chain of linked nodes selection: find smallest remaining unsorted object, swap with object in desired position merge sort: 1.Sort left half of array (using merge sort) 2.Sort right half of array (using merge sort) 3.Merge left and right subarrays by repeatedly taking the smaller element of the two sequences common stable sorting quicksort: sort(a) = [ sort(a[a<p]), p, sort(a[a>=p]) ] 1.Partition array about a "pivot" 2.Sort the subarray of values less than the pivot 3.Sort the subarray of values greater than the pivot Sort via repeated partitioning often fast in practice, doesn't require additional memory like merge sort, common unstable sorting used, good for large arrays

convert to an instance method: static <E> int size(BinaryNode<E> root) { if (root == null) { return 0; } return 1 + size(root.left) + size(root.right); }

int size() { int leftSize = (left == null) ? 0 : left.size(); int rightSize = (right == null) ? 0 : right.size(); return 1 + leftSize + rightSize; }

dictionary interface & use

interface Map<K, V> {...} •K: Type of keys •V: Type of values •looking up a value associated with some key

GUI widgets (match names and pictures)

labels, buttons, text fields, sliders, radio buttons, progress bars, and scroll bars.

Terminology: leaf, parent, size, height, subtree, node, root

leaf- no children ancestors above descendants root- node at top with no parent

linear and binary search loop invariants

linear inv: v not in a[0..i), and v in a[i..] (requires v is in a) binary inv: 0 <= l <= r < a.length, and v in a[l..r] (l and r are left and right bounds)

In your own words, describe the linear and binary search algorithms.

linear search- traversing an entire list until the list has ended and nothing was found or until the desired object is found. (front to back) binary search- requires a sorted list, continuously split the list in half and determine whether to keep searching in the left or right half based on the desired object's order in the list •Divide array in half; discard half that cannot contain target •Repeat until nothing left

efficiency Application to searching algorithms (linear and binary search)

linear: Best case: find it right away with just one comparison Worst case: have to look through entire array and it's not there O(n) binary: Best case: find it right away in the middle O(1) Worst case: repeatedly half until discover it's not there O(logn)

Recursive data types

linked list node (contains reference to a node, itself, base case is where next reference is null). list is a head value and a remainder list, null represents empty list

Developing loops with an invariant (define loop invariant)

loop invariant tells us the relationship between all the important variables in the loop each time we reach the top (and bottom) of the loop. specification that tells what always true before and after the loop body (might not hold inside body) use them to help with debugging and teach you to think clearly about loops

Time and space complexity & of recursive functions specifically

measure time complexity by number of basic operations recursive time complexity: •If recursive function body contains no loops, # of operations is proportional to total # of recursive function calls space complexity- measure by memory usage (don't count size of input objects) •Recursive functions require memory proportional to maximum depth of call stack

Analyzing complexity of nested loops

multiply each loop's complexity by each other

Load factor

number of elements / number of buckets can be >1 for chaining but not for linear probing •Expected cost of lookup with chaining is O(λ) •If array size is fixed, then λ is O(N) •If array size is proportional to N, then λ is O(1)

Tree traversal orders: preorder, inorder, postorder

preorder: •visit self, then traverse left subtree, then right subtree inorder: •Traverse left subtree •Visit self •Traverse right subtree postorder: traverse left subtree, then right subtree, then visit self

Given a class with two fields, implement the Comparable interface for it so that instances are ordered first by one field, then, in the case of a tie, by the other field.

public int compareTo(Professor other) { int c = Integer.compare(phdYear, other.phdYear); return (c != 0) ? c : name.compareTo(other.name); }

Asymptotic complexity (time and space, best case and worst case) for selection sort, insertion sort, merge sort, and quicksort.

selection: time: best & worst case: O(n^2) Space: O(1) insertion: time: worst : O(n^2), best: O(n) space: O(1) merge: time: O(N) to merge, best and worst overall: O(N*logN) space: O(N), O(N) to merge , there are log(N) recursive calls but only one recursive call is on the stack at a time quicksort: time: best (pivot is median, Each subarray is less than half the size of the original): O(N*logN), depth of recursion is O(logN) and partitioning one level is O(N) worst (pivot is smallest or largest value, one subarray is only one element shorter than original array): O(N^2), depth of recursion is O(N) and partitioning one level is O(N) space: O(logN) with tail recursion good pivot: find median of beginning, middle, & end bad pivot: very bad worst case

Asymptotic complexity of operations on general trees vs. BSTs **** size, height, searching, add

size and height visit every node, time complexity O(size) space complexity O(height) bc stack frames are accumulated until reaching a leaf, and stack is popped btw right and left subtrees BST searching/ add: time complexity O(height) worst case space complexity O(height) recursive, O(1) iterative

Given an English description of an algorithm, state what quantity describes the problem size

size is some representation of how "big" the input is, like: •Number of elements in list or array •Number of digits in number, or characters in string •Number of students in course •Number of rows/columns in table •Etc.

tree operation base cases size height contains add

size: node is a leaf, size = 1 ?? height: node is a leaf, size = 1 ??? contains: current node's data matches target, next subtree to search is empty (doesn't contain) add: current node's data matches target, next subtree to search is empty (replace empty node with new node)

Given a snippet of Swing code that registers an event listener, identify the event source, the event object, and the listener. JButton button = new JButton("B"); add(button); button.addActionListener(this); @Override public void actionPerformed( ActionEvent e) { print("Got " + e.getActionCommand() + " from " + e.getSource()); }

source- button listener- this listener implements ActionPerformed() event object- e

convert to static method: int height() { int leftHeight = (left == null) ? 0 : left.height(); int rightHeight = (right == null) ? 0 : right.height(); return 1 + Math.max(leftHeight, rightHeight); }

static <E> int height(BinaryNode<E> root) { if (root == null) { return 0; } return 1 + Math.max(height(root.left), height(root.right)); }

implement a partition procedure for quicksort /** * Partition `a[begin..end)` about a selected pivot `a[i]` such that `a[begin..i) <= a[i]` and * `a[i+1..end) >= a[i]` (as determined by `cmp`), returning `i`. */

static <T> int partition(T[] a, int begin, int end, Comparator<T> cmp) { // Choose pivot and swap to beginning of array view int iPivot = begin; // FIXME: bad choice - leads to worst-case behavior for sorted input swap(a, begin, iPivot); // Invariant: a[begin..i) <= a[i], a(j..end) >= a[i] int i = begin; int j = end - 1; while (i < j) { // FIXME: pivot will be first among duplicates - leads to worst-base behavior for // duplicated input. if (cmp.compare(a[i], a[i + 1]) > 0) { swap(a, i, i + 1); i += 1; } else { swap(a, i + 1, j); j -= 1; } } return i; }

implement insertion sort /** * Sort elements of `a` in ascending order (as determined by `cmp`) using the insertion sort * algorithm. Relative ordering of elements comparing as equal is preserved. */

static <T> void insertionSort(T[] a, Comparator<T> cmp) { // Invariant: a[0..i) is sorted int i = 0; while (i < a.length) { // Slide a[i] to its sorted position in a[0..i] // Invariant: a[j] < a[j+1..i] int j = i; // Stability requires `>` and not `>=` while (j > 0 && cmp.compare(a[j - 1], a[j]) > 0) { swap(a, j - 1, j); j -= 1; } i += 1; } }

Implement merge sort given the specifications for "merge" method /** * Sort elements of `a` in ascending order (as determined by `cmp`) using the merge sort * algorithm. Relative ordering of elements comparing as equal is preserved. */ /** * Sort `a[begin..end)` in ascending order (as determined by `cmp`). Will overwrite values in * scratch array `work`. Scratch array must be at least as long as the view being sorted. */

static <T> void mergeSort(T[] a, Comparator<T> cmp) { // Allocate work array T[] work = (T[]) new Object[a.length]; // Sort entire array mergeSort(a, 0, a.length, cmp, work); } static <T> void mergeSort(T[] a, int begin, int end, Comparator<T> cmp, T[] work) { // Base case: an array of 0 or 1 element is already sorted if (end - begin <= 1) { return; } int mid = begin + (end - begin) / 2; mergeSort(a, begin, mid, cmp, work); mergeSort(a, mid, end, cmp, work); merge(a, begin, mid, end, cmp, work); }

Implement quicksort given the specification for "partition" method /** * Sort elements of `a` in ascending order (as determined by `cmp`) using the quicksort * algorithm. Relative ordering of elements comparing as equal is not preserved. */ /** * Sort `a[begin..end)` in ascending order (as determined by `cmp`). */

static <T> void quickSort(T[] a, Comparator<T> cmp) { quickSort(a, 0, a.length, cmp); } /** * Sort `a[begin..end)` in ascending order (as determined by `cmp`). */ static <T> void quickSort(T[] a, int begin, int end, Comparator<T> cmp) { // Base case: an array of 0 or 1 element is already sorted if (end - begin <= 1) { return; } int iPivot = partition(a, begin, end, cmp); quickSort(a, begin, iPivot, cmp); quickSort(a, iPivot + 1, end, cmp); }

implement selection sort /** * Sort elements of `a` in ascending order (as determined by `cmp`) using the selection sort * algorithm. Relative ordering of elements comparing as equal is not preserved. */

static <T> void selectionSort(T[] a, Comparator<T> cmp) { // Invariant: a[0..i) is sorted, a[i..] >= a[0..i) int i = 0; while (i < a.length - 1) { // Find index of smallest element in a[i..] int jSmallest = i; for (int j = i + 1; j < a.length; ++j) { if (cmp.compare(a[j], a[jSmallest]) < 0) { jSmallest = j; } } // Swap smallest element to extend sorted portion swap(a, i, jSmallest); i += 1; } }

State the worst-case time complexity of a dictionary's "put" and "get" operations if entries are stored in a sorted or unsorted list.

unsorted: put O(1) , get O(N) sorted (keys comparable): put O(N), get O(logN) (binary search)

Resizing: why, how, efficiency cost

use resizing/ dynamic array because: If array size is fixed, then λ is O(N) •If array size is proportional to N, then λ is O(1) goal: λ ~ 0.75 •When load factor exceeds target, double size of array •Cannot simply copy old array elements; must re-hash keys •Cost of resizing: O(N)

find variant (use guard : while (i < size))

variant: size - i

The event dispatch thread (EDT)

while (a window is alive || a timer is running) { check for events; for each event { call event listeners; } } Computations on EDT block new events,

Possible height of a BST (perfect tree vs degenerate list)

worst case: linked list- height = N best case: each level has twice as many nodes as previous level - height ~ lg(N)

Binary search trees; implementing "find" and "add"

•BST invariant 1.All values in left subtree are less than this node's value 2.All values in right subtree are greater than this node's value Find: 1.If target matches value, return 2.Else, if target is less than value, search left 3.Else, search right contains: Base case •Current node's data matches target •Next subtree to search is empty Recursive case •If target < current node's data, recurse left •Otherwise, target > current node's data, so recurse right Add: •Same as contains(), but tweak base case action •If next subtree to search is empty, replace it with a new node containing the new value

array characteristics relating to efficiency

•Fast to read/write arbitrary locations, iterate in reverse •Swaps are cheap •Insertions are expensive


Conjuntos de estudio relacionados

exercises 1-4 to process technology

View Set

4. A gazdálkodás alapegységei (4.1-4.2.5)

View Set

Chapter 19: Working with Families

View Set

History of the Republican Party (Schlesinger)

View Set

The sequence of events in muscle contraction (Ch.9)

View Set