How does the following quicksort method works? - c#

I wrote my own Quicksort method for educational purposes. In order to improve it, I took a look at .NET source code to see how to LINQ OrderBy() method is implemented.
I found the following Quicksort method :
void QuickSort(int[] map, int left, int right) {
do {
int i = left;
int j = right;
int x = map[i + ((j - i) >> 1)];
do {
while (i < map.Length && CompareKeys(x, map[i]) > 0) i++;
while (j >= 0 && CompareKeys(x, map[j]) < 0) j--;
if (i > j) break;
if (i < j) {
int temp = map[i];
map[i] = map[j];
map[j] = temp;
}
i++;
j--;
} while (i <= j);
if (j - left <= right - i) {
if (left < j) QuickSort(map, left, j);
left = i;
}
else {
if (i < right) QuickSort(map, i, right);
right = j;
}
} while (left < right);
}
I am trying to understand the inner workings.
AFAIK it looks very similar to Hoare partition scheme but with some slight optimisations.
What is unclear to me is :
Why do we recurse only one side of the pivot after partitionning ? (depending the result of the if (j - left <= right - i) )
Why do we have a do { ... } while (left < right) over the whole thing ? Is it because we recurse only one side of the pivot as suggested above ?
Why is there a if (i < j) conditional test before the swap ? Isn't the break; statement before enough?
In comparison, here is how my actual implementation Quicksort looks (straight implementation of Hoare partition scheme)
void QuickSort(int[] map, int left, int right)
{
if (left < right)
{
int i = left - 1;
int j = right + 1;
int x = map[left + ((right - left) >> 1)];
while (true)
{
do { i++; } while (Compare(map[i], x) < 0);
do { j--; } while (Compare(map[j], x) > 0);
if (i >= j) break;
int temp = map[i];
map[i] = map[j];
map[j] = temp;
}
QuickSort(map, left, j);
QuickSort(map, j + 1, right);
}
}

Why do we recurse only one side of the pivot after partitionning ?
(depending the result of the if (j - left <= right - i) )
To minimize recursion depth (and stack usage). When we treat larger partition as soon as possible and do recursion only for smaller partition, depth does not rise above log(n)
Why do we have a do { ... } while (left < right) over the whole thing ?
Items before left and after right are sorted, so these indexes meet when all array is sorted
Why is there a if (i < j) conditional test before the swap ?
Just to avoid unnecessary swap for equal indexes

Related

How can I prevent my Quicksort Algorithm from throwing a StackOverflowException

