Bubble sort when using struct members - c#

struct SSales
{
private int Y;
private double S;
public int Year
{
get { return Y; }
set { Y = value; }
}
public double Sale
{
get { return S; }
set { S = value; }
}
public SSales (int _year, double _sales)
{
Y = _year;
S = _sales;
}
private void Sortbutton_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
if (yearradio.Checked)
{
int temp = 0;
for (int i = 0; i < bubble.Length - 1; i++)
{
for (int j = 0; j < bubble.Length - 1; j++)
{
if (bubble[i + 1].Year < bubble[i].Year)
{
temp = bubble[i].Year;
bubble[i].Year = bubble[i + 1].Year;
bubble[i + 1].Year = temp;
}
}
}
}
if (salesradio.Checked)
{
double temp2 = 0;
for (int i = 0; i < bubble.Length - 1; i++)
{
for (int j = 0; j < bubble.Length - 1; j++)
{
if (bubble[i + 1].Sale > bubble[i].Sale)
{
temp2 = bubble[i].Sale;
bubble[i].Sale = bubble[i + 1].Sale;
bubble[i + 1].Sale = temp2;
}
}
}
}
for (int i = 0; i < bubble.Length; i++)
{
listBox1.Items.Add(bubble[i].ToString());
}
}
While my bubble sorting algorithms work perfectly fine, they only sort incrementally with each click of the Sort Button. I need the listbox to be completely sorted with 1 click.
Also, as my code is now, the Years and Sales reorganize completely independent of each other. When the Sales index changes, the corresponding Year index remains at the same location and vice versa.
I'm guessing a for loop with int j would work, but I'm not sure how to implement it. Any help would be appreciated!

I see two issues. One you are setting/exchanging the Properties of the struct, not the structs themselves. That is why your sales and years are out of sync. You need to exchange the entire struct. Something like:
var temp = bubble[i];
bubble[i] = bubble[i + 1];
bubble[i + 1] = temp;
This leads to the second issue. You have a double loop using index variables i and j. Your swap only uses i. If you are trying to do a bubble sort do you really need nested loops? Consider the pseudo-code implementation that can be found here bubble sort, you should quickly be able to see the issue. Model your sort after that example.

I will guess that you are doing bubble sort for learning / practice reasons. If not you should just use the inbuilt Array.Sort() or Enumerable.OrderBy() or something similar.
There are multiple things you have done wrong. I have the improved code below which I will explain
struct SSales {
public int Year { get; set; } // use auto-properties for brevity
public double Sale { get; set; } // use auto-properties for brevity
public SSales(int year, double sales) {
Year = year;
Sale = sales;
}
}
// Use a generic routine to Swap, instead of replicating the code multiple times
// Note that we are passing by reference so the actual array eventually gets sorted
// Also, don't swap the properties, but the whole record. Else it will corrupt your data
static void Swap<T>(ref T obj1, ref T obj2) {
var temp = obj1;
obj1 = obj2;
obj2 = temp;
}
// Write the sort routine separately. Sorts usually just need a way to compare records, which can be provided by Caller (IoC pattern)
static void Sort<T>(T[] items, Func<T, T, int> comparer) {
for (int i = 0; i < items.Length - 1; i++) {
// Every execution of the inner loop will bubble-up the largest element in the range
// Your array is getting sorted from the end, so you don't need to re-compare the already sorted part
for (int j = 0; j < items.Length - 1 - i; j++) {
if (comparer(items[j], items[j + 1]) > 0) // call the generic user provided comparer to know the sequence
Swap(ref items[j], ref items[j + 1]); // use teh generic swapper to swap elements in the array
}
}
}
private void Sortbutton_Click(object sender, EventArgs e) {
listBox1.Items.Clear();
if (yearradio.Checked) {
// Invoke the Sort routine, with the required comparer
Sort(bubble, (a, b) => a.Year - b.Year);
}
if (salesradio.Checked) {
// Invoke the Sort routine, with the required comparer
Sort(bubble, (a, b) => (int)(a.Sale - b.Sale));
}
for (int i = 0; i < bubble.Length; i++) {
listBox1.Items.Add(bubble[i].ToString());
}
}
Hope that clarifies the issues you are facing and also helps you learn how to write better C# code.

Related

Sort by selection in C#

