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.
Related
MergeSort(string fileName, int left, int right)
{
int opCounter = 0;
opCounter++;
if (left < right)
{
opCounter++;
int mid = (left + right) / 2;
opCounter++;
opCounter += MergeSort(fileName, left, mid);
opCounter++;
opCounter += MergeSort(fileName, mid + 1, right);
opCounter++;
opCounter += Merge(fileName, left, mid, right);
opCounter++;
}
return opCounter;
}
How to perform this method with linked list? What should be "next", "previous" and "current"?
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
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I changed the pivot to be the first element in the array, will this Quicksort algorithm still be ok? and will the variable "l" in the data array be the first element in all the partitioning steps?
x = data[l] // pivot
thanks
public void QuickSort(int[] data)
{
QuickSort(data, 0, data.Length - 1);
}
private void QuickSort(int[] data, int l, int r)
{
int i, j;
int x;
i = l;
j = r;
x = data[l];
while (true)
{
while (data[i] < x)
{
i++;
}
while (data[j] > x)
{
j--;
}
if (l <= j)
{
int temporary = data[i];
data[i] = data[j];
data[j] = temporary;
i++;
j--;
}
if (i > j)
{
break;
}
}
if (l < j)
{
QuickSort(data, l, j);
}
if (i < r)
{
QuickSort(data, i, r);
}
}
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(int[] arr, int left, int right)
{
// For Recusrion
if (left < right)
{
int pivot = Partition(arr, left, right);
if (pivot > 1)
QuickSort(arr, left, pivot - 1);
if (pivot + 1 < right)
QuickSort(arr, pivot + 1, right);
}
}
your logic is not ok try this logic understand it first it's similar to your's but a slight difference
Here is a Quick Sort program that reads the numbers from the file and then sorts them smallest to highest. I need it to be able to do the exact same but for numbers with decimals. I understand there is ways of converting int to double, decimal or float, but I've tried everything i can and nothing is working, can someone show me how they would change this code in order to make it work. Below is how i've attempted to do it:
class quickSort
{
private double[] array = new double[1010];
private int len;
public void QuickSort()
{
sort(0, len - 1);
}
public void sort(double left, double right)
{
double pivot;
double leftend, rightend;
leftend = left;
rightend = right;
pivot = array[left];
while (left < right)
{
while ((array[right] >= pivot) && (left < right))
{
right--;
}
if (left != right)
{
array[left] = array[right];
left++;
}
while ((array[left] <= pivot) && (left < right))
{
left++;
}
if (left != right)
{
array[right] = array[left];
right--;
}
}
array[left] = pivot;
pivot = left;
left = leftend;
right = rightend;
if (left < pivot)
{
sort(left, pivot - 1);
}
if (right > pivot)
{
sort(pivot + 1, right);
}
}
public static void Main()
{
quickSort q_Sort = new quickSort();
string[] years = System.IO.File.ReadAllLines(#"C:\WS1_Rain.txt");
var yearArray = years.Select(item => Convert.ToDouble(item));
double[] array = yearArray.ToArray();
q_Sort.array = array;
q_Sort.len = q_Sort.array.Length;
q_Sort.QuickSort();
for (int j = 0; j < q_Sort.len; j++)
{
Console.WriteLine(q_Sort.array[j]);
}
Console.ReadKey();
}
}
}
If your end goal is to purely sort the array, you could just use Linq!
string[] years = System.IO.File.ReadAllLines(#"C:\WS1_Rain.txt");
var yearArray = years.Select(item => Convert.ToDouble(item));
double[] array = yearArray.ToArray();
var sorted = array.OrderBy(a => a);
However, if you wish to amend your QuickSort class, you could do it like this with minimal changes to your original code. It is only the array that needs to support doubles. Your supporting variables used to manage indexing can stay as integers:
class quickSort
{
private double[] array = new double[1010];
private int len;
public void QuickSort()
{
sort(0, len - 1);
}
public void sort(int left, int right)
{
double pivot;
int leftend, rightend;
leftend = (int)left;
rightend = (int)right;
pivot = array[left];
while (left < right)
{
while ((array[right] >= pivot) && (left < right))
{
right--;
}
if (left != right)
{
array[left] = array[right];
left++;
}
while ((array[left] <= pivot) && (left < right))
{
left++;
}
if (left != right)
{
array[right] = array[left];
right--;
}
}
array[left] = pivot;
pivot = left;
left = leftend;
right = rightend;
if (left < pivot)
{
sort(left, Convert.ToInt32(pivot - 1));
}
if (right > pivot)
{
sort(Convert.ToInt32(pivot + 1), right);
}
}
static void Main(string[] args)
{
quickSort q_Sort = new quickSort();
string[] years = System.IO.File.ReadAllLines(#"C:\WS1_Rain.txt");
var yearArray = years.Select(item => Convert.ToDouble(item));
double[] array = yearArray.ToArray();
var sorted = array.OrderBy(a => a);
q_Sort.array = array;
q_Sort.len = q_Sort.array.Length;
q_Sort.QuickSort();
for (int j = 0; j < q_Sort.len; j++)
{
Console.WriteLine(q_Sort.array[j]);
}
Console.ReadKey();
}
}
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.