For each exercise, write some assert
statements that check that your solution works correctly. For example:
int sum(int x, int y) { return x + y; }
assert sum(3, 4) == 7;
assert sum(1, 2) == 3;
If you want to easily copy-paste all of your code into and out of JLearner in one step, write your statements inside a main
method:
int sum(int x, int y) { return x + y; }
void main() {
assert sum(3, 4) == 7;
assert sum(1, 2) == 3;
}
main()
Note: all Java language constructs that you need to use for these exercises are also used in the examples that you can find via the drop-down menu at the top of the JLearner webpage, so you may want to open a second copy of JLearner in a separate window so that you can look at a relevant example while making an exercise.
Note: some later exercises build on earlier ones, so keep your solutions.
x
and y
are of type int
, x / y
is the quotient of x
and y
, rounded toward zero.)x
to the power of y
, for nonnegative y
(iterative implementation)x
to the power of y
, for nonnegative y
(recursive implementation)Vector
such that an object of this class can be used to store a two-dimensional vector with integer coordinates x
and y
. (That is, declare a class Vector
with a field x
and a field y
.)Vector
object. (Use the square root method you declared earlier.)Vector
object is larger than the vector stored in another given Vector
object. Use the size method you declared in the previous exercise.Vector
object that stores the sum of the vectors stored in two given Vector
objects.Vector
object by the vector stored in another given Vector
object. It updates the first Vector
object; it does not create a new object.A person is a sibling of another person if they are either a brother or a sister of the other person. For these exercises, assume no two siblings have the same age.
Person
with a field age
and a field nextOldestSibling
.) (The next oldest sibling of a person is the oldest sibling of that person that is younger than that person.) If a person has no younger siblings, store null
in the corresponding nextOldestSibling
field.Person
object corresponding to that person (iterative implementation).Person
object corresponding to that person (recursive implementation).Person
object corresponding to that person (iterative implementation).Person
object corresponding to that person (recursive implementation).Person
object corresponding to that person (iterative implementation).Person
object corresponding to that person (recursive implementation).Person
object corresponding to the youngest sibling of some person p to reflect the fact that a new sibling (age 0) was born, given a Person
object corresponding to person p (iterative implementation).Person
object corresponding to the youngest sibling of some person p to reflect the fact that a new sibling (age 0) was born, given a Person
object corresponding to person p (recursive implementation).isSorted
such that isSorted(array)
returns true
if and only if the values stored in array
are stored in ascending order, i.e. for any two indices I and J, if I < J then array[I] <= array[j]
.insert
such that insert(array, n, v)
inserts value v
into the sorted sequence of values stored at indices 0 (inclusive) through n
(exclusive) of array
, such that afterwards, the sequence of values stored at indices 0 (inclusive) through n + 1
(exclusive) is sorted. You will need to shift the elements of the sequence that are greater than v
one position to the right. You may assume that the given array has length n + 1
or greater.insertionSort
such that insertionSort(array)
replaces the sequence of values stored in array
with a sorted version of that sequence. Hint: first use insert
to insert the second element into the sequence at indices 0 (inclusive) through 1 (exclusive). Then use insert
to insert the third element into the sequence at indices 0 (inclusive) through 2 (exclusive). And so on.removeGreatest
such that removeGreatest(array, n)
returns the greatest element of the sequence of values stored at indices 0 (inclusive) through n
(exclusive) in array
, and removes one occurrence of that element from the sequence. You will need to shift the elements that appear after the removed element to the left by one position.selectionSort
such that selectionSort(array)
replaces the sequence of values stored in array
with a sorted version of that sequence. Hint: first use removeGreatest
to remove the greatest element of the sequence and then put that element in the last position. Then use removeGreatest
to remove the greatest element of the remaining sequence and then put that element in the one-but-last position. And so on.merge
such that merge(array1, array2)
returns an array that satisfies the following properties:
array1
plus the number of occurrences of V in array2
array1
and array2
are sorted (i.e. the elements in the array are stored in ascending order), then the result array is sorted as well.subarray
such that subarray(array, a, b)
returns an array of length b - a
that contains the elements at indices a
(inclusive) through b
(exclusive) of array
.mergeSort
such that mergeSort(array)
returns a new array that stores the sequence of values obtained by sorting the sequence of values stored in array
. Hint: if the length of the array is 0 or 1, just return a copy of the array. Otherwise, use subarray
to get the two halves of array
, sort them using a recursive call of mergeSort
, and then merge them using merge
.In computer science, a tree consists of a number of nodes. Each node has zero or more children, which are themselves nodes. Each node is a child of at most one other node, which is called its parent. Exactly one node has no parent; this node is called the root node of the tree. We say the tree is rooted in that node.
A binary tree is a tree where each node has at most two children.
We can interpret an array as a binary tree as follows:
We say a tree is a max-heap if the value of a node is not less than the values of its children. In a max-heap, the value of the root node is the maximum value of all nodes of the tree.
Given a max-heap with N nodes in an array of length N + 1 or greater, we can add an element to the heap by first putting it at index N, i.e., by adding it as a child of the node at index (N-1)/2. Notice that this may break the max-heap property: the new value might be greater than its parent's value. If so, we swap the two values. But then, if the parent node is not the root node, the max-heap property may still be broken because the parent node's value might be greater than its own parent node's value. So we apply this sift up operation recursively to fully restore the max-heap property.
heapAdd(array, n)
that adds the element at index n
to the heap at indices 0 (inclusive) through n
(exclusive), using the sift up algorithm.Given a max-heap with N nodes, we can remove the root element by replacing it with the value of the leaf node at index N-1. At this point, the max-heap property may be broken because the new root value might be less than the value of one (or both) of the root node's children. In that case, we swap the root node's value with that of the child with the greatest value. But then, if this child has children of its own, the max-heap property may still be broken since this child's value may now be less than that of one (or both) of its own children. Therefore, we recursively apply this sift down operation to the child.
heapRemove(array, n)
that removes the root element (i.e. the greatest element) of the heap at indices 0 (inclusive) through n
(exclusive) and returns its value.We can turn an array into a max-heap (known as heapification) as follows:
heapAdd
method defined above.We can sort an array by first heapifying it and then repeatedly removing the greatest element and putting it in the space freed.
heapSort
such that heapSort(array)
sorts array
using this heap sort algorithm.A search tree is an ordered tree: the order of the children of a given node is important. Specifically, in a search tree, if child A is before child B, then the values stored in the subtree rooted at child A are less than the values stored in the subtree rooted at child B.
TreeNode
that represents a node in a search tree. Each node stores a reference to its first child node (or null
if it has no children), its next sibling node (or null
if it has no further siblings), and its value. The children of a node are the first child and the first child's siblings.addLeafValues(node, array, i)
that writes the values of the descendants of node
that are leaves into array
starting at index i
and returns the number of values written. (Hint: use recursion.)false
.false
.true
.Consider the example tree shown above. The node id=8 has two children: id=7 and id=6. Node id=7 itself has three children: id=3, id=2, and id=1. Node id=6 has two children: id=5 and id=4. All nodes besides id=8 are descendants of id=8. The leaf nodes are id=3, id=2, id=1, id=5, and id=4. The interior nodes are id=8, id=7, and id=6.
This tree is a search tree: the values 10, 20, 30 stored in the subtree rooted at id=7 are less than the values 40, 50 stored in the subtree rooted at id=6. The search tree is valid: the value 30 of interior node id=7 is an upper bound for the values 10, 20, 30 stored by the subtree rooted in that node. The values of interior nodes id=8 and id=6 have no meaning because they have no next sibling.
The example tree shown above is perfectly balanced and two-three.
Notice that if a forest is two-three and has at most three trees, then checking whether the forest has a leaf with a given value, adding a given value, and removing a given value take time proportional to the height of the forest in the worst case. Furthermore, if the forest is perfectly balanced as well, the number of leaves in the forest is at least 2^(H-1) (rounded down), where H is the height of the forest, or, in other words, the height of the forest is at most the 2-logarithm of the number of leaves plus 1. That means that the lookup, add, and remove operations take time logarithmic in the number of leaves.