mirror of
https://github.com/tu-darmstadt-informatik/AuD18.git
synced 2025-12-13 09:55:49 +00:00
Add lots of documentation
This commit is contained in:
parent
eec221506b
commit
c55a7ecb6d
@ -5,59 +5,114 @@ import frame.SortArray;
|
|||||||
/**
|
/**
|
||||||
* Abstract superclass for the Quicksort algorithm.
|
* Abstract superclass for the Quicksort algorithm.
|
||||||
*
|
*
|
||||||
|
* Note: I got into a small optimization competition with a fellow student, trying
|
||||||
|
* to get read and write ops to an absolute minimum. While this code does not include
|
||||||
|
* most of the write optimizations for the sake of legibility, there is a fair bit of
|
||||||
|
* passing local variables between functions to save a few read ops.
|
||||||
|
* I do believe this to be well within the rules of the assignment as values are not
|
||||||
|
* cached in an array-like structure or passed between recursion levels.
|
||||||
|
* All optimizations merely cut down on duplicate / unnecessary reads and writes.
|
||||||
|
*
|
||||||
* @author NAJI
|
* @author NAJI
|
||||||
|
* @author Nils Rollshausen
|
||||||
*/
|
*/
|
||||||
public abstract class QuickSort {
|
public abstract class QuickSort {
|
||||||
|
|
||||||
// DO NOT modify this method
|
// DO NOT modify this method
|
||||||
public abstract void Quicksort(SortArray records, int left, int right);
|
public abstract void Quicksort(SortArray records, int left, int right);
|
||||||
|
|
||||||
// You may add additional methods here
|
/**
|
||||||
|
* Partitions an interval of the SortArray so that elements of the left part are <= pivot
|
||||||
|
* and elements on the right are >= pivot. This variant does not use any pre-cached values for array bounds
|
||||||
|
* @param pivot The value of the pivot element
|
||||||
|
* @param records The SortArray to partition
|
||||||
|
* @param leftBound Left bound index of the interval to partition
|
||||||
|
* @param rightBound Right bound index of the interval to partition
|
||||||
|
* @return index of the partition
|
||||||
|
*/
|
||||||
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound) {
|
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound) {
|
||||||
|
|
||||||
if(leftBound == rightBound)
|
if(leftBound == rightBound)
|
||||||
return leftBound;
|
return leftBound;
|
||||||
|
|
||||||
|
// Lookup bound values
|
||||||
SortingItem leftValue = records.getElementAt(leftBound);
|
SortingItem leftValue = records.getElementAt(leftBound);
|
||||||
SortingItem rightValue = records.getElementAt(rightBound);
|
SortingItem rightValue = records.getElementAt(rightBound);
|
||||||
|
|
||||||
|
// Use actual implementation with pre-cached bound values
|
||||||
return partition(pivot, records, leftBound, rightBound, leftValue, rightValue);
|
return partition(pivot, records, leftBound, rightBound, leftValue, rightValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partitions an interval of the SortArray so that elements of the left part are <= pivot
|
||||||
|
* and elements on the right are >= pivot. As the value of the leftmost array element is
|
||||||
|
* equal to the pivot value when using said element as the pivot, we can avoid reading that
|
||||||
|
* value from the array _again_ by passing it as an additional parameter
|
||||||
|
* @param pivot The value of the pivot element
|
||||||
|
* @param records The SortArray to partition
|
||||||
|
* @param leftBound Left bound index of the interval to partition
|
||||||
|
* @param rightBound Right bound index of the interval to partition
|
||||||
|
* @param leftValue Value of the element at index leftBound
|
||||||
|
* @return index of the partition
|
||||||
|
*/
|
||||||
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound, SortingItem leftValue) {
|
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound, SortingItem leftValue) {
|
||||||
|
|
||||||
if(leftBound == rightBound)
|
if(leftBound == rightBound)
|
||||||
return leftBound;
|
return leftBound;
|
||||||
|
|
||||||
|
// Look up the right bound value
|
||||||
SortingItem rightValue = records.getElementAt(rightBound);
|
SortingItem rightValue = records.getElementAt(rightBound);
|
||||||
|
|
||||||
|
// Use actual implementation with pre-cached bounds
|
||||||
return partition(pivot, records, leftBound, rightBound, leftValue, rightValue);
|
return partition(pivot, records, leftBound, rightBound, leftValue, rightValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partitions an interval of the SortArray so that elements of the left part are <= pivot
|
||||||
|
* and elements on the right are >= pivot. As the value of the bounding array elements is
|
||||||
|
* already known in some usecases, we can avoid reading these values from the array again
|
||||||
|
* by passing them as additional parameters
|
||||||
|
* @param pivot The value of the pivot element
|
||||||
|
* @param records The SortArray to partition
|
||||||
|
* @param leftBound Left bound index of the interval to partition
|
||||||
|
* @param rightBound Right bound index of the interval to partition
|
||||||
|
* @param leftValue Value of the element at index leftBound
|
||||||
|
* @param rightValue Value of the element at index rightBound
|
||||||
|
* @return index of the partition
|
||||||
|
*/
|
||||||
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound, SortingItem leftValue, SortingItem rightValue) {
|
protected int partition(SortingItem pivot, SortArray records, int leftBound, int rightBound, SortingItem leftValue, SortingItem rightValue) {
|
||||||
|
|
||||||
if(leftBound == rightBound)
|
|
||||||
return leftBound;
|
|
||||||
|
|
||||||
int leftIndex = leftBound;
|
int leftIndex = leftBound;
|
||||||
int rightIndex = rightBound;
|
int rightIndex = rightBound;
|
||||||
SortingItem temp;
|
SortingItem temp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using the Hoare partitioning scheme here as opposed to the one presented in the lecture
|
||||||
|
* for approximately three times less swap operations compared to the Lomuto scheme
|
||||||
|
* See https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme for details
|
||||||
|
* Essentially, increment / decrement left / right indices until a pair of values that are
|
||||||
|
* the wrong part of the list is found, then swap that pair and continue
|
||||||
|
*/
|
||||||
while(true){
|
while(true){
|
||||||
|
// As long as the items at left index are smaller than the pivot, they are on the right side
|
||||||
while(leftIndex < rightBound && leftValue.compareTo(pivot) < 0) {
|
while(leftIndex < rightBound && leftValue.compareTo(pivot) < 0) {
|
||||||
leftIndex += 1;
|
leftIndex += 1;
|
||||||
leftValue = records.getElementAt(leftIndex);
|
leftValue = records.getElementAt(leftIndex);
|
||||||
}
|
}
|
||||||
|
// Same for items at rightIndex larger than the pivot
|
||||||
while(rightIndex > leftBound && rightValue.compareTo(pivot) > 0) {
|
while(rightIndex > leftBound && rightValue.compareTo(pivot) > 0) {
|
||||||
rightIndex -= 1;
|
rightIndex -= 1;
|
||||||
rightValue = records.getElementAt(rightIndex);
|
rightValue = records.getElementAt(rightIndex);
|
||||||
}
|
}
|
||||||
|
// As soon as both indices cross, no more swaps have to be made and the partition is complete
|
||||||
if(leftIndex >= rightIndex) {
|
if(leftIndex >= rightIndex) {
|
||||||
return rightIndex;
|
return rightIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the indices are not equal (or crossed over), swap the two items that are in the wrong spot
|
||||||
records.setElementAt(leftIndex, rightValue);
|
records.setElementAt(leftIndex, rightValue);
|
||||||
records.setElementAt(rightIndex, leftValue);
|
records.setElementAt(rightIndex, leftValue);
|
||||||
|
// Also swap the local copies
|
||||||
temp = rightValue;
|
temp = rightValue;
|
||||||
rightValue = leftValue;
|
rightValue = leftValue;
|
||||||
leftValue = temp;
|
leftValue = temp;
|
||||||
|
|||||||
@ -8,24 +8,23 @@ public class QuickSortA extends QuickSort {
|
|||||||
* Quicksort algorithm implementation to sort a SorrtArray by choosing the
|
* Quicksort algorithm implementation to sort a SorrtArray by choosing the
|
||||||
* pivot as the first (leftmost) element in the list
|
* pivot as the first (leftmost) element in the list
|
||||||
*
|
*
|
||||||
* @param records
|
* @param records List of elements to be sorted as a SortArray
|
||||||
* - list of elements to be sorted as a SortArray
|
* @param left The index of the left bound for the algorithm
|
||||||
* @param left
|
* @param right The index of the right bound for the algorithm
|
||||||
* - the index of the left bound for the algorithm
|
|
||||||
* @param right
|
|
||||||
* - the index of the right bound for the algorithm
|
|
||||||
* @return Returns the sorted list as SortArray
|
* @return Returns the sorted list as SortArray
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void Quicksort(SortArray records, int left, int right) {
|
public void Quicksort(SortArray records, int left, int right) {
|
||||||
// implement the Quicksort A algorithm to sort the records
|
// Lists of length 1 or smaller are trivially sorted
|
||||||
// (choose the pivot as the first (leftmost) element in the list)
|
|
||||||
|
|
||||||
if(right - left < 1)
|
if(right - left < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Use the leftmost element as the pivot
|
||||||
SortingItem pivot = records.getElementAt(left);
|
SortingItem pivot = records.getElementAt(left);
|
||||||
|
// Pass the value of the pivot as the leftValue to partition to save an array read
|
||||||
int p = partition(pivot, records, left, right, pivot);
|
int p = partition(pivot, records, left, right, pivot);
|
||||||
|
|
||||||
|
// Sort the two sub-lists recursively (p being the position of the partition)
|
||||||
Quicksort(records, left, p);
|
Quicksort(records, left, p);
|
||||||
Quicksort(records, p+1, right);
|
Quicksort(records, p+1, right);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,52 +8,64 @@ public class QuickSortB extends QuickSort {
|
|||||||
* Quicksort algorithm implementation to sort a SorrtArray by choosing the
|
* Quicksort algorithm implementation to sort a SorrtArray by choosing the
|
||||||
* pivot as the median of the elements at positions (left,middle,right)
|
* pivot as the median of the elements at positions (left,middle,right)
|
||||||
*
|
*
|
||||||
* @param records
|
* @param records List of elements to be sorted as a SortArray
|
||||||
* - list of elements to be sorted as a SortArray
|
* @param left The index of the left bound for the algorithm
|
||||||
* @param left
|
* @param right The index of the right bound for the algorithm
|
||||||
* - the index of the left bound for the algorithm
|
|
||||||
* @param right
|
|
||||||
* - the index of the right bound for the algorithm
|
|
||||||
* @return Returns the sorted list as SortArray
|
* @return Returns the sorted list as SortArray
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void Quicksort(SortArray records, int left, int right) {
|
public void Quicksort(SortArray records, int left, int right) {
|
||||||
// implement the Quicksort B algorithm to sort the records
|
// Lists of length 1 or smaller are trivially sorted
|
||||||
// (choose the pivot as the median value of the elements at position
|
|
||||||
// (left (first),middle,right(last)))
|
|
||||||
if(right - left < 1)
|
if(right - left < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Get the left and right bounding values (for median calculation)
|
||||||
SortingItem leftValue = records.getElementAt(left);
|
SortingItem leftValue = records.getElementAt(left);
|
||||||
SortingItem rightValue = records.getElementAt(right);
|
SortingItem rightValue = records.getElementAt(right);
|
||||||
|
|
||||||
|
// Calculate the median pivot of left, right and middle
|
||||||
SortingItem pivot = getPivot(left, right, records, leftValue, rightValue);
|
SortingItem pivot = getPivot(left, right, records, leftValue, rightValue);
|
||||||
|
|
||||||
|
// Pass the already looked-up left and right values to partition to save two reads
|
||||||
int p = partition(pivot, records, left, right, leftValue, rightValue);
|
int p = partition(pivot, records, left, right, leftValue, rightValue);
|
||||||
|
|
||||||
|
// Sort the two sub-lists recursively (p being the position of the partition)
|
||||||
Quicksort(records, left, p);
|
Quicksort(records, left, p);
|
||||||
Quicksort(records, p+1, right);
|
Quicksort(records, p+1, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get the median pivot out of the leftmost, rightmost and middle element of an array
|
||||||
|
* @param left Leftmost index
|
||||||
|
* @param right Rightmost index
|
||||||
|
* @param records The Array to work on
|
||||||
|
* @param l The value of the array at the left index
|
||||||
|
* @param r The value of the array at the right index
|
||||||
|
* @return The value of the chosen median pivot
|
||||||
|
*/
|
||||||
private SortingItem getPivot(int left, int right, SortArray records, SortingItem l, SortingItem r) {
|
private SortingItem getPivot(int left, int right, SortArray records, SortingItem l, SortingItem r) {
|
||||||
|
// Calculate the index of the middle element (rounded down)
|
||||||
int mIndex = (int) Math.floor(left + (right - left) / 2);
|
int mIndex = (int) Math.floor(left + (right - left) / 2);
|
||||||
|
|
||||||
|
// If the array has only two items, the left item is the middle item so we don't have to look it up again
|
||||||
SortingItem m = (mIndex == left) ? l : records.getElementAt(mIndex);
|
SortingItem m = (mIndex == left) ? l : records.getElementAt(mIndex);
|
||||||
|
|
||||||
SortingItem t = null;
|
SortingItem t = null;
|
||||||
|
|
||||||
// 'Sort' the three elements by doing two swaps if necessary, then return the middle (= median) one
|
// The median element is the middle element of the sorted three element list of left, middle and right
|
||||||
|
// 'Sort' the three elements by doing two swaps if necessary, then return the middle one
|
||||||
if(l.compareTo(m) > 0) {
|
if(l.compareTo(m) > 0) { // (if l is larger than m, l and m need to be swapped)
|
||||||
t = l;
|
t = l;
|
||||||
l = m;
|
l = m;
|
||||||
m = t;
|
m = t;
|
||||||
}
|
}
|
||||||
|
if(r.compareTo(m) < 0) { // (if r is smaller than the new m, r and m need to be swapped)
|
||||||
if(r.compareTo(m) < 0) {
|
|
||||||
t = r;
|
t = r;
|
||||||
r = m;
|
r = m;
|
||||||
m = t;
|
m = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
return m;
|
return m; // return the new m
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ package lab;
|
|||||||
*
|
*
|
||||||
* This class represents one entry of the list that has to be sorted.
|
* This class represents one entry of the list that has to be sorted.
|
||||||
*
|
*
|
||||||
|
* Added Comparable interface to be able to, well, compare items
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public class SortingItem implements Comparable<SortingItem>{
|
public class SortingItem implements Comparable<SortingItem>{
|
||||||
|
|
||||||
@ -24,8 +26,11 @@ public class SortingItem implements Comparable<SortingItem>{
|
|||||||
this.Status = otherItem.Status;
|
this.Status = otherItem.Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compares self to another SortingItem, returns 0 if equal value, -1 if self is smaller, 1 if self is larger
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(SortingItem arg0) {
|
public int compareTo(SortingItem arg0) {
|
||||||
|
// If the BookSerialNumbers are equal, we return the result of the ReaderID comparison, otherwise comparing BookSerialNumbers is sufficient
|
||||||
|
// (string comparisons are lexicographical)
|
||||||
return this.BookSerialNumber.compareTo(arg0.BookSerialNumber) == 0 ? this.ReaderID.compareTo(arg0.ReaderID): this.BookSerialNumber.compareTo(arg0.BookSerialNumber);
|
return this.BookSerialNumber.compareTo(arg0.BookSerialNumber) == 0 ? this.ReaderID.compareTo(arg0.ReaderID): this.BookSerialNumber.compareTo(arg0.BookSerialNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user