Quicksort not sorting correctly - c#

Attempting to learn from doing an implementation of Quicksort, I cannot find out why it's not sorting properly.
Using this sequence:
6, 7, 12, 5, 9, 8, 65, 3
It returns this:
3, 5, 7, 8, 9, 65, 12, 6
It seems to sort somewhat, but not all. What have I missed?
Here's my code:
static void Main(string[] args)
{
QuickSort qs = new QuickSort();
int[] arr = new int[] { 6, 7, 12, 5, 9, 8, 65, 3 };
foreach (int l in arr)
{
Console.Write(l + ", ");
}
int left = 0;
int right = arr.Count() - 1;
int[] arrr = qs.DoQuickSort(ref arr, left, right);
Console.WriteLine("Sorted List: ");
foreach (int i in arrr)
{
Console.Write(i + " ");
}
Console.ReadLine();
}
public int Partition(int[] array, int left, int right, int PivotIndex)
{
int pivValue = array[PivotIndex];
array = Swap(ref array, PivotIndex, right);
int storeIndex = left;
for (int i = PivotIndex; i < right-1; i++)
{
if (array[i] <= pivValue)
{
array = Swap(ref array, i, storeIndex);
storeIndex = storeIndex + 1;
}
}
array = Swap(ref array, storeIndex, right);
return storeIndex;
}
public int[] Swap(ref int[] arr, int first, int second)
{
int temp = arr[first];
arr[first] = arr[second];
arr[second] = temp;
return arr;
}
public int[] DoQuickSort(ref int[] array, int left, int right)
{
if (right > left)
{
int PivIndex = (left + right) / 2;
int newPivIndex = Partition(array, left, right, PivIndex);
DoQuickSort(ref array, left, newPivIndex - 1);
DoQuickSort(ref array, newPivIndex + 1, right);
}
return array;
}

Are you asking to be handed a fish, or to be taught how to fish?
Learning how to debug your own programs, rather than relying upon other people to do it for you, is a skill that will serve you well in the future.
When faced with this problem, the first thing I would do is mark up the code with comments indicating the semantic purpose of each section of code:
// Choose a pivot halfway along the portion of the list I am searching.
int PivIndex = (left + right) / 2;
// Partition the array so that everything to the left of the pivot
// index is less than or equal to the pivot, and everything to
// the right of the pivot is greater than or equal to the pivot.
int newPivIndex = Partition(array, left, right, PivIndex);
// Recursively sort each half.
DoQuickSort(ref array, left, newPivIndex - 1);
DoQuickSort(ref array, newPivIndex + 1, right);
OK, now, somewhere in here there is a bug. Where? Start listing facts that you believe to be true, and write an assertion for every fact. Write yourself helper methods, like AllLessThan, which verify assertions for you.
// Choose a pivot halfway along the portion of the list I am searching.
int PivIndex = (left + right) / 2;
int pivotValue = array[PivIndex];
// Partition the array so that everything to the left of the pivot
// index is less than or equal to the pivot, and everything to
// the right of the pivot is greater than or equal to the pivot.
int newPivIndex = Partition(array, left, right, PivIndex);
Debug.Assert(array[newPivIndex] == pivotValue);
Debug.Assert(AllLessThanOrEqual(array, left, newPivIndex, pivotValue));
Debug.Assert(AllGreaterThanOrEqual(array, newPivIndex, right, pivotValue));
// Recursively sort each half.
DoQuickSort(ref array, left, newPivIndex - 1);
Debug.Assert(IsSorted(array, left, newPivIndex));
DoQuickSort(ref array, newPivIndex + 1, right);
Debug.Assert(IsSorted(array, left, right));
Now when you run this program again, the moment one of your assertions is violated, you'll get a box that pops up to tell you what the nature of the bug is. Keep doing that, documenting your preconditions and postconditions with assertions until you find the bug.

In your Partition method you have a wrong loop range:
for (int i = PivotIndex; i < right-1; i++)
Should become:
for (int i = left; i < right; i++)
Checkout the related wikipedia article which says:
function partition(array, left, right, pivotIndex)
pivotValue := array[pivotIndex]
swap array[pivotIndex] and array[right] // Move pivot to end
storeIndex := left
for i from left to right - 1 // left ≤ i < right
if array[i] ≤ pivotValue
swap array[i] and array[storeIndex]
storeIndex := storeIndex + 1
swap array[storeIndex] and array[right] // Move pivot to its final place
return storeIndex
Notice: left ≤ i < right