Im trying to write a quicksort algorithm in C#, and I've been getting System.StackOverflowExceptions for the last while and can't figure out why.
Here is my Class:
class quicksort : sortalgorithm
{
int pivotIndex = -1;
int pivotSwapped = 0;
Random rand = new Random();
public quicksort(int[] arr)
{
if (arr.Length < 5)
{
throw new Exception("Array has too few Entries");
}
toSort = arr;
}
protected override int sort()
{
if (pivotIndex == -1) getPivot();
int indexL = getIndexLeft();
int indexR = getIndexRight();
if (indexR != -1 && indexL != -1 && indexL != toSort.Length-1)
{
swap(indexL, indexR);
}
if (indexL > indexR)
{
Console.WriteLine("Index thingy");
swap(toSort.Length - 1, indexL);
pivotSwapped++;
if (pivotSwapped == toSort.Length - 1)
{
return 1;
}
else
{
Console.WriteLine("new piv");
pivotSwapped++;
getPivot();
sort();
return -1;
}
}
else
{
sort();
return -1;
}
}
private void swap(int i, int j)
{
int temp = toSort[i];
toSort[i] = toSort[j];
toSort[j] = temp;
}
private void getPivot()
{
pivotIndex = rand.Next(0, toSort.Length - 1);
swap(toSort.Length - 1, pivotIndex);
}
private int getIndexLeft() // Larger then Pivot Starting: Left
{ //Error Here
int itemFromLeft = -1;
for (int i = 0; i < toSort.Length - 1; i++)
{
if (toSort[i] >= toSort[toSort.Length - 1])
{
itemFromLeft = i;
i = toSort.Length + 1;
}
}
//Console.WriteLine(itemFromLeft);
return itemFromLeft;
}
private int getIndexRight()// Smaller then Pivot Starting: Right
{
int itemFromRight = -1;
for (int i = toSort.Length - 1; i >= 0; i--)
{
if (toSort[i] < toSort[toSort.Length - 1])
{
itemFromRight = i;
i = -1;
}
}
//Console.WriteLine(itemFromRight);
return itemFromRight;
}
}
I hope that someone can help me.
P.S. the class sortalgorithm in this case only has the the array toSort.
My Console output always looks a bit like this:
[4, 28, 62, 33, 11] // The unsorted array
Index thingy
new piv
Index thingy
new piv
Index thingy
new piv
Process is terminated due to `StackOverflowException`.
a stack overflow error usually happens when you have a error in your recursion , ie a function keeps calling itself until there is no room left in the stack to hold all the references,
the only obvious candidate is
**sort();**
return -1;
}
}
else
{
**sort();**
return -1;
}
so either one or the other recursive calls is probably incorrect and needs removing and i suspect the issue will be resolved
EDIT:
as you are unable to debug your code
this is what a quick sort should look like
public int sort()
{
qsort(0, toSort.Length - 1);
return 0;
}
private void qsort( int left, int right)
{
if (left < right)
{
int pivot = Partition( left, right);
if (pivot > 1)
{
qsort( left, pivot - 1);
}
if (pivot + 1 < right)
{
qsort( pivot + 1, right);
}
}
}
private int Partition( int left, int right)
{
int pivot = toSort[left];
while (true)
{
while (toSort[left] < pivot)
{
left++;
}
while (toSort[right] > pivot)
{
right--;
}
if (left < right)
{
if (toSort[left] == toSort[right]) return right;
int temp = toSort[left];
toSort[left] = toSort[right];
toSort[right] = temp;
}
else
{
return right;
}
}
}
Note that if left >= right do nothing
This simple implementation of Hoare partition scheme demonstrates how to avoid stack overflow by recursing on the smaller partition, and looping back for the larger partition, an idea used in the original quicksort. Stack space complexity is limited to O(log2(n)), but worst case time complexity is still O(n^2).
static public void Quicksort(int [] a, int lo, int hi)
{
while(lo < hi){ // while partition size > 1
int p = a[(lo + hi) / 2];
int i = lo, j = hi;
i--; // Hoare partition
j++;
while (true)
{
while (a[++i] < p) ;
while (a[--j] > p) ;
if (i >= j)
break;
int t = a[i];
a[i] = a[j];
a[j] = t;
}
i = j++; // i = last left, j = first right
if((i - lo) <= (hi - j)){ // if size(left) <= size(right)
Quicksort(a, lo, i); // recurse on smaller partition
lo = j; // and loop on larger partition
} else {
Quicksort(a, j, hi); // recurse on smaller partition
hi = i; // and loop on larger partition
}
}
}

Why is Parallel.Invoke much faster if the call is in a separate method?

