HW10 - james-bern/CS136 GitHub Wiki

image

README

Background

For an array/list with $n$ elements...

  • time complexity is how long an algorithm takes to run
  • space complexity is how much extra space an algorithm takes to run (the input array does NOT count)
    • if the algorithm is in-place (just uses swaps) then it is $O(1)$ space
    • if we had to build a work array of $n$ elements then it is $O(n)$ space

Overview

In this lab, you will get a feel for sorting and searching by implementing some little functions.

  • To search an array/list means to find a value in it (and return its index; return -1 if not there).
  • To sort an array/list means to rearrange its elements so they are in ascending order (get bigger from left to right.)

The lab will crescendo in two sorting algorithms that work by building data structures.

  • To do tree sort...
    1. Build a binary search tree (sorted binary tree).
    2. Traverse it to read out the elements in sorted order (and store them in an array/list).
  • To do heapsort...
    1. Build an (implicit) max binary heap.
    2. Repeatedly remove the largest element to build up a sorted array.
      • Recall that the max heap interface's remove() function removes and returns the max (largest) element in the heap.

TODO's

  • A-

    • Update Cow.
    • Implement and document (fill in the time and space complexities of) all the functions other than treeSort and heapSort.
    • IMPORTANT: Copy and paste the autograder output into a comment at the top of your code file once you're done with everything.
  • A

    • Implement and document treeSort.
  • A+

  • A++

    • Create a maze generator that uses some sort of (randomized) depth first traversal and draw it using Cow.

Starter Code

import java.util.*;
class HW10 extends Cow {

	public static void main(String[] arguments) {
		// NOTE: Feel free to write your own tests in main

		// int[] array = { 9, 4, 0, 1, 7, 6, 8, 3 }; 
		// PRINT(array);

		// BinarySearchTree bst = new BinarySearchTree();
		// bst.root = new Node();
		// bst.root.value = 7;
		// PRINT(bst);

		runAutograder();
	}


	// Returns whether the arrays A and B have all the exact same elements
	// in the exact same order.
	// 
	// Worst-case Time: O(?)
	// Worst-case Space: O(?)
	static boolean areEqual(int[] A, int[] B) {
		// TODO

		return false;
	}


	// Returns whether the array is sorted.
	// The empty array is considered to be sorted.
	// NOTE: For us, sorted means "in ascending order."
	// 
	// Worst-case Time: O(?)
	// Worst-case Space: O(?)
	static boolean isSorted(int[] array) {
		// TODO

		return false;
	}


	// Returns the index of the first element in array with this value.
	// Returns -1 if no such element exists.
	// 
	// Worst-case Time: O(?)
	// Worst-case Space: O(?)
	static int linearSearch(int[] array, int value) {
		// TODO

		return -1;
	}


	// Returns the index of an element in array with this value.
	// Returns -1 if no such element exists.
	// NOTE: this function assumes the array the sorted
        //       but should NOT check that it's sorted
        //       (checking would ruin the runtime)
	// 
	// Worst-case Time: O(?)
	// Worst-case Space: O(?)
	static int binarySearch(int[] array, int value) {
		// TODO

		return -1;
	}


	// Sorts the array in-place using bubble sort.
	// NOTE: You best-case runtime for bubble sort
        //       must be better than your worst-case runtime.
	// 
	// Worst-case Time: O(?)
	// Best-case Time: O(?)
	// Worst-case Space: O(?)
	static void bubbleSort(int[] array) {
		// TODO
		// HINT: Look up bubble sort on Wikipedia.

	}



	// Sorts the array using (out-of-place) tree sort.
	//
	// 1) Build a BinarySearchTree object out of the array.
	//
	//    No work-arounds allowed.
	//    You MUST implement an add function, and use it something like this:
	//
	//    for (int element : array) {
	//        binarySearchTree.add(element);
	//    }
	//
	// 2) Traverse the binary search tree
	//    (using the appropriate traversal order)
	//    to gather the elements in sorted order.
	//
	// NOTE: You can (and should) write more functions.
	// NOTE: You must document all functions you write.
	// 
	// Worst-case Time: O(?) // NOTE: Our BST is NOT self-balancing :(
	// Worst-case Space: O(?)
	static void treeSort(int[] array) {
		// TODO

		// HINT: You can print a BinarySearchTree bst; like this:
		// PRINT(bst);

	}

