I started learning algorithms and I am trying to implement Quicksort in C#.
This is my code:
class QuickSortDemo
{
public void Swap(ref int InputA, ref int InputB)
{
InputA = InputA + InputB;
InputB = InputA - InputB;
InputA = InputA - InputB;
}
public int Partition(int[] InputArray, int Low, int High)
{
int Pivot = InputArray[Low];
int LoopVariable1 = Low - 1;
int LoopVariable2 = High + 1;
while (true)
{
while (InputArray[--LoopVariable2] > Pivot) ;
while (InputArray[++LoopVariable1] < Pivot) ;
if (LoopVariable1 < LoopVariable2)
{
Swap(ref InputArray[LoopVariable1], ref InputArray[LoopVariable2]);
for (int LoopVariable = Low; LoopVariable <= High; LoopVariable++)
{
Console.Write(InputArray[LoopVariable] + " ");
}
Console.WriteLine();
}
else
{
for (int LoopVariable = Low; LoopVariable <= High; LoopVariable++)
{
Console.Write(InputArray[LoopVariable] + " ");
}
Console.WriteLine();
return LoopVariable2;
}
}
}
public void QuickSort(int[] InputArray,int Low, int High)
{
if (Low < High)
{
int Mid = Partition(InputArray, Low, High);
QuickSort(InputArray, Low, Mid);
QuickSort(InputArray, Mid + 1, High);
}
}
public static void Main()
{
int[] InputArray = { 10, 5, 6, 8, 23, 19, 12, 17 };
QuickSortDemo Demo = new QuickSortDemo();
for (int LoopVariable = 0; LoopVariable < InputArray.Length; LoopVariable++)
{
Console.Write(InputArray[LoopVariable]+" ");
}
Console.WriteLine();
Demo.QuickSort(InputArray, 0, InputArray.Length - 1);
for (int LoopVariable = 0; LoopVariable < InputArray.Length; LoopVariable++)
{
Console.Write(InputArray[LoopVariable] + " ");
}
Console.WriteLine();
}
}
For some reason I can't get this to work when I take the rightmost element in the array as pivot. I don't know what I am doing wrong. It would be really helpful if someone could explain me why this doesn't work when I take my rightmost element as the pivot. From what I learned, this should work for any input and any pivot element. Correct me if I am wrong.
Thank you.
I'm still not entirely sure I understand the question. But I was able to reproduce a problem (infinite recursion) when I change the line of code in the Partition() method from int pivot = inputArray[low]; to int pivot = inputArray[high];, and doing so seems consistent with your narrative:
I can't get this to work when I take the rightmost element in the array as pivot.
If I've understood the question correctly, then the basic problem is that when you change where you get the pivot, you also need to take this into account when returning the new mid-point. Currently, you return loopVariable2, which is correct when picking the pivot from the lower end of the array. But if you switch to picking the pivot from the upper end of the array, you need to return loopVariable2 - 1.
Another problem is that as you are scanning, you unconditionally increment or decrement the respective "loop variable", regardless of whether the current index is already at an element in the wrong partition. You need to check the current element position first, and only adjust the index if that element is in the correct partition.
Here is a correct version of the Partition() method where the pivot is selected using high instead of low:
public int Partition(int[] inputArray, int low, int high)
{
int pivot = inputArray[high];
int loopVariable1 = low;
int loopVariable2 = high;
while (true)
{
while (inputArray[loopVariable2] > pivot) loopVariable2--;
while (inputArray[loopVariable1] < pivot) loopVariable1++;
if (loopVariable1 < loopVariable2)
{
Swap(ref inputArray[loopVariable1], ref inputArray[loopVariable2]);
for (int loopVariable = low; loopVariable <= high; loopVariable++)
{
Console.Write(inputArray[loopVariable] + " ");
}
Console.WriteLine();
}
else
{
for (int loopVariable = low; loopVariable <= high; loopVariable++)
{
Console.Write(inputArray[loopVariable] + " ");
}
Console.WriteLine();
return loopVariable2 - 1;
}
}
}
In either case, note that the effect is to ensure that regardless of the pivot value selected, you always partition the array in such a way to ensure that a new pivot is always selected with each level of recursion, preventing the infinite loop.
By the way, and for what it's worth, I would not implement Swap() as you have. It's an interesting gimmick to do a no-temp-variable swap, but there is no practical benefit to doing so, while it does incur a significant code maintenance and comprehension cost. In addition, it will only work with integral numeric types; what if you want to extend your sort implementation to handle other types? E.g. ones that implement IComparable or where you allow the caller to provide an IComparer implementation?
IMHO a better Swap() method looks like this:
public void Swap<T>(ref T inputA, ref T inputB)
{
T temp = inputA;
inputA = inputB;
inputB = temp;
}
quick sort:
static public int Partition(int [] numbers, int left, int right)
{
int pivot = numbers[left];
while (true)
{
while (numbers[left] < pivot)
left++;
while (numbers[right] > pivot)
right--;
if (left < right)
{
int temp = numbers[right];
numbers[right] = numbers[left];
numbers[left] = temp;
}
else
{
return right;
}
}
}
static public void QuickSort_Recursive(int [] arr, int left, int right)
{
// For Recusrion
if(left < right)
{
int pivot = Partition(arr, left, right);
if(pivot > 1)
QuickSort_Recursive(arr, left, pivot - 1);
if(pivot + 1 < right)
QuickSort_Recursive(arr, pivot + 1, right);
}
}
Related
This question already has answers here:
StackOverflowException when perform Quicksort with ordered list
(1 answer)
Quicksort Algorithm (Cormen) gives Stackoverflow
(1 answer)
Closed 2 years ago.
This is my quick sort algorithm implementation. I'm getting System.StackOverflowException with the message 'Exception of type 'System.StackOverflowException' was thrown.' while trying to sort an array larger than 15k elements. I actually checked with 15000, 19000, 20000, 30000 elements, and the exception was thrown for the last 3 inputs.
private static int ArraySplitter(int[] intArr, int low, int high)
{
int pivot = intArr[high];
int lowIndex = (low - 1);
for (int i = low; i < high; i++)
{
if (intArr[i] <= pivot)
{
lowIndex++;
int temp = intArr[lowIndex];
intArr[lowIndex] = intArr[i];
intArr[i] = temp;
}
}
int tempHigh = intArr[lowIndex + 1];
intArr[lowIndex + 1] = intArr[high];
intArr[high] = tempHigh;
return lowIndex + 1;
}
private static void QSort(int[] intArr, int low, int high)
{
if (low < high)
{
int index = ArraySplitter(intArr, low, high);
QSort(intArr, low, index - 1);
QSort(intArr, index + 1, high);
}
}
public static void QuickSort(int[] intArr)
{
QSort(intArr, 0, intArr.Length - 1);
}
My python implementation also breaking for an array with larger than 5000 elements. Here is my python code -
def QUICKSORT(arr, p, r):
if p < r:
q = PARTITION(arr, p, r)
QUICKSORT(arr, p, q-1)
QUICKSORT(arr, q+1, r)
def PARTITION(arr, p, r):
x = arr[r]
i = p-1
for j in range(p, r-1):
if arr[j] <= x:
i = i + 1
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
temp = arr[i+1]
arr[i+1] = arr[r]
arr[r] = temp
return i+1
I followed the pseudocode from Thomas H Cormen's Introduction to algorithms.
What's seems to be the problem and how to fix this issue?
Choosing the first or last element in a sub-array for pivot leads to worst case space complexity O(n) if the data is already sorted or reverse sorted. For the questions code, swap the middle element with the last element (array[high]) before splitting to handle sorted or reverse sorted data. Still there are other patterns that result in worst case behavior.
Only using recursion for the smaller partition will limit stack space complexity to O(log(n)), but worst case time complexity remains O(n^2).
private static void QSort(int[] intArr, int low, int high)
{
while (low < high)
{
int index = ArraySplitter(intArr, low, high);
if((index - low) <= (high - index)){
QSort(intArr, low, index - 1);
low = index + 1;
} else {
QSort(intArr, index + 1, high);
high = index - 1;
}
}
}
How would I modify this for-loop so that it counts up for positive values of Step, but counts down for negative values of Step?
For Step = 2, the expected output is 2 4 6 8 10
For Step =- 2, the expected output is 10 8 6 4 2
// assume these 3 come from user input
int Lower = 2;
int Upper = 10;
int Step = 2;
for ( int i = Lower; i <= Upper; i += Step )
{
Console.Write(i + " ");
}
Just obey the KISS principle.
You can just put the logic into the initialiser and the condition of the for statement:
public static void ForLoopWithDirectionBasedOnStep(int minValue, int maxValue, int step)
{
// Avoid obvious hang
if ( step == 0 )
throw new ArgumentException("step cannot be zero");
// ( initialiser ; condition ; iterator )
for ( int i = step > 0 ? minValue : maxValue; minValue <= i && i <= maxValue; i += step )
Console.Write(i + " ");
}
so:
ForLoopWithDirectionBasedOnStep(minValue: 2, maxValue: 10, step: 2) returns:
2 4 6 8 10
ForLoopWithDirectionBasedOnStep(minValue: 2, maxValue: 10, step: -2) returns:
10 8 6 4 2
as desired.
The initialiser sets the start value
int i = step > 0 ? minValue : maxValue;
by using the conditional operator and is equivalent to
int i;
if ( step > 0 )
i = minValue;
else
i = maxValue;
The condition
minValue <= i && i <= maxValue
simply checks that the loop variable is within the range [minValue, maxValue].
Note that bad inputs are handled automatically because (emphasis mine):
The condition section, if present, must be a boolean expression. That expression is evaluated before every loop iteration.
so something like ForLoopWithDirectionBasedOnStep(minValue: 10, maxValue: 0, step: -2) which would count down from 0 to 10 doesn't print anything because, since 0 < 10, the body of the for statement is never executed.
You need a pre-processing step to change the comparison in the for loop as well as its limits
int Lower = 2;
int Upper = 10;
int Step = -2;
Func<int, bool> comparator = (j) => j <= Upper;
if (Step < 0)
{
var temp = Lower;
Lower = Upper;
Upper = temp;
comparator = (j) => j >= Upper;
}
for(int i=Lower; comparator(i); i+=Step)
{
Console.Write(i + " ");
}
DotNetFiddle
You can do two for loops based on the sign of the step variable:
static void Main(string[] args)
{
int lower = 2;
int upper = 10;
int step = -2;
if (Math.Sign(step) == 1)
{
for (int i = step; i < upper; i += step)
{
Console.WriteLine(string.Format("{0}", i));
}
}
else if (Math.Sign(step) == -1)
{
for (int i = upper; i >= lower; i += step)
{
Console.WriteLine(string.Format("{0}", i));
}
}
Console.ReadLine();
}
}
Note that this code is untested but the idea is to use a Predicate<T> for the loop and switching Upper and Lower if step is negative
// assume these 3 come from user input
int Lower = 2;
int Upper = 10;
int Step = 2;
if(step < 0){ int temp = Lower; Lower = Upper; Upper = temp;}
Predicate<int> LoopPred = (i =>
{
if(Step < 0)
return i >= Upper;
return i <= Upper;
})
for(int i=Lower; LoopPred(i); i+=Step)
{
Console.Write(i + “ “);
}
You can make a Func delegate to check if the step is negative and invert the bound checking condition.
Here's a sample:
class Program
{
public static void Print(int Lower, int Upper, int Step)
{
Func<int, bool> checkBounds = (i) => i <= Upper;
if (Step < 0)
{
Swap(ref Lower, ref Upper);
checkBounds = (i) => i >= Upper;
}
for (int i = Lower; checkBounds(i); i += Step)
Console.Write($"{i} ");
}
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
static void Main(string[] args)
{
// assume these 3 come from user input
int Lower = 2;
int Upper = 10;
int Step = -2;
Print(Lower, Upper, Step);
}
}
for(int i=Step>0?Lower:Upper; Step>0? i<=Upper: i>= Lower; i+=Step)
{
Console.Write(i + " ");
}
#john All you need to update the looping conditions.
I am making a program that has a function to sort names alphabetically, I easily used Array.Sort() and it worked but I need the algorithm of the sorting function to help me understand the function more
Here is the Array.cs: http://www.dotnetframework.org/default.aspx/DotNET/DotNET/8#0/untmp/whidbey/REDBITS/ndp/clr/src/BCL/System/Array#cs/2/Array#cs
There is the Sort Method
They use the QuickSort -they check for exceptions, and if everything is OK, they summon this:
internal void QuickSort(int left, int right) {
// Can use the much faster jit helpers for array access.
do {
int i = left;
int j = right;
// pre-sort the low, middle (pivot), and high values in place.
// this improves performance in the face of already sorted data, or
// data that is made up of multiple sorted runs appended together.
int middle = GetMedian(i, j);
SwapIfGreaterWithItems(i, middle); // swap the low with the mid point
SwapIfGreaterWithItems(i, j); // swap the low with the high
SwapIfGreaterWithItems(middle, j); // swap the middle with the high
Object x = keys[middle];
do {
// Add a try block here to detect IComparers (or their
// underlying IComparables, etc) that are bogus.
try {
while (comparer.Compare(keys[i], x) < 0) i++;
while (comparer.Compare(x, keys[j]) < 0) j--;
}
catch (IndexOutOfRangeException) {
throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", x, x.GetType().Name, comparer));
}
catch (Exception e) {
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
}
catch {
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"));
}
BCLDebug.Assert(i>=left && j<=right, "(i>=left && j<=right) Sort failed - Is your IComparer bogus?");
if (i > j) break;
if (i < j) {
Object key = keys[i];
keys[i] = keys[j];
keys[j] = key;
if (items != null) {
Object item = items[i];
items[i] = items[j];
items[j] = item;
}
}
i++;
j--;
} while (i <= j);
if (j - left <= right - i) {
if (left < j) QuickSort(left, j);
left = i;
}
else {
if (i < right) QuickSort(i, right);
right = j;
}
} while (left < right);
}
}
More info about it: https://en.wikipedia.org/wiki/Quicksort
I found this example of a merge sort algorithm online on a tutorial webpage and I have been trying to understand ow the code implements the algorithm. The example i found uses recursion and a temporary array to sort the array of unsorted algorithms.
My query is in the final step of the process. When copying the elements of the temporary array into the original array to sort the array. why does the algorithm decrements the right attribute instead of incrementing the left attribute? when i incremented the left left value the algorithm does not work.
class Assignment1
{
static void Main(string[] args)
{
Console.WriteLine("Size of array:");
int n = Convert.ToInt16(Console.ReadLine());
int[] unsorted = new int[n];
for(int i = 0; i < n; i++)
{
Console.WriteLine("Enter a number:");
unsorted[i] = Convert.ToInt16(Console.ReadLine());
}
Console.WriteLine("--------Sort---------");
Recursion_Sort(unsorted, 0, n - 1);
for (int i = 0; i < n; i++)
{
Console.WriteLine(unsorted[i]);
}
}
static public void Merge(int[] numbers, int left, int mid, int right, int n)
{
int[] tempArray = new int[n];
int i, lEnd, size, pos;
lEnd = mid - 1;
pos = left;
size = (right - left + 1);
while ((left <= lEnd) && (mid <= right))
{
if (numbers[left] <= numbers[mid])
{
tempArray[pos] = numbers[left];
pos++;
left++;
}
else
{
tempArray[pos] = numbers[mid];
pos++;
mid++;
}
}
while (left <= lEnd)
{
tempArray[pos] = numbers[left];
pos++;
left++;
}
while (mid <= right)
{
tempArray[pos] = numbers[mid];
pos++;
mid++;
}
Console.WriteLine(tempArray.Length);
for (i = 0; i < size; i++)
{
numbers[right] = tempArray[right];
right--;
}
}
static public void Recursion_Sort(int[] numbers, int left, int right)
{
int mid;
if (right > left)
{
mid = (right + left) / 2;
Recursion_Sort(numbers, left, mid);
Recursion_Sort(numbers, (mid + 1), right);
// we then merge the sorted sub arrays using the merge method
Merge(numbers, left, (mid + 1), right, numbers.Length);
}
}
}
left value is changing during merge and as you have code block
while (left <= lEnd)
{
//...
left++;
}
left will be finally assigned to lEnd + 1(the condition for ending while loop).
Otherwise right is not changing and is the last index of currently manipulated sequence.
Taking the risk of not answering the question like you want it, I would suggest LINQ. This is not merge sort in particular, but rather a concatenation of two arrays and then sorting.
If your array isn't so big that performance matters, you might want to go for this approach, because it's simple and less code (which is always good).
int[] arr1 = new[] { 1, 2, 3, 7, 8, 10 };
int[] arr2 = new[] { 4, 6, 9, 12, 15 };
int[] merged = arr1.Concat(arr2).OrderBy(n => n).ToArray();
Furthermore, I post this if it is interesting for others.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am applying the QuickSort algorithm from book introduction to Algorithm i wrote the code,but the output is not sorted correctly
Following is the algorithm
Quicksort(A, p, r)
{
if (p < r)
{
q = Partition(A, p, r)
Quicksort(A, p, q-1)
Quicksort(A, q+1, r)
}
}
Partition(A, p, r)
{
pivot = A[r]
i = p - 1
for j = p to r – 1
{
do if A[j] <= pivot
then
{
i = i + 1
exchange A[i] A[j]
}
}
exchange A[i+1] A[r]
return i+1
}
and here is my code
class Threads<T>
{
static bool IsLessThan(T x, T y)
{
if (((IComparable)(x)).CompareTo(y)<=0)
{
return true;
}
else {
return false;
}
}
public int Partition(T[] myarray, int low, int high)
{
T x = myarray[high];
T y;
int i = low - 1;
int j;
for (j = low; j < high - 1; j++)
{
//**************Added Text after edit,I forgot to put this
if (IsLessThan(myarray[j], x))
{
i++;
y = myarray[i];
myarray[i] = myarray[j];
myarray[j] = y;
}
}
y = myarray[i+1];
myarray[i+1] = myarray[high];
myarray[high] = y;
return i + 1;
}
public void QuickSort(T[] myarray, int low, int high)
{
if (low < high)
{
// int q = Partition(myarray,highh, low);
int q = Partition(myarray,low, high);
QuickSort(myarray, low, q - 1);
QuickSort(myarray, q + 1, high );
}
}
}
Following code shows, how the quicksort method is used
private void button1_Click(object sender, EventArgs e)
{
int[] myarray ={9,8,7,6,5,4,3,2};
textBox1.Text = "";
Threads<int> t1 = new Threads<int>();
t1.QuickSort(myarray, 0, myarray.Length-1);
for(int i=0;i<myarray.Length;i++)
textBox1.Text=textBox1.Text+" , "+myarray[i];
}
I get the Following output when i execute the program
8 , 7 , 6 , 5 , 4 , 3 , 9 , 2
Answer
value of i must be one less than j and low at start in the Partition function
I forgot to Swap myarray[i+1] and myarray[high] outside the loops in Partition Function.
Now the code is working accurately fine for strings ,int ,char etc
Try this:
public int Partition(T []myarray, int low, int high)
{
T x = myarray[high];
int i=low;
int j=high;
while(i< j)
{
while(i<j&& IsLessThan(myarray[i], x))
i++;
myarray[j]=myarray[i];
while(i<j&& IsLessThan(x,myarray[j]))
j--;
myarray[i]=myarray[j];
}
myarray[i] = myarray[high];
return i ;
}
Try some changes and debug your code
I tried following
private static int Partition(int[] input, int left, int right)
{
int pivot = input[right];
int temp;
int i = left;
for (int j = left; j < right; j++)
{
if (input[j] <= pivot)
{
temp = input[j];
input[j] = input[i];
input[i] = temp;
i++;
}
}
input[right] = input[i];
input[i] = pivot;
return i;
}
if Pivot element is middle element
private static List<int> QuickSort(List<int> a, int left, int right)
{
int i = left;
int j = right;
double pivotValue = ((left + right) / 2);
int x = a[Convert.ToInt32(pivotValue)];
int w = 0;
while (i <= j)
{
while (a[i] < x)
{
i++;
}
while (x < a[j])
{
j--;
}
if (i <= j)
{
w = a[i];
a[i++] = a[j];
a[j--] = w;
}
}
if (left < j)
{
QuickSort(a, left, j);
}
if (i < right)
{
QuickSort(a, i, right);
}
return a;
}
First this part looked plain wrong, but even after changing the sort doesn't work
int q = Partition(myarray, high, low);
Low and high should be changed
int q = Partition(myarray, low, high);
I think this question is less about an algorithm, and more about the methods of debugging one. So I used a Quicksort algorithm I found online and tried to implement it in C#. I had problems of my own, but debugged it fairly quickly. The code below has some useful techniques to help with debugging. (The algorithm came from http://rosettacode.org/wiki/Sorting_algorithms/Quicksort.)
Here are my general comments when comparing your code:
It was confusing having an array of ints to test with when dealing with integer indices as well, so I changed my array to strings.
I used a lot of Console.WriteLine so I could trace the steps the algorithm was taking. That's what quickly helped me find that low/high issue.
I didn't not use a variable named i as anything but a for loop index. That's just confusing as all heck to use it as anything else -- don't do that!
Since every QuickSort algortithm had multiple swaps I created a Swap function. Even if you only do it once in your code, it clutters up the purpose of the Partition method to have such a trival pattern in there. To put this another way, why did it make sense for you to make IsLessThan a method but not Swap?
Speaking about IsLessThan, technically you actually wrote a method for IsLessThanOrEqual. Don't name things incorrectly, because if somebody uses that and assumes it is just for Less Then, they'll have a hard time when they have unpredictable results. IsLSE would be understandable by most.
Your for loop is the only place you use the variable j so why declare it outside of the for statement? Just adds an extra line and variable in a scope you don't need it.
Here's my QuickSort. It appears to work:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string[] myarray = { "g", "b", "e", "f", "a", "d", "c"};
QuickSort<string> t1 = new QuickSort<string>();
t1.Sort(myarray, 0, myarray.Length - 1);
}
}
public class QuickSort<T>
{
static int Compare (T x, T y)
{
return ((IComparable)(x)).CompareTo(y);
}
private void Swap(T[] myarray, int i, int j)
{
// swapping indices just for writeline purposes
if (i > j)
{
int t = i;
i = j;
j = t;
}
Console.WriteLine("Swap: {0}:{1} with {2}:{3}",
i, myarray[i], j, myarray[j]);
T temp = myarray[i];
myarray[i] = myarray[j];
myarray[j] = temp;
Console.WriteLine("Result: {0}", String.Join(",", myarray));
}
private int Partition(T[] myarray, int low, int high, int pivotIndex)
{
T pivotVal = myarray[pivotIndex];
Swap(myarray, pivotIndex, high);
int currentLow = low;
while (low <= high) {
while (Compare(myarray[low], pivotVal) < 0) {
low++;
}
while (Compare(myarray[high], pivotVal) > 0) {
high--;
}
if (low <= high) {
Swap(myarray, low, high);
low++;
high--;
}
}
return low;
}
public void Sort(T[] myarray, int low, int high)
{
if (low < high)
{
Console.WriteLine(("Start: {0}", String.Join(",", myarray));
int pivotIndex = (low + high) / 2;
Console.WriteLine("QuickSort: P: {0}, L: {1}, H: {2}",
pivotIndex, low, high);
pivotIndex = Partition(myarray, low, high, pivotIndex);
Sort(myarray, low, pivotIndex - 1);
Sort(myarray, pivotIndex + 1, high);
}
}
}