I implemented the QuickSort-Algorithm 3 times and measured the time for sorting 50 million random numbers:
sequential (took ~14 seconds)
With Parallel.Invoke() in the same method as the sorting algorithm (took ~12 seconds)
With Parallel.Invoke() in a separate method (took ~7 seconds)
So my question is: Why is Parallel.Invoke() much faster if the call is in a separate method?
On my computer the 3. example was more than twice as fast as the 2.
2. With Parallel.Invoke() in the same method as the sorting algorithm
public class ParallelQuickSort
{
private const int Threshold = 100;
public static void Sort(int[] array)
{
if (array == null || array.Length == 0)
{
new ArgumentException("number array must be at least of length 1");
}
QuickSort(array, 0, array.Length - 1);
}
private static void QuickSort(int[] array, int left, int right)
{
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j)
{
while (array[i] < m) { i++; }
while (array[j] > m) { j--; }
if (i <= j)
{
var t = array[i]; array[i] = array[j]; array[j] = t;
i++; j--;
}
}
if (j - left > Threshold && right - i > Threshold)
{
Parallel.Invoke(
() => QuickSort(array, left, j),
() => QuickSort(array, i, right)
);
}
else
{
if (j > left) { QuickSort(array, left, j); }
if (i < right) { QuickSort(array, i, right); }
}
}
}
3. With Parallel.Invoke() in a separate method
public class ParallelSeparateMethodQuickSort
{
private const int Threshold = 100;
public static void Sort(int[] array)
{
if (array == null || array.Length == 0)
{
new ArgumentException("number array must be at least of length 1");
}
QuickSort(array, 0, array.Length - 1);
}
private static void QuickSort(int[] array, int left, int right)
{
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j)
{
while (array[i] < m) { i++; }
while (array[j] > m) { j--; }
if (i <= j)
{
var t = array[i]; array[i] = array[j]; array[j] = t;
i++; j--;
}
}
if (j - left > Threshold && right - i > Threshold)
{
ParallelInvoke(array, left, j, i, right);
}
else
{
if (j > left) { QuickSort(array, left, j); }
if (i < right) { QuickSort(array, i, right); }
}
}
private static void ParallelInvoke(int[] array, int left, int j, int i, int right)
{
Parallel.Invoke(
() => QuickSort(array, left, j),
() => QuickSort(array, i, right)
);
}
}
Full Code
using System;
using System.Threading.Tasks;
using System.Diagnostics;
namespace parallelQuicksort
{
class Program
{
static void Main(string[] args)
{
const int N = 50_000_000;
for (int i = 0; i < 5; i++)
{
var array = GetRandomArray(N);
Measure("Sequential\t", () => SequentialQuickSort.Sort(array));
array = GetRandomArray(N);
Measure("Parallel\t", () => ParallelQuickSort.Sort(array));
array = GetRandomArray(N);
Measure("P. Separate Method", () => ParallelSeparateMethodQuickSort.Sort(array));
}
}
private static int[] GetRandomArray(int length)
{
var random = new Random(4711);
var array = new int[length];
for (int i = 0; i < length; i++)
{
array[i] = random.Next();
}
return array;
}
public static void Measure(string name, Action action)
{
Stopwatch stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
var time = stopwatch.ElapsedMilliseconds;
Console.WriteLine($"{name}: \tElapsed={time}");
}
}
public class SequentialQuickSort
{
public static void Sort(int[] array)
{
if (array == null || array.Length == 0)
{
new ArgumentException("number array must be at least of length 1");
}
QuickSort(array, 0, array.Length - 1);
}
private static void QuickSort(int[] array, int left, int right)
{
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j)
{
while (array[i] < m) { i++; }
while (array[j] > m) { j--; }
if (i <= j)
{
var t = array[i]; array[i] = array[j]; array[j] = t;
i++; j--;
}
}
if (j > left) { QuickSort(array, left, j); }
if (i < right) { QuickSort(array, i, right); }
}
}
public class ParallelQuickSort
{
private const int Threshold = 100;
public static void Sort(int[] array)
{
if (array == null || array.Length == 0)
{
new ArgumentException("number array must be at least of length 1");
}
QuickSort(array, 0, array.Length - 1);
}
private static void QuickSort(int[] array, int left, int right)
{
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j)
{
while (array[i] < m) { i++; }
while (array[j] > m) { j--; }
if (i <= j)
{
var t = array[i]; array[i] = array[j]; array[j] = t;
i++; j--;
}
}
if (j - left > Threshold && right - i > Threshold)
{
Parallel.Invoke(
() => QuickSort(array, left, j),
() => QuickSort(array, i, right)
);
}
else
{
if (j > left) { QuickSort(array, left, j); }
if (i < right) { QuickSort(array, i, right); }
}
}
}
public class ParallelSeparateMethodQuickSort
{
private const int Threshold = 100;
public static void Sort(int[] array)
{
if (array == null || array.Length == 0)
{
new ArgumentException("number array must be at least of length 1");
}
QuickSort(array, 0, array.Length - 1);
}
private static void QuickSort(int[] array, int left, int right)
{
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j)
{
while (array[i] < m) { i++; }
while (array[j] > m) { j--; }
if (i <= j)
{
var t = array[i]; array[i] = array[j]; array[j] = t;
i++; j--;
}
}
if (j - left > Threshold && right - i > Threshold)
{
ParallelInvoke(array, left, j, i, right);
}
else
{
if (j > left) { QuickSort(array, left, j); }
if (i < right) { QuickSort(array, i, right); }
}
}
private static void ParallelInvoke(int[] array, int left, int j, int i, int right)
{
Parallel.Invoke(
() => QuickSort(array, left, j),
() => QuickSort(array, i, right)
);
}
}
}
You find my code here: https://github.com/Lazzaretti/ParallelQuicksort
Output
Sequential : Elapsed=14534
Parallel : Elapsed=11960
P. Separate Method: Elapsed=6353
Sequential : Elapsed=14620
Parallel : Elapsed=11954
P. Separate Method: Elapsed=6647
Sequential : Elapsed=14529
Parallel : Elapsed=11870
P. Separate Method: Elapsed=6389
Sequential : Elapsed=14512
Parallel : Elapsed=11787
P. Separate Method: Elapsed=6590
Sequential : Elapsed=16203
Parallel : Elapsed=11738
P. Separate Method: Elapsed=6674
After fixing that problem with sorting already sorted array mentioned in comments, your problem still reproduces.
I think the reason is how and what is captured by lambdas you pass to Parallel.Invoke.
In first case (when Parallel.Invoke is inside QuickSort method):
Parallel.Invoke(
() => QuickSort(array, left, j),
() => QuickSort(array, i, right)
);
You capture 5 variables, all of which are used throughout the whole QuickSort method. All those variables become fields of compiler generated class. That means whole QuickSort method now works with object fields and not local variables. If you decompile that method you'll see something like this:
var cDisplayClass20 = new SomeCompilerGeneratedClass();
cDisplayClass20.array = array;
cDisplayClass20.left = left;
cDisplayClass20.right = right;
cDisplayClass20.i = cDisplayClass20.left;
cDisplayClass20.j = cDisplayClass20.right;
int num1 = cDisplayClass20.array[(cDisplayClass20.left + cDisplayClass20.right) / 2];
while (cDisplayClass20.i <= cDisplayClass20.j) // field access
{
while (cDisplayClass20.array[cDisplayClass20.i] < num1) // field access
cDisplayClass20.i++;
while (cDisplayClass20.array[cDisplayClass20.j] > num1) // and again
cDisplayClass20.j--;
if (cDisplayClass20.i <= cDisplayClass20.j) // again field access
{
// they are everywhere
int num2 = cDisplayClass20.array[cDisplayClass20.i];
cDisplayClass20.array[cDisplayClass20.i] = cDisplayClass20.array[cDisplayClass20.j];
cDisplayClass20.array[cDisplayClass20.j] = num2;
cDisplayClass20.i++;
cDisplayClass20.j--;
}
}
Which confirms point above.
However if you move Parallel.Invoke to separate method, that is no longer the case. 5 variables are still captured, but that does not affect whole QuickSort method, because capture now happens inside separate ParallelInvoke method, and so is localized. The QuickSort still works with local variables and not fields of compiler generated class. If you decompile version with separate method, it will look exactly like you wrote:
int index1 = left;
int index2 = right;
int num1 = array[(left + right) / 2];
while (index1 <= index2) // local variables
{
while (array[index1] < num1) // num1 is local variable
++index1;
while (array[index2] > num1)
--index2;
if (index1 <= index2) // local variables again
{
int num2 = array[index1];
array[index1] = array[index2];
array[index2] = num2;
++index1;
--index2;
}
}
...
Now, I assume that accessing object fields (which are generally on heap) is somewhat slower than accessing local variables (which are generally on stack \ in CPU register), hence version with separate method is faster. Eric Lippert also notes in comments that:
The jitter will likely do a worse job with the fields than it will
with the locals because it will not enregister them as aggressively.
You can confirm the above by modifying your first version like this:
private static void QuickSort(int[] array, int left, int right) {
var i = left;
var j = right;
var m = array[(left + right) / 2];
while (i <= j) {
while (array[i] < m) {
i++;
}
while (array[j] > m) {
j--;
}
if (i <= j) {
var t = array[i];
array[i] = array[j];
array[j] = t;
i++;
j--;
}
}
if (j - left > Threshold && right - i > Threshold) {
// copy all variables you pass to lambda
// so that their capture does not affect the whole method
var tmpArray = array;
var tmpLeft = left;
var tmpJ = j;
var tmpI = i;
var tmpRight = right;
Parallel.Invoke(
() => QuickSort(tmpArray, tmpLeft, tmpJ),
() => QuickSort(tmpArray, tmpI, tmpRight)
);
}
else {
if (j > left) {
QuickSort(array, left, j);
}
if (i < right) {
QuickSort(array, i, right);
}
}
}
}
Then execution time of both approaches will be the same.
As #Eugene mentions in comments and in his answer - there might be other things that slow this down besides field vs local variable access - such as construction and (potentially) garbage collection of compiler-generated classes mentioned above. However, these are all consequences of the same source root - capturing local variables in closure in different ways.
!!!!!!!!! This answer is not actual for now !!!!!
I know this is not a proper answer, just want to make it visible:
try to change order of tests:
Measure("P. Separate Method", () => ParallelSeparateMethodQuickSort.Sort(array));
Measure("Sequential\t", () => SequentialQuickSort.Sort(array));
Measure("Parallel\t", () => ParallelQuickSort.Sort(array));
And you will see:
P. Separate Method: Elapsed=8710
Sequential : Elapsed=4140
Parallel : Elapsed=7928
P. Separate Method: Elapsed=9033
Sequential : Elapsed=4098
Parallel : Elapsed=7881
So I think your tests are wrong and this question does not make sense.
And quick investigation shows that in every of the tests you change your source array, so each next test has already sorted array.
p.s. But I think this question really exists. If you try to correct the code you will see, that calling separate method works faster!
p.p.s plz let me know if somebody has an answer or if question was corrected
There are two reasons for that: additional constructor call (~30% of time) and field-access instead of variable access (~30% of time). When you use enclosure directly inside your method the auto-generated class for that enclosure is being instantiated in each call of this method (which in your case leads to garbage collections, pic below).
And all calls to variables are now calls to fields also (which is slower) as stated by #Evk.
But when your enclosure is wrapped inside another method it is instantiated only when wrapper method was called. So in case of separated method enclosure object is being created only when if (j - left > Threshold && right - i > Threshold) was "true". As described by #Evk you can copy values to new variables declared inside if, it will give you same result as wrapping it in to a method.
I ran a profiler and got this (look at highlighted row):
This is fast separate method:
And this is slow method:
Also, look at the compiled version (note that in slow case we access fields, not variables):
//Compilation of "if (j - left > Threshold && right - i > Threshold)"
//Slow method:
// [106 13 - 106 63]
IL_012f: ldloc.0 // 'CS$<>8__locals0'
IL_0130: ldfld int32 parallelQuicksort.ParallelQuickSort/'<>c__DisplayClass2_0'::j
IL_0135: ldloc.0 // 'CS$<>8__locals0'
IL_0136: ldfld int32 parallelQuicksort.ParallelQuickSort/'<>c__DisplayClass2_0'::left
IL_013b: sub
IL_013c: ldc.i4.s 100 // 0x64
IL_013e: ble.s IL_0153
IL_0140: ldloc.0 // 'CS$<>8__locals0'
IL_0141: ldfld int32 parallelQuicksort.ParallelQuickSort/'<>c__DisplayClass2_0'::right
IL_0146: ldloc.0 // 'CS$<>8__locals0'
IL_0147: ldfld int32 parallelQuicksort.ParallelQuickSort/'<>c__DisplayClass2_0'::i
IL_014c: sub
IL_014d: ldc.i4.s 100 // 0x64
IL_014f: cgt
IL_0151: br.s IL_0154
IL_0153: ldc.i4.0
IL_0154: stloc.s V_8
//fast separate method
// [151 13 - 151 63]
IL_006b: ldloc.1 // j
IL_006c: ldarg.1 // left
IL_006d: sub
IL_006e: ldc.i4.s 100 // 0x64
IL_0070: ble.s IL_007b
IL_0072: ldarg.2 // right
IL_0073: ldloc.0 // i
IL_0074: sub
IL_0075: ldc.i4.s 100 // 0x64
IL_0077: cgt
IL_0079: br.s IL_007c
IL_007b: ldc.i4.0
IL_007c: stloc.s V_8