I am a complete beginner in programming. Trying to make sorting a choice. Everything seems to be ok. Only there is one caveat. Only numbers up to 24 index are filled in the new array. I can’t understand what the problem is.
int[] Fillin(int[] mass)
{
Random r = new Random();
for(int i = 0; i < mass.Length; i++)
{
mass[i] = r.Next(1, 101);
}
return mass;
}
int SearchSmall(int[] mass)
{
int smallest = mass[0];
int small_index = 0;
for(int i = 1; i < mass.Length; i++)
{
if (mass[i] < smallest)
{
smallest = mass[i];
small_index = i;
}
}
return small_index;
}
int[] Remove(int[] massiv,int remind)
{
List<int> tmp = new List<int>(massiv);
tmp.RemoveAt(remind);
massiv = tmp.ToArray();
return massiv;
}
public int[] SortMass(int[] mass)
{
mass = Fillin(mass);
Print(mass);
Console.WriteLine("________________________________");
int[] newmass = new int[mass.Length];
int small;
for(int i = 0; i < mass.Length; i++)
{
small = SearchSmall(mass);
newmass[i] = mass[small];
mass = Remove(mass, small);
}
return newmass;
}
I think your main issue is that when you remove an element in the Remove function, the main loop in for (int i = 0; i < mass.Length; i++) will not check all elements o the initial array. A simple (and ugly) way to fix that would be not to remove the elements but to assign a very high value
public static int[] Remove(int[] massiv, int remind)
{
massiv[remind] = 999999;
return massiv;
}
Or as Legacy suggested simply modify the mass.length for newmass.lengh in the main loop.
As some others have mentioned this is not the best way to order an array, but it is an interesting exercise.

Bubble sort is not working

Currently the user is able to input numbers into the ListBox, and I want the ListBox to be sorted by the bubble sort below when a sort checkbutton is clicked. However, it only outputs the index number e.g. 0,1,2,3... I am not allowed to use any array or containers just the items property and parsing and converting.
private void sorted()
{
int a = Convert.ToInt32(lstHoldValue.Items.Count);
int temp;
for (int i = 0; i < a; i++)
{
for (int j = i + 1; j < a; j++)
{
if (Convert.ToInt32(lstHoldValue.Items[i]) >
Convert.ToInt32(lstHoldValue.Items[j]))
{
temp = Convert.ToInt32(lstHoldValue.Items[i]);
(lstHoldValue.Items[i]) = Convert.ToInt32(lstHoldValue.Items[j]);
(lstHoldValue.Items[j]) = temp;
}
}
}
lstHoldValue.Items.Clear();
for (int i = 0; i < a; i++)
{
Convert.ToInt32(lstHoldValue.Items.Add("\t" + i));
}
}
This is more like a Bubble sort algorithm:
private void BubbleSort()
{
int a = lstHoldValue.Items.Count;
for (int i = 0; i < a - 1; i++)
{
var k = 0;
for(var j = 1; j < a - i; j++)
{
if (Convert.ToInt32(lstHoldValue.Items[j]) < Convert.ToInt32(lstHoldValue.Items[k]))
{
var temp = lstHoldValue.Items[j];
lstHoldValue.Items[j] = lstHoldValue.Items[k];
lstHoldValue.Items[k] = temp;
k = j;
}
else
{
k++;
}
}
}
}
It will sort the numbers in the Items collection of your lstHoldValue listBox control
If the item in the ListBox is not an integer, then it is sorted as zero (0).
This sets bubbleUp to true indicating that a swap has been made in the list box. This variable is used to indicate that a swap has/has not been made in the last comparison. Entering the while(bubbleUp) sets bubbleUp to false to indicate that no swaps have been made. Then a loop through each item in the list box to compare adjacent items and swap if needed. If a swap is made, bubbleUp gets set to true indicating that the sort is not finished. bubbleUp only needs to be set once in the for loop to indicate another iteration is necessary. CheckItem is the conversion from string to integer. Hope this helps.
private void sorted()
{
bool bubbleUp = true;
string temp = "";
while (bubbleUp)
{
// bubble up adjacent values
bubbleUp = false;
for (int i = 0; i < _ListBox.Items.Count - 1; i++)
{
if (CheckItem(_ListBox.Items[i].ToString()) > CheckItem(_ListBox.Items[i + 1].ToString()))
{
temp = _ListBox.Items[i].ToString();
_ListBox.Items[i] = _ListBox.Items[i + 1];
_ListBox.Items[i + 1] = temp;
bubbleUp = true;
}
}
}
}
private int CheckItem(string inItem)
{
int value;
if (int.TryParse(inItem, out value))
return value;
return 0;
}
private void button1_Click(object sender, EventArgs e)
{
sorted();
}

How to combine paths through a loop