	static class Node {
		int value;
		Node leftChild;
		Node rightChild;
	}

	static class BinarySearchTree {
		Node root;

		void add(int value) {
			// TODO

			// HINT: You can print this BinarySearchTree like this:
			// PRINT(this);

		}
	}



	// Sort the array using (in-place) implicit heap sort (swaps only).
	//
	// 1) "Heapify" the array.
	//
	//    (Turn the array into an implicit max binary heap using swaps.)
	//
	//    [ implicit-heap ]
	//
	//
	// 2) Repeatedly swap the last node/element of the heap with
	//    the 0-th element/max element/root of the heap,
	//    then sink it down into the remaining heap.
	//
	//    The max element is "removed" into the right side of the array,
	//    where the sorted array is built up incrementally.
	//
	//    [ implicit-heap | sorted-array ]
	// 
	// Worst-case Time: O(?)
	// Worst-case Space: O(?)
	static void heapSort(int[] array) {
		// TODO

	}



	////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////
	// YOUR CODE ENDS //////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////



	static void runAutograder() {
		// indices  0  1  2  3  4  5  6  7
		int[] U = { 9, 4, 0, 1, 7, 6, 8, 3 }; // Unsorted
		int[] S = { 0, 1, 3, 4, 6, 7, 8, 9 }; // Sorted
		int[] L = { 0, 1, 3, 4, 6, 7, 8    }; // different Length
		int[] V = { 0, 1, 3, 4, 5, 7, 8, 9 }; // different Values
		int[] O = { 0 };                      // One element
		int[] E = {};                         // Empty

		System.out.println("\nRUNNING AUTOGRADER");
		System.out.println(  "------------------");

		PRINT("NOTE: Many of these tests assume your areEqual(...) implementation is perfect.");

		System.out.print("\nareEqual");
		_GRADE(areEqual(S, S.clone()) == true);
		_GRADE(areEqual(S, U) == false);
		_GRADE(areEqual(S, V) == false);
		_GRADE(areEqual(S, L) == false);
		_GRADE(areEqual(S, E) == false);
		_GRADE(areEqual(E, E) == true);

		System.out.print("\nisSorted");
		_GRADE(isSorted(S) == true);
		_GRADE(isSorted(U) == false);
		_GRADE(isSorted(O) == true);
		_GRADE(isSorted(E) == true);

		System.out.print("\nlinearSearch");
		_GRADE(linearSearch(S, 0) == 0);
		_GRADE(linearSearch(S, 6) == 4);
		_GRADE(linearSearch(S, 9) == 7);
		_GRADE(linearSearch(S, 5) == -1);
		_GRADE(linearSearch(S, -10) == -1);
		_GRADE(linearSearch(O, 0) == 0);
		_GRADE(linearSearch(O, 5) == -1);
		_GRADE(linearSearch(E, 5) == -1);
		_GRADE(linearSearch(U, 9) == 0);

		System.out.print("\nbinarySearch");
		_GRADE(binarySearch(S, 0) == 0);
		_GRADE(binarySearch(S, 6) == 4);
		_GRADE(binarySearch(S, 9) == 7);
		_GRADE(binarySearch(S, 5) == -1);
		_GRADE(binarySearch(S, -10) == -1);
		_GRADE(binarySearch(O, 0) == 0);
		_GRADE(binarySearch(O, 5) == -1);
		_GRADE(binarySearch(E, 5) == -1);


		System.out.print("\nbubbleSort");
		{
			int[] U_clone = U.clone();
			bubbleSort(U_clone);
			_GRADE(areEqual(S, U_clone) == true);
		}


		System.out.print("\ntreeSort");
		{
			int[] U_clone = U.clone();
			treeSort(U_clone);
			_GRADE(areEqual(S, U_clone) == true);
		}

		System.out.print("\nheapSort");
		{
			int[] U_clone = U.clone();
			heapSort(U_clone);
			_GRADE(areEqual(S, U_clone) == true);
		}
	}

	static void _GRADE(boolean shouldBeTrue) {
		System.out.print(shouldBeTrue ? '+' : '-');
	}

	static void PRINT(BinarySearchTree bst) { HW10_printBinaryTree(bst.root); }

}