confused with Quick sort algorithm C#

I've been working out how to do quicksort in C# for college. I've nearly got it working.Only a few numbers don't appear in the correct order.
array: 1,5,8,6,7,3,2,4,9
"sorted" into: 1,5,4,6,2,3,7,8,9
instead of 1,2,3,4,5,6,7,8,9.
Not sure where I'm going wrong in my code:
int[] array4 = { 1, 5, 8, 6, 7, 3, 2, 4, 9};
QuickSort quick = new QuickSort();
quick.Quicksort(array4, 0, array4.Length - 1);
public void Quicksort(int[] array, int left, int right)
{
int pivot, temp;
pivot = array[(left + right / 2)];
do
{
while ((array[left] < pivot) && (left < right))
left++;
while ((pivot < array[right]) && (right > left))
right--;
if (left <= right)
{
temp = array[left];
array[left] = array[right];
array[right] = temp;
left++;
right--;
}
}
while (left <= right);
if (left < right) Quicksort(array, left, right);
}
}
Thanks
In your Quicksort method, as Lee’s comment points out, you are not calling the quicksort method with the left and right portions of the partition/pivot.
First I am confident you are not getting the proper pivot at the line:
pivot = array[(left + right / 2)];
Above, the division will happen first, so it will divide “right” by two then add to “left”. This is going to give you the wrong pivot. It should be:
pivot = array[(left + right) / 2];
Second, when you enter the Quicksort method, you are given the STARTING index values (left/right) and you use these variables to get the next pivot. This will throw off the “left” and “right” STARTING indexes when you change them. So you need to make a copy of those STARTING values and use the copied values to create the next partition/pivot.
Below are changes I made to your code and it appears to work properly.
public static void Quicksort(int[] array, int left, int right)
{
int pivot, temp;
pivot = array[(left + right) / 2];
int originalLeft = left;
int originalRight = right;
do
{
while (array[left] < pivot)
left++;
while (pivot < array[right])
right--;
if (left <= right)
{
temp = array[left];
array[left] = array[right];
array[right] = temp;
left++;
right--;
}
}
while (left <= right);
// note that the original left and right values are needed here
if (originalLeft < right)
Quicksort(array, originalLeft, right);
if (left < originalRight)
Quicksort(array, left, originalRight);
}
Hope this helps.

