This question already has answers here:
How to sort a list so that the same changes would happen in another list?
(3 answers)
Closed 3 months ago.
I have a question:
For example I had a list with 132 and a second list 123
I would want my 132 list.sort but the 123 list to sort like 132:
was:
132
123
After
123
132
Not switch place but if the first element changes position in list 1 so should list 2, please help.
This answer involves writing (or editing an existing) sorting algorithm.
In the example below, I've illustrated a way in Javascript to enhance a simple Bubble Sort algorithm to include a second array to manipulate aswell.
I know the question is for C#, but the same concepts apply.
The arrays need to have the same length, otherwise you will get errors.
// Bubblesort algorithm stolen from https://www.geeksforgeeks.org/bubble-sort/
// edited to include a second array (arr2) that mirrors the sorting of the first array
function swap(arr, xp, yp)
{
var temp = arr[xp];
arr[xp] = arr[yp];
arr[yp] = temp;
}
function bubbleSort( arr, arr2, n)
{
var i, j;
for (i = 0; i < n-1; i++)
{
for (j = 0; j < n-i-1; j++)
{
if (arr[j] > arr[j+1])
{
swap(arr,j,j+1);
// swap the elements of the second array aswell
swap(arr2,j,j+1);
}
}
}
}
// just a function for printing
function printArray(arr, size)
{
var i;
for (i=0; i < size; i++)
document.write(arr[i]+ " ");
document.write("<br />");
}
// the two arrays
var array1 = [5, 1, 4, 2, 8];
var array2 = [2, 3, 4, 5, 6];
var n = 5;
document.write("UnSorted arrays: <br />");
printArray(array1, n);
printArray(array2, n);
bubbleSort(array1, array2, n);
document.write("Sorted arrays: <br />");
printArray(array1, n);
printArray(array2, n);
If you run the above snippet, you see that the '1' in the second position of the first array has moved to the first position.
The second array had a '3' in the second position, which is also swapped to the first position, like the first array did.
Long story short: The second array mirrors the sorting of the first array.
Related
I am trying to write an implementation on C# of Subsets pattern read here 14 Patterns to Ace Any Coding Interview Question:
It looks obvious but confuses me. My research says me it should be implemented via Jagged Arrays (not Multidimensional Arrays). I started:
int[] input = { 1, 5, 3 };
int[][] set = new int[4][];
// ...
Could someone help with 2, 3 and 4 steps?
The instructions provided seem to lend themselves more to a c++ style than a C# style. I believe there are better ways than manually building arrays to get a list of subsets in C#. That said, here's how I would go about implementing the instructions as they are written.
To avoid having to repeatedly grow the array of subsets, we should calculate its length before we allocate it.
Assuming n elements in the input, we can determine the number of possible subsets by adding:
All subsets with 0 elements (the empty set)
All subsets with 1 element
All subsets with 2 elements
...
All subsets with n-1 elements
All subsets with n elements (the set itself)
Mathematically, this is the summation of the binomial coefficient. We take the sum from 0 to n of n choose k which evaluates to 2^n.
The jagged array should then contain 2^n arrays whose length will vary from 0 to n.
var input = new int[] { 1, 3, 5 };
var numberOfSubsets = (int)Math.Pow(2, input.Length);
var subsets = new int[numberOfSubsets][];
As the instructions in your article state, we start by adding the empty set to our list of subsets.
int nextEmptyIndex = 0;
subsets[nextEmptyIndex++] = new int[0];
Then, for each element in our input, we record the end of the existing subsets (so we don't end up in an infinite loop chasing the new subsets we will be adding) and add the new subset(s).
foreach (int element in input)
{
int stopIndex = nextEmptyIndex - 1;
// Build a new subset by adding the new element
// to the end of each existing subset.
for (int i = 0; i <= stopIndex; i++)
{
int newSubsetLength = subsets[i].Length + 1;
int newSubsetIndex = nextEmptyIndex++;
// Allocate the new subset array.
subsets[newSubsetIndex] = new int[newSubsetLength];
// Copy the elements from the existing subset.
Array.Copy(subsets[i], subsets[newSubsetIndex], subsets[i].Length);
// Add the new element at the end of the new subset.
subsets[newSubsetIndex][newSubsetLength - 1] = element;
}
}
With some logging at the end, we can see our result:
for (int i = 0; i < subsets.Length; i++)
{
Console.WriteLine($"subsets[{ i }] = { string.Join(", ", subsets[i]) }");
}
subsets[0] =
subsets[1] = 1
subsets[2] = 3
subsets[3] = 1, 3
subsets[4] = 5
subsets[5] = 1, 5
subsets[6] = 3, 5
subsets[7] = 1, 3, 5
Try it out!
What I find easiest is translating the problem from a word problem into a more logical one.
Start with an empty set : [[]]
So the trick here is that this word problem tells you to create an empty set but immediately shows you a set that contains an element.
If we break this down into arrays instead(because I personally find it more intuitive) we can translate it to:
Start with an array of arrays, who's first element is an empty array. (instead of null)
So basically
int[][] result = new int[]{ new int[0] };
Now we have somewhere to start from, we can start to translate the other parts of the word problem.
Add the First Number (1) to all existing subsets to create subsets: [[],[1]]
Add the Second Number (5) to all existing subsets ...
Add the Third Number (3) to all existing subsets ...
There's a lot of information here. Let's translate different parts
Add the 1st Number ...
Add the 2nd Number ...
Add the nth Number ...
The repetition of these instructions and the fact that each number 1, 5, 3 matches our starting set of {1, 5, 3} tells us we should use a loop of some kind to build our result.
for(int i = 0; i < set.Length; i++)
{
int number = set[i];
// add subsets some how
}
Add the number to all existing subsets to create subsets: [[],[1]
A couple things here stand out. Notice they used the word Add but provide you an example where the number wasn't added to one of the existing subsets [[]] turned into [[],[1]]. Why is one of them still empty if we added 1 to all of them?
The reason for this is because when we create the new subsets and all their variations, we want to keep the old ones. So we do add the 1 to [](the first element) but we make a copy of [] first. That way when we add 1 to that copy, we still have the original [] and now a brand new [1] then we can combine them to create [[],[1]].
Using these clues we can decipher that Add the number to all existing subsets, actually means make copies of all existing subsets, add the number to each of the copies, then add those copies at the end of the result array.
int[][] result = new int[]{ new int[0] };
int[] copy = result[0];
copy.Append(1); // pseudo code
result.Append(copy); // pseudo code
// result: [[],[1]]
Let's put each of those pieces together and put together the final solution, hopefully!
Here's an example that I threw together that works(at least according to your example data).
object[] set = { 1, 5, 3 };
// [null]
object[][] result = Array.Empty<object[]>();
// add a [] to the [null] creating [[]]
Append(ref result, Array.Empty<object>());
// create a method so we can add things to the end of an array
void Append<T>(ref T[] array, T SubArrayToAdd)
{
int size = array.Length;
Array.Resize(ref array, size + 1);
array[size] = SubArrayToAdd;
}
// create a method that goes through all the existing subsets and copies them, adds the item, and adds those copies to the result array
void AddSubsets(object item)
{
// store the length of the result because if we don't we will infinitely expand(because we resize the array)
int currentLength = result.Length;
for (int i = 0; i < currentLength; i++)
{
// copy the array so we don't change the original
object[] previousItemArray = result[i]; // []
// add the item to it
Append(ref previousItemArray, item); // [1]
// add that copy to the results
Append(ref result, previousItemArray); // [[]] -> [[],[1]]
}
}
// Loop over the set and add the subsets to the result
for (int i = 0; i < set.Length; i++)
{
object item = set[i];
AddSubsets(item);
}
everyone. I've this small task to do:
There are two sequences of numbers:
A[0], A[1], ... , A[n].
B[0], B[1], ... , B[m].
Do the following operations with the sequence A:
Remove the items whose indices are divisible by B[0].
In the items remained, remove those whose indices are divisible by B[1].
Repeat this process up to B[m].
Output the items finally remained.
Input is like this: (where -1 is delimiter for two sequences A and B)
1 2 4 3 6 5 -1 2 -1
Here goes my code (explanation done via comments):
List<int> result = new List<int>(); // list for sequence A
List<int> values = new List<int>(); // list for holding value to remove
var input = Console.ReadLine().Split().Select(int.Parse).ToArray();
var len = Array.IndexOf(input, -1); // getting index of the first -1 (delimiter)
result = input.ToList(); // converting input array to List
result.RemoveRange(len, input.Length - len); // and deleting everything beyond first delimiter (including it)
for (var i = len + 1; i < input.Length - 1; i++) // for the number of elements in the sequence B
{
for (var j = 0; j < result.Count; j++) // going through all elmnts in sequence A
{
if (j % input[i] == 0) // if index is divisible by B[i]
{
values.Add(result[j]); // adding associated value to List<int> values
}
}
foreach (var value in values) // after all elements in sequence A have been looked upon, now deleting those who apply to criteria
{
result.Remove(value);
}
}
But the problem is that I'm only passing 5/11 tests cases. The 25% is 'Wrong result' and the rest 25% - 'Timed out'. I understand that my code is probably very badly written, but I really can't get to understand how to improve it.
So, if someone more experienced could explain (clarify) next points to me it would be very cool:
1. Am I doing parsing from the console input right? I feel like it could be done in a more elegant/efficient way.
2. Is my logic of getting value which apply to criteria and then storing them for later deleting is efficient in terms of performance? Or is there any other way to do it?
3. Why is this code not passing all test-cases or how would you change it in order to pass all of them?
I'm writing the answer once again, since I have misunderstood the problem completely. So undoubtly the problem in your code is a removal of elements. Let's try to avoid that. Let's try to make a new array C, where you can store all the correct numbers that should be left in the A array after each removal. So if index id is not divisible by B[i], you should add A[id] to the array C. Then, after checking all the indices with the B[i] value, you should replace the array A with the array C and do the same for B[i + 1]. Repeat until you reach the end of the array B.
The algorithm:
1. For each value in B:
2. For each id from 1 to length(A):
3. If id % value != 0, add A[id] to C
4. A = C
5. Return A.
EDIT: Be sure to make a new array C for each iteration of the 1. loop (or clear C after replacing A with it)
I was curious as to how a foreach loop in C# iterates over a multidimensional array. In the following code, the second nested for loop was originally a foreach which would give the incorrect position of the pitches placed in the loop. I know it's kind of difficult to intuit what it does, but it's basically this: Pitches are put into a multidimensional array (here, numVoices is 2 and exLength is 10) so that you will have a 2x10 array of pitches; each of these rows of pitches are then played at the same time by the MIDI output device. When I used a foreach to then put the pitches' names into a string so that I could display what pitches were in what place inside of the grid, the foreach would display them in the "wrong" order (i.e., [0,3] in the pitch grid was not what was printed in the string). Using a nested for, this problem disappeared. I tried to recreate this with a smaller example of a 2D list of ints (code below) but it gives the "right" answer this time. Why?
//put pitches into grid
//numVoices = 2, exLength = 10 (10 notes long, 2 voices)
for (int i = 0; i < numVoices; i++ )
{
for(int j = 0; j < exLength; j++)
{
//here we generate random pitches in different octaves
//depending on the voice (voice 2 is in octave
//below voice 1, etc)
randnum = (random.Next(100 - (i * 13), 112 - (i * 13)));
melodyGrid[j, i] = (Pitch)randnum;
}
}
for (int i = 0; i < numVoices; i++)
{
for (int j = 0; j < exLength; j++)
{
//this down here makes it more readable for
//humans
//e.g. "FSharp5" becomes "F#5"
noteNames += String.Format("{0, -6}", melodyGrid[j,i].ToString().Replace("Sharp", "#").Replace("Flat", "b"));
}
noteNames += "\r\n"; //lower voices are just separated by newlines
}
Console.WriteLine(noteNames);
The following code works "correctly," however:
int[,] nums = { {1, 2, 3},
{4, 5, 6},
{7, 8 ,9} };
foreach (int i in nums)
{
Console.Write("{0} ", i);
}
Is it possible I was just making a semantic mistake? Or do foreach loops iterate through arrays in differing manners?
I was curious as to how a foreach loop in C# iterates over a multidimensional array.
As always for questions like this, the ultimate authority is the C# language specification. In this case, section 8.8.4:
The order in which foreach traverses the elements of an array, is as follows: For single-dimensional arrays elements are traversed in increasing index order, starting with index 0 and ending with index Length – 1. For multi-dimensional arrays, elements are traversed such that the indices of the rightmost dimension are increased first, then the next left dimension, and so on to the left.
Now, compare that with how you're iterating with your for statements:
for (int i = 0; i < numVoices; i++ )
{
for(int j = 0; j < exLength; j++)
{
...
melodyGrid[j, i] = (Pitch)randnum;
In other words, you're incrementing the leftmost dimension first... so yes, this will give a different result from foreach. If you want to use foreach but get the same iteration order, you'll need to switch the indexes for voices and length. Alternatively, if you want to keep the same order of indexes, just use the for loop and be happy with it.
Recently i faced a question in
C#,question is:-
There are three int arrays
Array1={88,65,09,888,87}
Array2={1,49,921,13,33}
Array2={22,44,66,88,110}
Now i have to get array of highest 5 from all these three arrays.What is the most optimized way of doing this in c#?
The way i can think of is take an array of size 15 and add array elements of all three arrays and sort it n get last 5.
An easy way with LINQ:
int[] top5 = array1.Concat(array2).Concat(array3).OrderByDescending(i => i).Take(5).ToArray();
An optimal way:
List<int> highests = new List<int>(); // Keep the current top 5 sorted
// Traverse each array. No need to put them together in an int[][]..it's just for simplicity
foreach (int[] array in new int[][] { array1, array2, array3 }) {
foreach (int i in array) {
int index = highests.BinarySearch(i); // where should i be?
if (highests.Count < 5) { // if not 5 yet, add anyway
if (index < 0) {
highests.Insert(~index, i);
} else { //add (duplicate)
highests.Insert(index, i);
}
}
else if (index < 0) { // not in top-5 yet, add
highests.Insert(~index, i);
highests.RemoveAt(0);
} else if (index > 0) { // already in top-5, add (duplicate)
highests.Insert(index, i);
highests.RemoveAt(0);
}
}
}
Keep a sorted list of the top-5 and traverse each array just once.
You may even check the lowest of the top-5 each time, avoiding the BinarySearch:
List<int> highests = new List<int>();
foreach (int[] array in new int[][] { array1, array2, array3 }) {
foreach (int i in array) {
int index = highests.BinarySearch(i);
if (highests.Count < 5) { // if not 5 yet, add anyway
if (index < 0) {
highests.Insert(~index, i);
} else { //add (duplicate)
highests.Insert(index, i);
}
} else if (highests.First() < i) { // if larger than lowest top-5
if (index < 0) { // not in top-5 yet, add
highests.Insert(~index, i);
highests.RemoveAt(0);
} else { // already in top-5, add (duplicate)
highests.Insert(index, i);
highests.RemoveAt(0);
}
}
}
}
The most optimized way for a fixed K=5 is gong through all arrays five times, picking the highest element not taken so far on each pass. You need to mark the element that you take in order to skip it on subsequent passes. This has the complexity of O(N1+N2+N3) (you go through all N1+N2+N3 elements five times), which is as fast as it can get.
You can combine the arrays using LINQ, sort them, then reverse.
int[] a1 = new int[] { 1, 10, 2, 9 };
int[] a2 = new int[] { 3, 8, 4, 7 };
int[] a3 = new int[] { 2, 9, 8, 4 };
int[] a4 = a1.Concat(a2).Concat(a3).ToArray();
Array.Sort(a4);
Array.Reverse(a4);
for (int i = 0; i < 5; i++)
{
Console.WriteLine(a4[i].ToString());
}
Console.ReadLine();
Prints: 10, 9, 9, 8, 8 from the sample I provided as input for the arrays.
Maybe you could have an array of 5 elements which would be the "max values" array.
Initially fill it with the first 5 values, which in your case would just be the first array. Then loop through the rest of the values. For each value, check it against the 5 max values from least to greatest. If you find the current value from the main list is greater than the value in the max values array, insert it above that element in the array, which would push the last element out. At the end you should have an array of the 5 max values.
For three arrays of length N1,N2,N3, the fastest way should be combining the 3 arrays, and then finding the (N1+N2+N3-4)th order statistic using modified quick sort.
In the resultant array, the elements with indices (N1+N2+N3-5) to the maximum (N1+N2+N3-1) should be your 5 largest. You can also sort them later.
The time complexity of this approach is O(N1+N2+N3) on average.
Here are the two ways for doing this task. The first one is using only basic types. This is the most efficient way, with no extra loop, no extra comparison, and no extra memory consumption. You just pass the index of elements that need to be matched with another one and calculate which is the next index to be matched for each given array.
First Way -
http://www.dotnetbull.com/2013/09/find-max-top-5-number-from-3-sorted-array.html
Second Way -
int[] Array1 = { 09, 65, 87, 89, 888 };
int[] Array2 = { 1, 13, 33, 49, 921 };
int[] Array3 = { 22, 44, 66, 88, 110 };
int [] MergeArr = Array1.Concat(Array2).Concat(Array3).ToArray();
Array.Sort(MergeArr);
int [] Top5Number = MergeArr.Reverse().Take(5).ToArray()
Taken From -
Find max top 5 number from three given sorted array
Short answer: Use a SortedList from Sorted Collection Types in .NET as a min-heap.
Explanation:
From the first array, add 5 elements to this SortedList/min-heap;
Now iterate through all the rest of the elements of arrays:
If an array element is bigger than the smallest element in min-heap then remove the min element and push this array element in the heap;
Else, continue to next array element;
In the end, your min-heap has the 5 biggest elements of all arrays.
Complexity: Takes Log k time to find the minimum when you have a SortedList of k elements. Multiply that by total elements in all arrays because you are going to perform this 'find minimum operation' that many times.
Brings us to overall complexity of O(n * Log k) where n is the total number of elements in all your arrays and k is the number of highest numbers you want.
I have some tasks about sorting arrays in C#. I've been trying everything I could think of - no luck.
The task is to sort an array of integers by known sorting algorithms (insertion, selection, bubble, quick). Thing is, I have to sort ONLY the smallest M elements.
Example: I have an array of 7 elements 2 9 8 3 4 15 11 and I need to sort the smallest 3 elements so that my array becomes 2 3 4 9 8 15 11.
Please help, I can't seem to find anything neither here in SO, nor anywhere through Google. I don't ask to do all the algorithms for me, I just need one of those just to get hold on how's that possible.
E: Thank you for your thoughts. I've reviewed all of your recommendations and have accomplished to make an insertion sort like that:
static int[] insertSort(int[] arr, out int swaps, out int checks) {
int step = 0;
swaps = 0;
checks = 0;
for (int i = 0; i < arr.Length; i++) {
int min = arr[i], minind = i;
for (int j = i + 1; j < arr.Length; j++) {
checks++;
if (arr[j] < min) {
min = arr[j];
minind = j;
}
}
int temp = arr[minind];
if (step < M) {
for (int j = minind; j > i; j--) {
swaps++;
arr[j] = arr[j - 1];
}
arr[i] = temp;
swaps++;
step++;
}
}
return arr;
}
Swaps and checks - requirement for my application.
P.S. I've seen many times that SO doesn't like to do homework for someone. That's why I haven't asked for code, I've just asked for thoughts on how to accomplish that.
Thanks again for those who have helped me out here.
Since there is no efficiency limitations:
Set i to 0.
Look for the minimum among the not sorted elements.
Insert it into the position i, shift the array.
Increment i.
Repeat M times.
Complexity is O(N * M).
Without seeing your implementation, this is hard to answer. There are many ways to do this, and most are straight-forward.
Here are a few ideas though:
Create a "temporary" array that only holds the numbers to sort, sort it, then replace in original array (probably a sub-optimal solution)
Use a for loop that iterates the number of times you need (3 or whatever). This is probably the best solution
Post your code here on SO and some naive person will probably give you a solution so you don't have to do your schoolwork yourself. (This is a lazy and unbecoming solution)
I think here is what you are looking for, this is an example sorting of array ascending based on specific indixes.
int startIndex=2;
int endIndex=5;
int[] elements=new int[7];
elements[0]=2;
elements[1]=9;
elements[2]=8;
elements[3]=3;
elements[4]=4;
elements[5]=15;
elements[6]=11;
for (int a=startIndex-1;a<endIndex;a++){
for(int b=startIndex-1;b<endIndex;b++){
if (elements[a]<elements[b]){
int temp =elements[a];
elements[a]=elements[b];
elements[b]=temp;
}
}
}
for (int c=0;c<elements.Length;c++){
Console.Write(elements[c]+",");
}
Just change the "<" to ">" if you want to sort it desc.
You'd want to take a look at what sorting algorithm you're required to use. Say for example we're using one that uses a for loop. Most cases you'd see something like this
for(int i = 0; i < arrayName.length(); i++)
{}
In your case, just change the parameters of the for loop
for(int i = 0; i < M; i++)
{}
Where M is less than arrayName.length(); and is the number of positions from the beginning you would like to sort.
The rest of the array, untouched, should remain the same.
Couple things. Most sorting algorithms use array.length as the maximum range.
Could you just use m there instead? ie
for (int i = 0; i < m; i++)
Also, you could use a temporary array of the first m characters, sort it, then reassign.
int[] temp;
for (int i = 0; i < m; i++)
{
temp[i] = realArray[i];
}
//sort, then
for (int i = 0; i < m; i++)
{
realArray[i] = temp[i];
}
I would sort the full array and put it into the an other one.
Truncate the new array to only keep the smallest x elements.
Get the largest number from that array (in your example, 4).
Loop through the initial array and append all numbers that are higher.
Input: 2 9 8 3 4 15 11
Sort all: 2 3 4 8 9 11 15
Truncate: 2 3 4
Get highest value from this array (4)
Loop through original array and append
Is 2 higher than 4? no
Is 9 higher than 4? yes, append (we now have: 2 3 4 9)
Is 8 higher than 4? yes, append (we now have: 2 3 4 9 8)
Is 3 higher than 4? no
Is 4 higher than 4? no
Is 15 higher than 4? yes, append (we now have: 2 3 4 9 8 15)
Is 11 higher than 4? yes, append (we now have: 2 3 4 9 8 11)
*This is not the most efficient way and might cause problems if you have duplicate numbers
Any prescriptions on using LINQ?
int a[] = new int[] {2, 9, 8, 3, 4, 15, 11};
const int M = 5;
a = a.Take(M).OrderBy(e => e).ToArray(); // EDIT: Added .ToArray()