Solution

👀
import java.util.*;
class HW10A extends Cow {

	static void runAutograder() {
		// indices  0  1  2  3  4  5  6  7
		int[] U = { 9, 4, 0, 1, 7, 6, 8, 3 }; // Unsorted
		int[] S = { 0, 1, 3, 4, 6, 7, 8, 9 }; // Sorted
		int[] L = { 0, 1, 3, 4, 6, 7, 8    }; // different Length
		int[] V = { 0, 1, 3, 4, 5, 7, 8, 9 }; // different Values
		int[] O = { 0 };                      // One element
		int[] E = {};                         // Empty
		System.out.println("NOTE: Later tests use your areEqual(...) implementation.");

		System.out.println("\nRUNNING AUTOGRADER");
		System.out.print  ("------------------");

		System.out.print("\nareEqual");
		_grade(areEqual(S, S.clone()) == true);
		_grade(areEqual(S, U) == false);
		_grade(areEqual(S, V) == false);
		_grade(areEqual(S, L) == false);
		_grade(areEqual(S, E) == false);
		_grade(areEqual(E, E) == true);

		System.out.print("\nisSorted");
		_grade(isSorted(S) == true);
		_grade(isSorted(U) == false);
		_grade(isSorted(O) == true);
		_grade(isSorted(E) == true);

		System.out.print("\nlinearSearch");
		_grade(linearSearch(S, 0) == 0);
		_grade(linearSearch(S, 6) == 4);
		_grade(linearSearch(S, 9) == 7);
		_grade(linearSearch(S, 5) == -1);
		_grade(linearSearch(S, -10) == -1);
		_grade(linearSearch(O, 0) == 0);
		_grade(linearSearch(O, 5) == -1);
		_grade(linearSearch(E, 5) == -1);
		_grade(linearSearch(U, 9) == 0);

		System.out.print("\nbinarySearch");
		_grade(binarySearch(S, 0) == 0);
		_grade(binarySearch(S, 6) == 4);
		_grade(binarySearch(S, 9) == 7);
		_grade(binarySearch(S, 5) == -1);
		_grade(binarySearch(S, -10) == -1);
		_grade(binarySearch(O, 0) == 0);
		_grade(binarySearch(O, 5) == -1);
		_grade(binarySearch(E, 5) == -1);


		System.out.print("\nbubbleSort");
		{
			int[] U_clone = U.clone();
			bubbleSort(U_clone);
			_grade(areEqual(S, U_clone) == true);
		}


		System.out.print("\ntreeSort");
		{
			int[] U_clone = U.clone();
			treeSort(U_clone);
			// System.out.println(Arrays.toString(U_clone)); 
			_grade(areEqual(S, U_clone) == true);
		}

		System.out.print("\nheapSort");
		{
			int[] U_clone = U.clone();
			heapSort(U_clone);
			_grade(areEqual(S, U_clone) == true);
		}
	}

	static void _grade(boolean shouldBeTrue) {
		char c;
		if (shouldBeTrue) {
			c = '+';
		} else {
			c = '-';
		}
		System.out.print(c);
	}

	////////////////////////////////////////////////////////////////////////////////
	// YOUR CODE STARTS HERE ///////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////////


	public static void main(String[] arguments) {
		// int[] array = { 9, 4, 0, 1, 7, 6, 8, 3 }; 
		// System.out.println(Arrays.toString(array));

		runAutograder();
	}

	// Get whether the arrays A and B have all the exact same elements
	// in the exact same order.
	// 
	// Worst-case Time: O(n)
	// Worst-case Space: O(1)
	static boolean areEqual(int[] A, int[] B) {
		if (A.length != B.length) return false;
		for (int i = 0; i < A.length; ++i) {
			if (A[i] != B[i]) return false;
		}
		return true;
	}

	// Get whether the array is sorted.
	// The empty array is considered to be sorted.
	// NOTE: For us, sorted means "in ascending order."
	// 
	// Worst-case Time: O(n)
	// Worst-case Space: O(1)
	static boolean isSorted(int[] array) {
		for (int i = 0; i < array.length - 1; ++i) {
			int j = i + 1;
			if (array[i] > array[j]) return false;
		}
		return true;
	}