Reversing my MergeSort Algorithm

Basically I've got a merge sort in my code to order a group of numbers in a text file. I've given the option through a menu to allow the user to sort these numbers in ascending and descending order. At the moment I only have it one way and I can't for the life of me figure out how to reverse it. It's probably something really simple. Here's my code:
Methods
static public void mergemethod(int [] numbers, int left, int mid, int right)
{
int [] temp = new int[144];
int i, left_end, num_elements, tmp_pos;
left_end = (mid - 1);
tmp_pos = left;
num_elements = (right - left + 1);
while ((left <= left_end) && (mid <= right))
{
if (numbers[left] <= numbers[mid])
temp[tmp_pos++] = numbers[left++];
else
temp[tmp_pos++] = numbers[mid++];
}
while (left <= left_end)
temp[tmp_pos++] = numbers[left++];
while (mid <= right)
temp[tmp_pos++] = numbers[mid++];
for (i = 0; i < num_elements; i++)
{
numbers[right] = temp[right];
right--;
}
}
static public void sortmethod(int [] numbers, int left, int right)
{
int mid;
if (right > left)
{
mid = (right + left) / 2;
sortmethod(numbers, left, mid);
sortmethod(numbers, (mid + 1), right);
mergemethod(numbers, left, (mid+1), right);
}
}
Where it's used.
private static void SortVolume(int sortingAD)
{
if (sortingAD == 1)
{
int[] numbers = Array.ConvertAll(Volume, int.Parse);
int len = 144;
Console.WriteLine("\nAscending order: To return, press any key");
sortmethod(numbers, 0, len - 1);
for (int i = 0; i < 144; i++)
Console.WriteLine(numbers[i]);
}
else if (sortingAD == 2)
{
//This is where the reverse code will go(?)
}
Console.ReadKey();
MainMenu(true);
}
Thanks.