I am creating a folder maze creator (creating one folder within another) application for some fun, but it seems to be much more complicated than what I first expected. I think examples will explain this better. Basically I want to do this
for (int i = 0; i < iterations; i++)
{
Directory.CreateDirectory(dirName + i);
for (int ii = 0; ii < iterations; ii++)
{
Directory.CreateDirectory(Path.Combine(dirName + i, dirName + ii));
for (int iii = 0; iii < iterations; iii++)
{
Directory.CreateDirectory(Path.Combine(dirName + i, dirName + ii, dirName + iii));
for (int iv = 0; iv < iterations; iv++)
{
Directory.CreateDirectory(Path.Combine(dirName + i, dirName + ii, dirName + iii, dirName + iv));
}
}
}
}
But, instead of setting the number of nested loops, I wanted the user to be able to set that at runtime. So with some help from Programmers, I got this.
private void Recursion(int depth)
{
if (depth >= depthMax)
return;
//string[] folderPlace = new string[depthMax + 1];
//for (int ii = 0; ii < depthMax; ii++)
//{
// if (ii <= depth)
// {
// folderPlace[ii] = dirName;
// }
// else
// {
// folderPlace[ii] = "";
// }
//}
for (int i = 0; i < iterations; i++)
{
folderPlace[1] = dirName + i;
Directory.CreateDirectory(dirName + i);
Recursion(depth + 1);
}
}
But I just can't seem to find a way to get the right path in an array so every folder goes where it should automatically...
Any help appreciated, thanks!
I would suggest you have a look at this example:
public partial class Form1 : Form
{
const string DirName = "Mydir";
const string RootFolder = #"c:\test";
public Form1()
{
CreateDirectories(0, 5, 5, RootFolder);
InitializeComponent();
}
public void CreateDirectories(int currentDepth, int maxDepth, int iterations, string root)
{
if (currentDepth > maxDepth)
{
return;
}
for (var i = 0; i < iterations; i++)
{
var currentDirName = Path.Combine(root, DirName + i.ToString());
Directory.CreateDirectory(currentDirName);
CreateDirectories(currentDepth + 1, maxDepth, iterations, currentDirName);
}
}
}
Basically, you need to know the difference between Iterations and Recursions.
Iterations are all types of loops (eg. for, foreach). Recursion means a method calls its self and provides some kind of an exit-condition to prevent endless exectution.
Recursions allow you to define the level of nesting for operations, but is considered slower. Iterations only allow you to define how many circles one iteration processes, but not the level of nesting which is kind of hard coded (the 3 nested loops in you first example). IMO Iterations are easier to understand and to develop, compared to recursion.
Your particular problem calls for a combined solution, where a method calls its self recursively inside iteration-circles.
A possible solution would look like below (the most interesting part is the Magic(int, string)-method, where i have added some comments to point out examples of the things I have explained above):
private class FolderMaze
{
private const string SubDirectoryName = "Maze_";
private readonly string baseDirectory;
private readonly int depth;
private readonly int nFoldersPerIteration;
public FolderMaze(string baseDirectory, int depth, int nFoldersPerIteration)
{
this.baseDirectory = baseDirectory;
this.depth = depth;
this.nFoldersPerIteration = nFoldersPerIteration;
}
public void Magic()
{
this.Magic(0, this.baseDirectory);
}
private void Magic(int iteration, string iterationPath)
{
// exit condition
if (iteration >= this.depth)
{
return;
}
// Iteration
for (int i = 0; i < this.nFoldersPerIteration; i++)
{
var currentPath = Path.Combine(iterationPath, SubDirectoryName + i);
Directory.CreateDirectory(currentPath);
// recursive call to Magic(int string)
this.Magic(++iteration, currentPath);
}
}
}
You can call it like this:
var maze = new FolderMaze(Environment.CurrentDirectory, 2, 2);
maze.Magic();
I did put the solution inside a class, so that you can focus on the important things in the Magic-method, but you could also put this inside a single method w/o a surrounding class like in the answer provided by #Jean F.

Ranking with tiebreakers