In addition to my comment on the question itself, I wanted to point out that the Swap() and DoQuickSort() methods do not need to return the array (as per my note in the comment which explains that the array is a reference itself). To that end, your code to do the job should look like the following:
public int Partition(int[] array, int left, int right, int pivotIndex)
{
int pivValue = array[pivotIndex];
Swap(array, pivotIndex, right);
int storeIndex = left;
for (int i = left; i < right; i++)
{
if (array[i] <= pivValue)
{
Swap(array, i, storeIndex);
storeIndex = storeIndex + 1;
}
}
Swap(array, storeIndex, right);
return storeIndex;
}
public void Swap(int[] arr, int first, int second)
{
int temp = arr[first];
arr[first] = arr[second];
arr[second] = temp;
}
public void DoQuickSort(int[] array, int left, int right)
{
if (right > left)
{
int pivIndex = (left + right) / 2;
int newPivIndex = Partition(array, left, right, pivIndex);
DoQuickSort(array, left, newPivIndex - 1);
DoQuickSort(array, newPivIndex + 1, right);
}
}

Related

Editing a Merge Sort to sort in Descending Order C#

I am going through a sample piece of coursework that I had been sent by a friend to get myself ready for University coursework in my next year. I have been attempting to create a sorting and searching system to handle assorted data. For this task I have created a merge sort system which can sort my data in ascending order. However, I am unsure how I would edit this algorithm so it could sort in descending order instead. Could anyone possibly explain how this could be done?
The current algorithm handles string values.
Since this is based on a University task it requires myself to actually code the sorting and searching algorithms and not use the inbuilt functions available through Visual Studio.
static public void MainMerge<T>(T[] values, int left, int mid, int right) where T : IComparable<T>
{
int c = values.Length;
T[] temp = new T[c];
int i, eol, num, pos;
eol = (mid - 1);
pos = left;
num = (right - left + 1);
while ((left <= eol) && (mid <= right))
{
if (values[left].CompareTo(values[mid]) < 0)
temp[pos++] = values[left++];
else
temp[pos++] = values[mid++];
}
while (left <= eol)
temp[pos++] = values[left++];
while (mid <= right)
temp[pos++] = values[mid++];
for (i = 0; i < num; i++)
{
values[right] = temp[right];
right--;
}
}
static public void SortMerge<T>(T[] values, int left, int right) where T : IComparable<T>
{
int mid;
if (right > left)
{
mid = (right + left) / 2;
SortMerge(values, left, mid);
SortMerge(values, (mid + 1), right);
MainMerge(values, left, (mid + 1), right);
}
}
You just need to change this: values[left].CompareTo(values[mid]) < 0 to this: values[left].CompareTo(values[mid]) >= 0. The merge phase is the only place where comparison is used.
You can also pass a comparator to your sort function to allow a custom comparison predicate.
There's one more way to do it: you can use your current algorithm and reverse the result.

What's wrong with my MergeSort? C#

To keep it short. I made a MergeSort, which should sort an int[]. To test the MergeSort I made an array containing 4 integers in random order. It returns the exact list in the order given to the MergeSort, instead of doing what it should do. I wonder what I did wrong.
Edit: The first two merges return these two lists: 4, 15 && 5, 16, however the last merge doesn't work.
This is the actual code:
public static int[] Merge(int[] list, int left, int mid, int right)
{
int[] returnList = new int[list.Length];
int Index = left;
//last value of left half
int leftEnd = mid;
//both the left side and right side contain atleast one value
while (leftEnd >= left && right >= (mid + 1))
{
if (list[left] <= list[(mid + 1)])
{
returnList[Index] = list[left];
left++;
}
else
{
returnList[Index] = list[(mid + 1)];
mid++;
}
Index++;
}
//right side is empty, left contains atleast one value
while (leftEnd >= left)
{
returnList[Index] = list[left];
left++;
Index++;
}
//left side is empty, right contains atleast one value
while (right >= (mid + 1))
{
returnList[Index] = list[(mid + 1)];
mid++;
Index++;
}
return returnList;
}
public static int[] Split(int[] list, int left, int right)
{
if (right > left)
{
int mid = (right + left) / 2;
Split(list, left, mid); //first half
Split(list, (mid + 1), right); //second half
list = Merge(list, left, (mid + 1), right);
}
return list;
}
My Main:
int[] intArray = new int[] { 12, 4, 16, 5 };
int[] ReturnTest = MergeSort.Split(intArray, 0, intArray.Length - 1);
foreach (int value in ReturnTest)
{
Console.WriteLine(value);
}
The expected output: 4, 5, 12, 16
The actual output: 12, 4, 16, 5