c# quicksort string array class

How do I make the function return the result of the sorted arrays?
class quiksort
{
public static char[] qsort(char[] items)
{
return qs(items, 0, items.Length - 1);
}
// A recursive version of Quicksort for characters.
static char[] qs(char[] items, int left, int right)
{
int i, j;
char x, y;
i = left; j = right;
x = items[(left + right) / 2];
do
{
while ((items[i] < x) && (i < right)) i++;
while ((x < items[j]) && (j > left)) j--;
if (i <= j)
{
y = items[i];
items[i] = items[j];
items[j] = y;
i++; j--;
}
} while (i <= j);
if (left < j)
{
return qs(items, left, j);
}
if (i < right)
{
return qs(items, i, right);
}
}
}
the errors says that not all code paths return a value? what does that mean
The problem is in your second method. As the error states all code paths must return a value. You have your return statements within if statements. Even if one of those will always execute the compiler does not care, as far as it is concerned there is the potential that your method won't return anything. You need to add a return as the last line of the function.
static char[] qs(char[] items, int left, int right)
{
int i, j;
char x, y;
i = left; j = right;
x = items[(left + right) / 2];
do
{
while ((items[i] < x) && (i < right)) i++;
while ((x < items[j]) && (j > left)) j--;
if (i <= j)
{
y = items[i];
items[i] = items[j];
items[j] = y;
i++; j--;
}
} while (i <= j);
if (left < j)
{
return qs(items, left, j);
}
if (i < right)
{
return qs(items, i, right);
}
return //whatever is most appropriate in the case that you arrive here
}
I'm not sure what value you actually want to return there so I'll leave that to you. If you expect to only get there in the case of an error then I would probably use null.
evanmcdonnal's answer is correct as to the error.
More generally though, it's confusing to have a method take a char[] parameter, change that char[] and then return a char[] (the same char[], but that isn't obvious from just the signature.
If you're going to do an in-place replacement like this, then your code will be clearer if you just return void, making it obvious that you alter the char[] passed as a parameter.
Conversely, if you're going to return a sorted array, then return a new array, and leave the one you are passed unaltered.

Categories