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.
Related
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
}
}
}
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
Currently my Quick Sort algorithm sorts the array in Ascending order in case 1 but I would like to make it so that when the user selects option 2 (case 2) then it sorts the array in Descending order. Do I have to create 2 separate algorithms for each case? Or is there a simpler and more efficient way?
Appreciate the help.
static void Main(string[] args)
{
Console.WriteLine("Analysis of Seismic Data.\n");
Console.WriteLine("Selection of Arrays:");
//Display all the options.
Console.WriteLine("1-Year");
Console.WriteLine("2-Month");
Console.WriteLine("3-Day");
Console.WriteLine("4-Time");
Console.WriteLine("5-Magnitude");
Console.WriteLine("6-Latitude");
Console.WriteLine("7-Longitude");
Console.WriteLine("8-Depth");
Console.WriteLine("9-Region");
Console.WriteLine("10-IRIS_ID");
Console.WriteLine("11-Timestamp\n\n");
Console.WriteLine("Use numbers to select options.");
//Read in user's decision.
Console.Write("Select which array is to be analysed:");
int userDecision = Convert.ToInt32(Console.ReadLine());
//Selected which array is to be analyzed
switch (userDecision)
{
case 1:
Console.WriteLine("\nWould you like to sort the Array in Ascending or Descending order?");
Console.WriteLine("1-Ascending");
Console.WriteLine("2-Descending");
//Create another switch statement to select either ascending or descending sort.
int userDecision2 = Convert.ToInt32(Console.ReadLine());
switch (userDecision2)
{
case 1:
//Here algorithm sorts my array in ascending order by default.
QuickSort(Years);
Console.WriteLine("Contents of the Ascending Year array: ");
foreach (var year in Years)
{
Console.WriteLine(year);
}
break;
case 2:
//How do I sort the same array in Descending order when Option 2 is selected?
//QuickSort(Years) Descendingly.
Console.WriteLine("Contents of the Descending Year array: ");
foreach (var year in Years)
{
Console.WriteLine(year);
}
break;
}
break;
case 2:
Console.WriteLine("\nWould you like to sort the Array in Ascending or Descending order?");
Console.WriteLine("1-Ascending");
Console.WriteLine("2-Descending");
//Create another switch statement to select either ascending or descending sort.
int userDecision3 = Convert.ToInt32(Console.ReadLine());
switch (userDecision3)
{
case 1:
QuickSort(Years);
Console.WriteLine("Contents of the Ascending Month array: ");
foreach (var month in Months)
{
Console.WriteLine(month);
}
break;
case 2:
//Same problem, how do I sort it in descending order?
Console.WriteLine("Contents of the Descending month array: ");
foreach (var month in Months)
{
Console.WriteLine();
}
break;
}
break;
}
}
public static void QuickSort<T>(T[] data) where T : IComparable<T>
{
Quick_Sort(data, 0, data.Length - 1);
}
public static void Quick_Sort<T>(T[] data, int left, int right) where T : IComparable<T>
{
int i, j;
T pivot, temp;
i = left;
j = right;
pivot = data[(left + right) / 2];
do
{
while ((data[i].CompareTo(pivot) < 0) && (i < right)) i++;
while ((pivot.CompareTo(data[j]) < 0) && (j > left)) j--;
if (i <= j)
{
temp = data[i];
data[i] = data[j];
data[j] = temp;
i++;
j--;
}
} while (i <= j);
if (left < j) Quick_Sort(data, left, j);
if (i < right) Quick_Sort(data, i, right);
}
You can change your Quicksort method to accept an IComparer<T> comparer, and then use that to make the comparisons.
Then you can use Comparer<T>.Default if you want the default comparison order, or you can use Comparer<T>.Create() to create a custom (e.g. reversed) comparison.
Compilable example:
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
int[] data = {6, 7, 2, 3, 8, 1, 9, 0, 5, 4};
QuickSort(data);
Console.WriteLine(string.Join(", ", data)); // Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
QuickSort(data, Comparer<int>.Create((a, b) => b.CompareTo(a)));
Console.WriteLine(string.Join(", ", data)); // Prints 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
}
public static void QuickSort<T>(T[] data)
{
Quick_Sort(data, 0, data.Length - 1, Comparer<T>.Default);
}
public static void QuickSort<T>(T[] data, IComparer<T> comparer)
{
Quick_Sort(data, 0, data.Length - 1, comparer);
}
public static void Quick_Sort<T>(T[] data, int left, int right, IComparer<T> comparer)
{
int i, j;
T pivot, temp;
i = left;
j = right;
pivot = data[(left + right) / 2];
do
{
while ( (comparer.Compare(data[i], pivot) < 0) && (i < right)) i++;
while ( (comparer.Compare(pivot, data[j]) < 0) && (j > left)) j--;
if (i <= j)
{
temp = data[i];
data[i] = data[j];
data[j] = temp;
i++;
j--;
}
} while (i <= j);
if (left < j) Quick_Sort(data, left, j, comparer);
if (i < right) Quick_Sort(data, i, right, comparer);
}
}
}
This has two advantages:
The type T does NOT need to implement IComparable<T>. You can pass in an IComparer<T> object that does the comparison.
You can sort the same data in multiple ways, by passing in different custom IComparer<T> object.
This is the approach adopted by a number of the Linq IEnumerable extensions, for example Enumerable.OrderBy()
Add parameter that allows to change result of comparisons
(data[i].CompareTo(pivot) < 0)
and
(pivot.CompareTo(data[j]) < 0)
One simple way: argument descending +1/-1 and use
data[i].CompareTo(pivot) * descending < 0
Just adding a bit of cream on #Matthew Watson answer. As after reading the post from #Illimar he wanted to give the user the choice in sorting the Array either in Ascending mode or in Descending mode. I have just edited #Matthew work to come up with the following:
using System;
using System.Collections.Generic;
using System.Threading;
namespace Test1
{
class Program
{
static void Main(string[] args)
{
MySorter();
}
static void MySorter()
{
int[] data = MyArray();
Console.WriteLine();
Console.WriteLine("Chose 1 to sort Ascending or 2 to sort Descending:");
//int choice = Console.Read();
ConsoleKeyInfo choice = Console.ReadKey();
Console.WriteLine();
if (choice.Key == ConsoleKey.D1 || choice.Key == ConsoleKey.NumPad1)
{
QuickSort(data);
string result = string.Join(", ", data);
Console.WriteLine(result);
Thread.Sleep(4000);
}
else if (choice.Key == ConsoleKey.D2 || choice.Key == ConsoleKey.NumPad2)
{
QuickSort(data, Comparer<int>.Create((a, b) => b.CompareTo(a)));
Console.WriteLine(string.Join(", ", data));
Thread.Sleep(4000);
}
else
{
Console.WriteLine("wrong input.");
Thread.Sleep(2000);
Environment.Exit(0);
}
}
public static int[] MyArray()
{
Console.WriteLine("Enter a total of 10 numbers to be sorted: ");
int[] InputData = new int[10];
for (int i = 0; i < InputData.Length; i++)
{
var pressedkey = Console.ReadKey();
int number;
bool result = int.TryParse(pressedkey.KeyChar.ToString(), out number);
if (result)
{
InputData[i] = number;
}
}
return InputData;
}
public static void QuickSort<T>(T[] data)
{
Quick_Sort(data, 0, data.Length - 1, Comparer<T>.Default);
}
public static void QuickSort<T>(T[] data, IComparer<T> comparer)
{
Quick_Sort(data, 0, data.Length - 1, comparer);
}
public static void Quick_Sort<T>(T[] data, int left, int right, IComparer<T> comparer)
{
int i, j;
T pivot, temp;
i = left;
j = right;
pivot = data[(left + right) / 2];
do
{
while ((comparer.Compare(data[i], pivot) < 0) && (i < right)) i++;
while ((comparer.Compare(pivot, data[j]) < 0) && (j > left)) j--;
if (i <= j)
{
temp = data[i];
data[i] = data[j];
data[j] = temp;
i++;
j--;
}
} while (i <= j);
if (left < j) Quick_Sort(data, left, j, comparer);
if (i < right) Quick_Sort(data, i, right, comparer);
}
}
}
So In the above the User gives the numbers to be sorted as Input and then chooses how the sorting should be done?
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();
}
}
I'm making a menu of all kinds of sorting algorithm and now I'm stuck at merge sort.
I encountered an error after clicking the execute button. I entered 5 numbers in TextBox1 and another set of 5 numbers in TextBox2. It says that index was outside the bounds of the array. I indicated on the codes where it appeared. Any ideas what is the problem?
private void ExeButton_Click(object sender, EventArgs e)
{
string[] numsInString = EntNum.Text.Split(' '); //split values in textbox
string[] numsInString1 = EntNum1.Text.Split(' ');
for (int j = 0; j < numsInString.Length; j++)
{
a[j] = int.Parse(numsInString[j]);
}
for (int j = 0; j < numsInString1.Length; j++)
{
b[j] = int.Parse(numsInString1[j]);
}
{
sortArray();
Display();
}
}
public void sortArray()
{
m_sort(0, 10 - 1);
}
public void m_sort(int left, int right)
{
int mid;
if (right > left)
{
mid = (right + left) / 2;
m_sort(left, mid);
m_sort(mid + 1, right);
merge(left, mid + 1, right);
}
}
public void merge(int left, int mid, int right)
{
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 (a[left] <= a[mid]) //index was outside the bounds of the the array
{
b[tmp_pos] = a[left];
tmp_pos = tmp_pos + 1;
left = left + 1;
}
else
{
b[tmp_pos] = a[mid];
tmp_pos = tmp_pos + 1;
mid = mid + 1;
}
}
while (left <= left_end)
{
b[tmp_pos] = a[left];
left = left + 1;
tmp_pos = tmp_pos + 1;
}
while (mid <= right)
{
b[tmp_pos] = a[mid];
mid = mid + 1;
tmp_pos = tmp_pos + 1;
}
for (i = 0; i < num_elements; i++)
{
a[right] = b[right];
right = right - 1;
}
}
private void ClearButton_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
}
public void Display()
{
int i;
String numbers = "";
for (i = 0; i < 10; i++)
numbers += a[i].ToString() + " ";
numbers += b[i].ToString() + " ";
richTextBox1.AppendText(numbers + "\n");
}
Regarding your concrete question: a is an array of 5 elements with the indexes 0, 1, 2, 3, 4, so a[4] is the last element. You start with m_sort(0, 10 - 1) = m_sort(0, 9);. In m_sort() you compute
mid = (right + left) / 2 = (9 + 0) / 2 = 4
and call
merge(left, mid + 1, right) = merge(0, 4 + 1, 9) = merge(0, 5, 9).
In merge(int left, int mid, int right) you evaluate:
if (a[left] <= a[mid]) i.e. if (a[0] <= a[5])
so you access a[5] which is out of bounds.
I think your merge sort can be simplified considerably. You might look at the many resources on the Web or in a textbook on algorithms.
Here is an example of a way to simplify the merge, assuming each list is sorted with element 0 having the highest value:
int c[] = new int[a.Length + b.Length];
int aPos = 0;
int bPos = 0;
for(int i = 0; i < c.Length; i++)
{
if(a[APos] > b[bPos])
{
c[i] = a[Apos];
if(aPos < aPos.Length - 1)
aPos++;
}
else
{
c[i] = b[bPos];
if(bPos < bPos.Length - 1)
bPos++;
}
}
// array of integers to hold values
private int[] a = new int[100];
private int[] b = new int[100];
// number of elements in array
private int x;
// Merge Sort Algorithm
public void sortArray()
{
m_sort( 0, x-1 );
}
public void m_sort( int left, int right )
{
int mid;
if( right > left )
{
mid = ( right + left ) / 2;
m_sort( left, mid );
m_sort( mid+1, right );
merge( left, mid+1, right );
}
}
public void merge( int left, int mid, int right )
{
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( a[left] <= a[mid] )
{
b[tmp_pos] = a[left];
tmp_pos = tmp_pos + 1;
left = left +1;
}
else
{
b[tmp_pos] = a[mid];
tmp_pos = tmp_pos + 1;
mid = mid + 1;
}
}
while( left <= left_end )
{
b[tmp_pos] = a[left];
left = left + 1;
tmp_pos = tmp_pos + 1;
}
while( mid <= right )
{
b[tmp_pos] = a[mid];
mid = mid + 1;
tmp_pos = tmp_pos + 1;
}
for( i = 0; i < num_elements; i++ )
{
a[right] = b[right];
right = right - 1;
}
}