merge sort implementation query

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.

Implementing QuickSort recursively in C#

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);
}
}

Rotating right an array of int in c#?

I've got an homework assignment:
need to implement a function (RotateRight) that gets an array of INT and a number:
int[] res = RotateRight(new int[] { 1, 2, 3, 4, 5, 6 }, 2);
//so then res will be {5,6,1,2,3,4}
and return the array after rotating all of the items to the right according to the number that been given, In our case 2.
And I have to do this efficiently in terms of memory space.
my best idea is:
if the number that been given is x, to use a new int[] tmpArray in the size of x to copy all the last x items to it. then with a for loop to shift all the rest of the int to the right.
And in the end to copy the items in the tmpArray to the begining of the original array.
Thanks in advance for any advice or help
You can use the beauty of the Linq langage to return an IEnumerable without dealing with array size:
/// <summary>
/// Get c = a mod (b) with c in [0, b[ like the mathematical definition
/// </summary>
public static int MathMod(int a, int b)
{
int c = ((a % b) + b) % b;
return c;
}
public static IEnumerable<T> ShiftRight<T>(IList<T> values, int shift)
{
for (int index = 0; index < values.Count; index++)
{
yield return values[MathMod(index - shift, values.Count)];
}
}
Usage :
[TestMethod]
public void TestMethod1()
{
var res = ShiftRight(new [] { 1, 2, 3, 4, 5, 6 }, 2).ToArray();
Assert.IsTrue(res.SequenceEqual(new[] { 5, 6, 1, 2, 3, 4 }));
}
Most memory possible makes no sense, you probably mean as little memory as possible? If so you should swap each item in the array using XOR, i.e:
var a = 2096;
var b = 842390;
a ^= b;
b ^= a;
a ^= b;
would swap these numbers.
EDIT
Code to do the whole thing in place:
public static void RotateRight(int[] input, int right)
{
for (var i = 0; i < right; i += 1)
{
RotateRightOne(input);
}
}
public static void RotateRightOne(int[] input)
{
var last = input.Length - 1;
for (var i = 0; i < last; i += 1)
{
input[i] ^= input[last];
input[last] ^= input[i];
input[i] ^= input[last];
}
}
Usage:
var arr = new[] {1, 2, 3, 4, 5, 6};
RotateRight(arr, 2);
As Servy points out, this is only for integers
Don't know C#, but here are two C++ versions, both in place, the first (rotate) does the minimum possible number of element moves by exploiting the cyclic structure of the rotation permutation, the second (rotate_k) just does 2*n moves for an array of length n. In both versions it's used that rotate right by k is the same as rotate left by n - k % n, so they in fact do the equivalent left rotation.
#include <iostream>
#include <vector>
#include <algorithm>
void
rotate (size_t k, std::vector<int> &a) {
size_t n = a.size();
k = n - k % n;
size_t m = n;
size_t i = 0;
while (m > 0) {
int t = a[i];
size_t j = i;
while (i != (j + k) % n) {
a[j] = a[(j + k) % n];
j = (j + k) % n;
--m;
}
a[j] = t;
--m;
++i;
}
}
void
rotate_k (size_t k, std::vector<int> &a) {
size_t n = a.size();
k = n - k % n;
std::reverse (a.begin(), a.end());
std::reverse (a.begin(), a.begin() + n - k);
std::reverse (a.begin() + n - k, a.end());
}
int
main () {
std::vector<int> a = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
rotate (12, a);
for (auto i : a)
std::cout << i << " ";
std::cout << std::endl;
}
You just need to figure out the final index for each element after rotating it k times rather than actually rotating it k times.
This worked for me:
for(int i=0;i<a.Length;i++){
rotated[(k+i)%(a.Length)]=a[i];
}
Here's a quick sample on rotating an array A right K steps:
var splitPoint=A.Length-(K%A.Length);
var result=new int[A.Length];
int idx=0;
for(var pos=0;pos<A.Length;pos++)
{
if(pos<A.Length-splitPoint)
{
result[pos]=A[splitPoint+pos];
}
else
{
result[pos]=A[idx];
idx++;
}
}
return result;
C# 8 now has Indices and Ranges
Rotate Right...
int[] r = t[1..].Concat(t[0..1]).ToArray();
Rotate Left...
int[] r = t[^1..^0].Concat(t[..^1]).ToArray();
in place of the "1" above, a variable can also be used: int[] r = t[amt..].Concat(t[0..amt]).ToArray();

Categories

Resources