CompSci-61B Lecture Topic 5
Ordering Values In Data Structures

Instructor: Prof. Robert Burns

Ref: Carrano ch.2, 10-15 and App.C
card applet
www.cs.ubc.ca sorting demos

Two Sorting Paradigms
maintaining ordered data structures
  "insertion" sort
  sorts with each insertion
ordering after-the-fact
  "selection" sort
  moves values from unsorted group to sorted
    one-at-a-time

The Selection Sort
swap pairs of values after values are stored
uses nested for-loops: O(n2)

for (i = 0; i < n; i++)
  for (j = i + 1; j < n; j++)
    if (a.getEntry(j).compareTo(a.getEntry(i)) < 0)
      swap values at i and j
The Insertion Sort
inserts values, moving to make space
click here for algorithm
uses nested for-loops: O(n2)

Sorting Faster Than O(n2)
do fewer comparisons
do fewer swaps
classic algorithms exist for:
  array vs. linked list
  ordered vs. unordered data

The Shell Sort
attempts to minimize swaps vs insertion sort
click here for algorithm
Analysis: O(n1.25)
...does not adapt well to linked lists!
  because i += d does not translate well

Writing Generic Methods
in separate "utility" class that works with all MyLists
  using MyList's size, getEntry, and replace methods
adding to "MyUtilities" class (e.g.:)
public static <T> void shellSort(MyList<T> a)
but: have to designate T as allowing "compareTo":
  replace 1st T with T extends Comparable<T>
in case values are from derived classes:
  replace new <T> with <? super T>
  indexed, so not for ordered data structures
requires that "compareTo" be defined for T
  it is for Integer and String,...

The "Radix Sort" Algorithm
radix sort (ref)

Divide-And-Conquer Sorting
since running time is such a strong function of size,
  sort smaller lists and merge them
classic examples: merge sort & quick sort (and radix sort)
versions for unordered arrays and linked-lists
recursive, by nature!

The "Merge Sort" Algorithm
divide values into separate collections
subdivide each collection
repeat subdividing until each collection has size=1
merge collections into one big collection
suitable for arrays and linked-lists
for lists with more than one value:
click here for algorithm

The "Quick Sort" Algorithm
divide values into a pivot and 2 separate collections
swap values so that:
  ...all values in 1st collection are less than the pivot
  ...all values in 2nd collection are greater
concatenate 1st collection, pivot, and 2nd collection
recurse 1st and 2nd collections until
  ...each has size=1
suitable for arrays and linked-lists
click here for linked list algorithm
click here for array algorithm

Writing Separate Algorithms For Array-based and Linked
this is what the "identification interfaces" are for!

public static <T> void mergeSort(MyList<T> a)
{
  if (a instanceof MyLinkedDataStructure)
    mergeSortL(a); // a linked-list version
  else
    throw new UnsupportedOperationException(...);
}

public static <T> void quickSort(MyList<T> a)
{
  if (a instanceof MyArrayBasedDataStructure)
    quickSortA(a, 0, a.size() - 1);
  else if (a instanceof MyLinkedDataStructure)
    quickSortL(a);
  else
    throw new UnsupportedOperationException(...);
}

use private recursive methods called by these public methods
and actually it's <T extends Comparable<? super T>>
  instead of the 1st <T>

Sets: Permanently Sorted Data Structures
indexed "add" mutator no longer supported
"add-to-the-end" mutator modified to seek insertion point
new not-indexed "remove" mutator seeks matching value's index
a useful private method: private int getIndex(T value)


Zero-based getIndex methods, vs. Carrano chapter 13 position-based getPosition:
  // using 0-based indexes instead of 1-based positions: +(nValues+1) for where value should have been found but wasn't
  private int getIndex(T value) // O(n)
  {
    assert value != null; // do not allow null values to be added
  
    int index = 0;
    for (; index < size(); index++) // traverse with an index-based getEntry
    {
      int compare = value.compareTo(getEntry(index));
      if (compare < 0) // value should have appeared here
        index += size() + 1; // mark as "should have appeared here"
      if (compare <= 0) break; // no need to look further
    }

    return index;
  }

  // using 0-based indexes instead of 1-based positions: +(nValues+1) for where value should have been found but wasn't
  private int getIndex(T value) // O(n)
  {
    assert value != null; // do not allow null values to be added
  
    int index = 0;
    for (T dataValue: this) // traverse with an iterator
    {
      int compare = value.compareTo(dataValue);
      if (compare < 0) // value should have appeared here
        index += size() + 1; // mark as "should have appeared here"
      if (compare <= 0) break; // no need to look further
      ++index;
    }

    return index;
  }
Instead of negative return values to indicate no-match-found as in Carrano, these methods add size+1 to the return value.
An Interface For A "Set"
public interface MySortedList<T extends Comparable<? super T>> extends MyList<T>
{
  public boolean remove(T value); // removes 1st instance of matching reference
}
public boolean add(T value) is already in "MyList", but needs to be redefined so as to not add to the end. public boolean add(int, T value) exists in "MyList", but cannot be used. Nor can public boolean replace(int, T value), because they can upset the sortedness of the data structure. So we either have to make a new interface that does not derive from "MyList", or do this:
public boolean add(int index, T value)
{
  throw new UnsupportedOperationException(getClass().getName() + ".add(int, T) is not supported");
}

public boolean replace(int index, T value)
{
  throw new UnsupportedOperationException(getClass().getName() + ".replace(int, T) is not supported");
}

[ Home | Contact Prof. Burns ]