How do I go about ranking a list based on one criteria, and then separating ties (of which there are likely to be a lot) based on another?
My (working) attempt to rank drivers based on their points totals.
for (int j = 0; j < career.NumberOfDrivers; j++)
{
int rank = 1;
for (int i = 0; i < career.NumberOfDrivers; i++)
{
if (career.driver[j].championshipPoints < career.driver[i].championshipPoints)
{
rank += 1;
}
}
career.driver[j].championshipRank = rank;
}
Presumably I then want to cycle through afterwards checking for ties - the problem is there are definitely going to be situations when I have several drivers tied on 0.
How do I go about simply breaking these tiebreakers alphabetically (ie based on first letter of driver's names)?
EDIT - my solution if its of interest. Pretty nasty hack in some respects but does the job.
List<Driver> tempDriver = new List<Driver>();
for (int i = 0; i < career.driver.Count; i++)
{
tempDriver.Add(career.driver[i]);
}
tempDriver.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.Name.CompareTo(d2.Name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
for (int i = 0; i < career.NumberOfDrivers; i++)
{
for (int j = 0; j < career.NumberOfDrivers; j++)
{
if (career.driver[i].Name == tempDriver[j].Name)
{
career.driver[i].championshipRank = j + 1;
}
}
}
You can use Sort() on your drivers List, using a delegate.
If drivers have same points number, return the comparison result of their names.
Else return the comparison or their points
driversList.Sort(
delegate(Driver d1, Driver d2)
{
if (d1.championshipPoints == d2.championshipPoints)
{
return d1.name.CompareTo(d2.name);
}
return d1.championshipPoints.CompareTo(d2.championshipPoints);
}
);
Then the ranking of a driver is simply its index in the sorted list.
Edit:
This solution has n*log(n) complexity (the OP first attempt is O(n^2) )

Sort String Array using Levenstein Algorithm results

I've been working on an Access file editor in C#, and i've been trying to get a search feature added to my program. So far, I have the database file populate a 2D array, which i then use to populate a ListView box in another window. From this new window, I would like to be able to search each entry by Model Number. So far, i've managed to incorporate the Levenstein Algorithm, which seems to have much use. I can get the algorithm to assign the distance value between each entry and the search keyboard, and assign that value to another integer array. I can also sort the results in increasing order.
However, my current problem is that i'd would like to have the Model numbers sorted with the same respect to the distance values from the Levenstein Algorithm, so that the most relevant result becomes the first choice in the ListView box. Any ideas anyone??!?!
Here's what i've got so far:
private void OnSearch(object sender, System.EventArgs e)
{
string a;
string b;
int[] result = new int[1000];
int[] sorted = new int[1000];
for (int i = 0; i < rowC; i++)
{
a = PartNum[i]; // Array to search
b = SearchBox1.Text; // keyword to search with
if (GetDistance(a, b) == 0)
{
return;
}
result[i] = GetDistance(a, b); //add each distance result into array
}
int index;
int x;
for (int j = 1; j < rowC; j++) //quick insertion sort
{
index = result[j];
x = j;
while ((x > 0) && (result[x - 1] > index))
{
result[x] = result[x - 1];
x = x - 1;
}
result[x] = index;
}
}
public static int GetDistance(string s, string t)
{
if (String.IsNullOrEmpty(s) || String.IsNullOrEmpty(t))
{
MessageBox.Show("Please enter something to search!!");
return 0;
}
int n = s.Length;
int m = t.Length;
if (n == 0)
{
return m;
}
else if (m == 0)
{
return n;
}
int[] p = new int[n + 1];
int[] d = new int[n + 1];
int[] _d;
char t_j;
int cost;
for (int i = 0; i <= n; i++)
{
p[i] = i;
}
for (int j = 1; j <= m; j++)
{
t_j = t[j - 1];
d[0] = j;
for (int i = 1; i <= n; i++)
{
cost = (s[i - 1] == t_j) ? 0 : 1;
d[i] = Math.Min(Math.Min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
}
_d = p;
p = d;
d = _d;
}
return p[n];
}
Do you have LINQ available to you? If so:
var ordered = PartNum.OrderBy(x => GetDistance(x, SearchBox1.Text))
.ToList();
// Do whatever with the ordered list
Note that this has the disadvantage of not aborting early if you find an exact match, as well as not making the actual distances available - but it's not entirely clear how you're using the results anyway...
Another option would be:
var ordered = (from word in PartNum
let distance = GetDistance(word, SearchBox1.Text))
orderby distance
select new { word, distance }).ToList();
Then you've got the distance as well.
In order to sort your array by Levenstein distance you need to include the model numbers as part of your array so that, when you sort the array by Levenstein number, the model numbers will go along for the ride.
To do this, create a class representing each part:
public class Part
{
public string PartNumber;
public int LevensteinDistance;
}
and then create an array of Part:
Part[] parts;
You can then reference each element like so:
parts[n].LevensteinDistance
parts[n].PartNumber

Categories