	// Get the index of the first element in array with this value.
	// Returns -1 if no such element exists.
	// 
	// Worst-case Time: O(n)
	// Worst-case Space: O(1)
	static int linearSearch(int[] array, int value) {
		for (int i = 0; i < array.length; ++i) {
			if (array[i] == value) {
				return i;
			}
		}
		return -1;
	}

	// Get the index of the first element in array with this value.
	// Returns -1 if no such element exists.
	// NOTE!: array must be sorted; otherwise returns nonsense.
	// 
	// Worst-case Time: O(log n)
	// Worst-case Space: O(1)
	static int binarySearch(int[] array, int value) {
		if (array.length == 0) return -1;
		int i = 0;
		int j = array.length - 1;
		int k = -1;
		do {
			k = (i + j) / 2;
			if (value == array[k]) {
				return k;
			} else if (value < array[k]) {
				j = k - 1;
			} else {
				i = k + 1;
			}
		} while (i <= j);
		return -1;
	}


	// Sorts the array in-place using bubble sort.
	// NOTE: Best-case runtime is better than worst-case runtime.
	// 
	// Worst-case Time: O(n^2)
	// Best-case Time: O(n)
	// Worst-case Space: O(1)
	static void bubbleSort(int[] array) {
		// HINT: Look up bubble sort on Wikipedia.
		for (int rep = 0; rep < array.length; ++rep) {
			boolean madeAnySwaps = false;
			for (int i = 0; i < (array.length - 1) - rep; ++i) {
				if (array[i] > array[i + 1]) {
					madeAnySwaps = true;
					int tmp = array[i];
					array[i] = array[i + 1];
					array[i + 1] = tmp;
				}
			}
			if (!madeAnySwaps) break;
		}
	}


	static class Node {
		int value;
		Node leftChild;
		Node rightChild;
	}

	static class BinarySearchTree {
		Node root;
		void add(int value) {
			Node newNode = new Node();
			newNode.value = value;
			if (root == null) {
				root = newNode;
				return;
			}
			Node curr = root;
			while (true) {
				if (value < curr.value) {
					if (curr.leftChild == null) {
						curr.leftChild = newNode;
						return;
					} else {
						curr = curr.leftChild;
					}
				} else {
					if (curr.rightChild == null) {
						curr.rightChild = newNode;
						return;
					} else {
						curr = curr.rightChild;
					}
				}
			}
		}
		ArrayList<Integer> gatherNodesIntoSortedArrayList() {
			ArrayList<Integer> result = new ArrayList<>();
			_gatherHelper(root, result);
			return result;
		}
		void _gatherHelper(Node curr, ArrayList<Integer> result) {
			if (curr.leftChild != null) _gatherHelper(curr.leftChild, result);
			result.add(curr.value);
			if (curr.rightChild != null) _gatherHelper(curr.rightChild, result);
		}
	}

	// Sort the array using (out-of-place) tree sort.
	//
	// 1) Build a BinarySearchTree object out of the array.
	//
	//    No work-arounds allowed.
	//    You MUST implement an add function, and use it like this:
	//
	//    for (int element : array) {
	//        binarySearchTree.add(element);
	//    }
	//
	// 2) Traverse the binary search tree
	//    (using the appropriate traversal order)
	//    to gather the elements in sorted order.
	//
	// NOTE: You can (and should) write more functions.
	// NOTE: You must document all functions.
	// 
	// Worst-case Time: O(n^2) NOTE: our tree is NOT self-balancing
	// Worst-case Space: O(n)
	static void treeSort(int[] array) {
		BinarySearchTree binarySearchTree = new BinarySearchTree();
		for (int element : array) {
			binarySearchTree.add(element);
			// HW10_printBinaryTree(binarySearchTree.root);
		}

		// HINT: Here is a function I wrote that can print your tree (or heap)
		//       Definitely print the tree to check your work!
		//       Try lots of different test cases!
		// HW10_printBinaryTree(binarySearchTree.root);

		// v This is meh, but fine :)
		ArrayList<Integer> tmp = binarySearchTree.gatherNodesIntoSortedArrayList();
		assert(array.length == tmp.size());
		for (int i = 0; i < array.length; ++i) array[i] = tmp.get(i);
	}

	static void heapSort(int[] array) {
	}

}
⚠️ **GitHub.com Fallback** ⚠️