What's wrong with my merge sort implementation [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm sure I'm making an incredibly silly mistake but I've been at this for hours and I just want my code to sort prettily... Something is going wrong with implementation when odd numbers come into the equation.
Below is my MergeSplit method:
static List<Motor> MergeSplit(List<int> ListX)
{
int n = ListX.Count;
if (n <= 1)
return ListX;
List<int> left = new List<int>();
List<int> right = new List<int>();
for (int i = 0; i < n; i++)
{
if (i < (n / 2))
left.Add(ListX[i]);
else
right.Add(ListX[i]);
}
left = MergeSplit(left);
right = MergeSplit(right);
return Merge(left, right);
}
And here is the Merge method:
static List<int> Merge(List<int> ListX, List<int> ListY)
{
List<int> result = new List<int>();
int i = 0;
while (ListX.Count > i && ListY.Count > i)
{
if (ListX[i] > ListY[i])
{
result.Add(ListY[i]);
result.Add(ListX[i]);
}
else
{
result.Add(ListX[i]);
result.Add(ListY[i]);
}
i++;
}
//If odd, add the rest to the result
if (ListX.Count > ListY.Count)
result.Add(ListX[ListX.Count - 1]);
else if (ListY.Count > ListX.Count)
result.Add(ListY[ListY.Count - 1]);
return result;
}
Thanks for your help!
Update
The algorithm just doesnt sort correctly with certain inputs

The problem is your Merge routine
You are comparing the left and right and adding them to the list respectively, where you should be comparing the heads, and adding the lowest to the result, and removing that head respectively for the next comparison
This is the pseudo code from wiki https://en.wikipedia.org/wiki/Merge_sort
while left is not empty and right is not empty do
if first(left) ≤ first(right) then
append first(left) to result
left := rest(left)
else
append first(right) to result
right := rest(right)
You can see why thats important here
As you can see its actually comparing the first left and first right, then adding them to the result and removing that item from the list. which is vastly different from what you are doing. you either need 2 index variables, or remove the items from the list
while (listX.Count > 0 && listY.Count > 0)
if (listX[0] > listY[0])
{
result.Add(listY[0]);
listY.RemoveAt(0);
}
else
{
result.Add(listX[0]);
listX.RemoveAt(0);
}
if (listX.Count > 0)
result.AddRange(listX);
else if (listY.Count > 0)
result.AddRange(listY);
Just for fun, i found this was easier to play with queues, they seem to like this sort of thing
private static Queue<int> Merge(Queue<int> left, Queue<int> right)
{
var result = new Queue<int>();
while (left.Count > 0 && right.Count > 0)
result.Enqueue(left.Peek() > right.Peek() ? right.Dequeue() : left.Dequeue());
foreach (var item in left)
result.Enqueue(item);
foreach (var item in right)
result.Enqueue(item);
return result;
}
private static Queue<int> MergeSplit(Queue<int> list)
{
var n = list.Count;
if (n <= 1)
return list;
var left = new Queue<int>();
var right = new Queue<int>();
for (var i = 0; i < n; i++)
if (i < n / 2)
left.Enqueue(list.Dequeue());
else
right.Enqueue(list.Dequeue());
left = MergeSplit(left);
right = MergeSplit(right);
return Merge(left, right);
}
Usage
var list = new List<int> { 8, 7, 6, 4, 43, 23, 435, 76, 7, 7877, 5, 421, 2 };
var results = MergeSplit(new Queue<int>(list));
Console.WriteLine(string.Join(", ", results));
Output
2, 4, 5, 6, 7, 7, 8, 23, 43, 76, 421, 435, 7877
Full Demo Here

Related

remove sequential numbers from int array / int list [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
im generating a list of numbers such as 100 101 102 103 110 111 112 130 131 132 and i want to remove numbers that are sequential till the next number but with a specific tolerance...
so with a tolerance of 3 for example my output will be
100 102
110 112
130 132
the tolerance value must be adjustable...
ADRS is my list of int's,
ADRSC is my list of strings after being cleaned
for (int v = 0; v <= ADRS.Count - 2; v++)
{
if (v == 0) //adds first number
ADRSC.Add(ADRS[v].ToString());
if (ADRS[v]-(int.Parse(ADRSC.Last())) > 3)
{
ADRSC.Add(ADRS[v].ToString());
}
}
in short:
i want to remove the numbers inbetween, that have a difference of under 3
This worked
adrCache = Nums.First();
for (int x=1; x < Nums.Count-2; x++)
{
if ((Nums[x+1] - adrCache) <= 4)
{
adrCache = Nums[x+1];
Nums[x] = 0; //ill then just remove all zeros
}
else {
adrCache = Nums[x];
}
}
Here's a shot at what you're looking for, but the question is a little unclear to me:
public static List<int> RemoveSequential(List<int> items, int tolerance)
{
if (items == null || items.Count < 2) return items;
var result = new List<int> {items.First()};
var sequenceCount = 0;
for(int i = 1; i < items.Count; i++)
{
if (Math.Abs(items[i] - items[i - 1]) == 1)
{
sequenceCount++;
if (sequenceCount == tolerance - 1)
{
result.Add(items[i]);
}
}
else
{
sequenceCount = 0;
result.Add(items[i]);
}
}
return result;
}
Usage would look like:
public static void Main(string[] args)
{
var input = new List<int> {100, 101, 102, 103, 110, 111, 112, 130, 131, 132};
var output = RemoveSequential(input, 3);
Console.WriteLine($"Input: {string.Join(", ", input)}");
Console.WriteLine($"Output: {string.Join(", ", output)}");
GetKeyFromUser("\nDone! Press any key to exit...");
}
Output

How can I find the size of the largest subset where their maximum difference is 5?

I have an array of numbers, and I am trying to find the size of the largest subset, such that all numbers in the subset are less than 5 apart. They can assume to be sorted, if necessary.
For example:
[1, 2, 5, 6, 8, 9, 15]
4 is the largest subset. (5, 6, 8, 9)
I expect that there is a simple LINQ answer, but I am not thinking of it. I could sort it and then iterate through it with each starting number, keeping track of the most from that number, but that seems very ugly and inefficient. Any ideas?
Clarification on what I want to avoid:
(1,2,5) - 3
(2,5,6) - 3
(5,6,8,9) - 4
(6,8,9) - 3
(8,9) - 2
(9) - 1
(15) - 1
This problem can be solved by maintaining two pointers left and right.
Suppose you have a sorted array arrayof n numbers.
left = 0
right = 0
ans = INT_MIN
while right == n :
if array[right] - array[left] < 5:
right++
else:
left++
ans = max(right - left + 1, ans)
Start by making the biggest possible set at the beginning of the sorted list. Then keep removing values from the front until the next one fits, and add as many as possible after the next one too. This makes a new set. Keep track of the largest at any given moment.
Something like this in C#:
static IEnumerable<T[]> CloseSublists<T>(this IEnumerable<T> values, Func<T, T, bool> isClose) where T : IComparable<T> {
var window = new Queue<T>();
var enumerator = values.GetEnumerator();
if (!enumerator.MoveNext()) {
return;
}
bool more;
do {
window.Enqueue(enumerator.Current);
} while ((more = enumerator.MoveNext()) && isClose(window.Peek(), enumerator.Current));
yield return window.ToArray();
while (more) {
do {
window.Dequeue();
} while (window.Count != 0 && !isClose(window.Peek(), enumerator.Current));
do {
window.Enqueue(enumerator.Current);
} while ((more = enumerator.MoveNext()) && isClose(window.Peek(), enumerator.Current));
yield return window.ToArray();
}
}
and
public static T MaxBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> key) where TKey : IComparable<TKey> {
var enumerator = items.GetEnumerator();
enumerator.MoveNext();
var max = enumerator.Current;
TKey maxKey = key(max);
while (enumerator.MoveNext()) {
T current = enumerator.Current;
TKey currentKey = key(current);
int relation = currentKey.CompareTo(maxKey);
if (relation > 0) {
max = current;
maxKey = currentKey;
}
}
return max;
}
used as:
int[] x = {1, 2, 5, 6, 8, 9, 15};
x.CloseSublists((a, b) => b < a + 5).MaxBy(l => l.Length)
Based on answer of Prince, I rewrote it to C# and improved a bit:
protected int MaxSubLen(int[] arr, int diffLessThan)
{
int l = 0, r = 0;
while (r < arr.Length)
{
if (arr[r] - arr[l] >= diffLessThan)
{
++l;
}
++r;
}
return r - l;
}
and, just for fun, the sequence returning generic version:
protected IEnumerable<T> MaxSubarray<T>(IList<T> arr, Func<T, T, bool> isClose_L_R)
{
int l = 0, r = 0, start = 0;
while (r < arr.Count)
{
if (isClose_L_R(arr[l], arr[r]))
{
start = l;
}
else
{
++l;
}
++r;
}
for (int i = start; i < start + r - l; ++i)
{
yield return arr[i];
};
}

Order array such that all positive numbers appear first

i am writing a code in c# to sort an array, i want all the negative values in the right side and all the positive values in the left side, the should not be in decreasing order
namespace SortApp
{
class Program
{
static void Main(string[] args)
{
int[] newInt = new int[] { 5, -2, -1, -4, -20, 6, 7, -14, 15, -16, 8, 9, 10 };
int size = 12, i= 0; // or newInt.Length
for (i = 0; i < newInt.Length; i++)
{
if (newInt[i] < 0 && newInt[size] > 0)
{
int temp = newInt[i];
newInt[i] = newInt[size];
newInt[size] = temp;
size--;
}
}
for (i = 0; i < newInt.Length; i++)
{
Console.Write(newInt[i]);
Console.Write(" ");
}
}
}
}
but the output is something like this (-20 is on wrong side):
5 10 9 8 -20 6 7 -14 15 -16 -4 -1 -2
but the intended output is:
5 10 9 8 15 6 7 -14 -20 -16 -4 -1 -2
Why is my code not producing my intended output?
Your solution incorrectly decides when to finish the loop. Also, it unconditionally increments i in the loop header, and never decrements size even when it points to a negative number.
Here is how you fix it:
for (i = 0; i < size ; ) {
if (newInt[i] < 0 && newInt[size] >= 0) {
int temp = newInt[i];
newInt[i] = newInt[size];
newInt[size] = temp;
size--;
i++;
continue;
}
if (newInt[i] >= 0) {
i++;
}
if (newInt[size] < 0) {
size--;
}
}
Here is a demo on ideone.
You can rewrite this loop using a more readable identifiers for your left and right pointers, rather than using i and size. This would make your algorithm look more "symmetric" in the code, to recognize the symmetry in its design:
int left = 0, right = newInt.Length-1;
while (left < right) {
if (newInt[left] < 0 && newInt[right] >= 0) {
int temp = newInt[left];
newInt[left] = newInt[right];
newInt[right] = temp;
right--;
left++;
continue;
}
if (newInt[left] >= 0) {
left++;
}
if (newInt[right] < 0) {
right--;
}
}
Here is an ideone link to the alternative implementation.
Try this solution:
var newInt = new[] {5, -2, -1, -4, -20, 6, 7, -14, 15, -16, 8, 9, 10};
var solution = newInt.GroupBy(i => i > 0).
SelectMany(g => g).
ToArray();
The problem with your algorithm is that when you decrease size, you end up having newInt[size] point at a negative value, and the if block is not entered.
The general idea for a pretty easy easy solution would be to start one index, call it left at the beginning of the array, and another, called right at the end of the array.
Increment left until you find a negative number, or until left == right. When you hit a negative number, decrement right until you find a positive number, or until right == left.
If left is indexing a negative number and right is indexing a positive number, swap the two items and start incrementing left again.
The general idea, not tested:
int left = 0;
int right = a.Length-1;
while (left < right)
{
if (a[left] < 0)
{
while (right > left)
{
if (a[right] >= 0)
{
// swap here
int temp = a[left];
a[left] = a[right];
a[right] = temp;
break;
}
--right;
}
}
++left;
}
This yields the desired order with a minimum of loops
int[] newInt = new int[] { 5, -2, -1, -4, -20, 6, 7, -14, 15, -16, 8, 9, 10 };
int lt = 0;
int rt = newInt.Length - 1;
while (true) {
// Find first negative number
while (newInt[lt] >= 0 && lt < rt) {
lt++;
}
// Find last positive number
while (newInt[rt] < 0 && rt > lt) {
rt--;
}
if (lt == rt) {
break; // Finished
}
// Swap
int temp = newInt[lt];
newInt[lt] = newInt[rt];
newInt[rt] = temp;
}
//TODO: Print result
if you can use generics and linq than the easiest solution would be :
int[] newInt = new int[] { 5, -2, -1, -4, -20, 6, 7, -14, 15, -16, 8, 9, 10 };
newInt.ToList().Sort();
newInt.Reverse();
newInt = newInt.ToArray();
Hope this will help !!

inserting element into a list, if condition is met with C#

How to insert some number into the middle of the list, if there is no such number present?
In the example below I'm trying to insert number 4
List<int> list1 = new List<int>(){ 0, 1, 2, 3, 5, 6 };
int must_enter = 4;
if (!list1.Contains(must_enter))
{
list1.Add(must_enter);
}
As the result number will be entered at the end of the list, but I want it right after 3 (before 5).
please note that due to project's specifics I can't use sorted list, but all numbers in the list are guaranteed to be in ascending order (0,2,6,9,10,...)
EDIT: I knew about an error and that's what I did:
List<int> list0 = new List<int>() { 1, 2, 3, 5, 6 };
int must_enter = 7;
if (!list0.Contains(must_enter))
{
if (must_enter < list0.Max())
{
int result = list0.FindIndex(item => item > must_enter || must_enter > list0.Max());
list0.Insert(result, must_enter);
}
else
{
list0.Add(must_enter);
}
}
edit2: anyway I've switched to BinarySearch method due to several factors. Everyone thanks for your help!
You could do something like this:
int index = list1.BinarySearch(must_enter);
if (index < 0)
list1.Insert(~index, must_enter);
This way you will keep the list sorted with the best possible performance.
You can do:
list1.Add(must_enter);
And then order the list:
list1 = list1.OrderBy(n => n).ToList();
The result will be:
0, 1, 2, 3, 4, 5, 6
EDIT:
Or use an extesion method:
static class Utility
{
public static void InsertElement(this List<int> list, int n)
{
if(!list.Contains(n))
{
for(int i = 0; i < list.Count; i++)
{
if(list[i] > n)
{
list.Insert(i-1, n);
break;
}
if(i == list.Count - 1)
list.Add(n);
}
}
}
}
And then:
list1.InsertElement(must_enter);
You are looking for
list1.Insert(index, must_enter);
To insert an element at a specific index rather than at the end of the list.
You'll have to find the index to insert at first which is easily done with a binary search. Start with the value in the middle of the list and compare it to your number to insert. If it's greater, search the lower half of the list, if it's more, search the upper half of the list. Repeat the process, dividing the list in half each time until you find the spot where the item before is less than the one you are inserting and the item after is more than the one you are inserting. (edit: of course, if you list is always very small, it's probably less hassle just to iterate through the list from the beginning to find the right spot!)
List<int> list1 = new List<int>() { 0, 1, 2, 3, 5, 6 };
int must_enter = 4;
for (int i = 0; i < list1.Count; i++)
{
if (must_enter >= list1[i])
{
list1.Insert(i + 1, must_enter);
}
}
Edit: I like sarwar026, implementation better.
list1.Insert(4, 4)
List<T>.Insert Method - Inserts an element into the List at the specified index.
Quick Note-
the Insert instance method on the List type does not have good performance in many cases. because for Insert, list has to adjust the following elements.
here is the original post from where i got this answer try it out may help you : Finding best position for element in list
List<int> list = new List<int>{0,2,6,9,10};
for (int i = 0; i < list1.Count; i++)
{
int index = list.BinarySearch(i);
if( i < 0)
{
int insertIndex = ~index;
list.Insert(insertIndex, i);
}
}
just for one missing element as op needs
int index = list.BinarySearch(4);
if( index < 0)
{
int insertIndex = ~index;
list.Insert(insertIndex, 4);
}
or
List<int> list1 = new List<int>() { 0,2,6,9,10 };
int must_enter = 4;
for (int i = 0; i < list1.Count; i++)
{
if (!list1.Contains(i))
{
list1.Insert(i , i);
}
}
just for one element as op needs
if (!list1.Contains(4))
{
list1.Insert(4 , 4);
}
List<int> list1 = new List<int>(){ 0, 1, 2, 3, 5, 6 };
int must_enter = 4;
if (!list1.Contains(must_enter))
{
int result = list.FindIndex(item => item > must_enter);
if(result!=-1)
list1.Insert(result, must_enter);
else // must_enter is not found
{
if(must_enter > list.Max()) // must_enter > max value of list
list1.Add(must_enter);
else if(must_enter < list.Min()) // must_enter < min value of list
list1.Insert(0, must_enter);
}
}
First, find the index of the number which is greater than must_enter(4) and then insert the must_enter to that position
if (!list1.Contains(must_enter))
{
SortedSet<int> sorted = new SortedSet<int>( list1 );
sorted.Add( must_enter );
list1 = sorted.ToList();
}

Merging two lists into one and sorting the items

Is there a way to merge(union without dupes) two given lists into one and store the items in sorted way by using ONE for loop?
Also, i am looking for a solution which does not makes use of API methods ( like, union, sort etc).
Sample Code.
private static void MergeAndOrder()
{
var listOne = new List<int> {3, 4, 1, 2, 7, 6, 9, 11};
var listTwo = new List<int> {1, 7, 8, 3, 5, 10, 15, 12};
//Without Using C# helper methods...
//ToDo.............................
//Using C# APi.
var expectedResult = listOne.Union(listTwo).ToList();
expectedResult.Sort();//Output: 1,2,3,4,5,6,7,8,9,10,11,12,15
//I need the same result without using API methods, and that too by iterating over items only once.
}
PS: I have been asked this question in an interview, but couldn't find answer as yet.
Why can't you use the api methods? Re-inventing the wheel is dumb. Also, it's the .ToList() call that's killing you. Never call .ToList() or .ToArray() until you absolutely have to, because they break your lazy evaluation.
Do it like this and you'll enumerate the lists with the minimum amount necessary:
var expectedResult = listOne.Union(listTwo).OrderBy(i => i);
This will do the union in one loop using a hashset, and lazy execution means the base-pass for the sort will piggyback on the union. But I don't think it's possible finish the sort in a single iteration, because sorting is not a O(n) operation.
Without the precondition that both lists are sorted before the merge + sort operation, you can't do this in O(n) time (or "using one loop").
Add that precondition and the problem is very easy.
Keep two iterators, one for each list. On each loop, compare the element from each list and choose the smaller. Increment that list's iterator. If the element you are about to insert in the final list is already the last element in that list, skip the insert.
In pseudocode:
List a = { 1, 3, 5, 7, 9 }
List b = { 2, 4, 6, 8, 10 }
List result = { }
int i=0, j=0, lastIndex=0
while(i < a.length || j < b.length)
// If we're done with a, just gobble up b (but don't add duplicates)
if(i >= a.length)
if(result[lastIndex] != b[j])
result[++lastIndex] = b[j]
j++
continue
// If we're done with b, just gobble up a (but don't add duplicates)
if(j >= b.length)
if(result[lastIndex] != a[i])
result[++lastIndex] = a[i]
i++
continue
int smallestVal
// Choose the smaller of a or b
if(a[i] < b[j])
smallestVal = a[i++]
else
smallestVal = b[j++]
// Don't insert duplicates
if(result[lastIndex] != smallestVal)
result[++lastIndex] = smallestVal
end while
private static void MergeTwoSortedArray(int[] first, int[] second)
{
//throw new NotImplementedException();
int[] result = new int[first.Length + second.Length];
int i=0 , j=0 , k=0;
while(i < first.Length && j <second.Length)
{
if(first[i] < second[j])
{
result[k++] = first[i++];
}
else
{
result[k++] = second[j++];
}
}
if (i < first.Length)
{
for (int a = i; a < first.Length; a++)
result[k] = first[a];
}
if (j < second.Length)
{
for (int a = j; a < second.Length; a++)
result[k++] = second[a];
}
foreach (int a in result)
Console.Write(a + " ");
Console.WriteLine();
}
Using iterators and streaming interface the task is not that complicated:
class MergeTwoSortedLists
{
static void Main(string[] args) {
var list1 = new List<int?>() {
1,3,5,9,11
};
var list2 = new List<int?>() {
2,5,6,11,15,17,19,29
};
foreach (var c in SortedAndMerged(list1.GetEnumerator(), list2.GetEnumerator())) {
Console.Write(c+" ");
}
Console.ReadKey();
}
private static IEnumerable<int> SortedAndMerged(IEnumerator<int?> e1, IEnumerator<int?> e2) {
e2.MoveNext();
e1.MoveNext();
do {
while (e1.Current < e2.Current) {
if (e1.Current != null) yield return e1.Current.Value;
e1.MoveNext();
}
if (e2.Current != null) yield return e2.Current.Value;
e2.MoveNext();
} while (!(e1.Current == null && e2.Current == null));
}
}
Try this:
public static IEnumerable<T> MergeWith<T>(IEnumerable<T> collection1, IEnumerable<T> collection2,
IComparer<T> comparer)
{
using (var enumerator1 = collection1.GetEnumerator())
using (var enumerator2 = collection2.GetEnumerator())
{
var isMoveNext1 = enumerator1.MoveNext();
var isMoveNext2 = enumerator2.MoveNext();
do
{
while (comparer.Compare(enumerator1.Current, enumerator2.Current) < 0 || !isMoveNext2)
{
if (isMoveNext1)
yield return enumerator1.Current;
else
break;
isMoveNext1 = enumerator1.MoveNext();
}
if (isMoveNext2)
yield return enumerator2.Current;
isMoveNext2 = enumerator2.MoveNext();
} while (isMoveNext1 || isMoveNext2);
}
}
You could write a loop that merges and de-dups the lists and uses a binary-search approach to insert new values into the destination list.
var listOne = new List<int> { 3, 4, 1, 2, 7, 6, 9, 11 };
var listTwo = new List<int> { 1, 7, 8, 3, 5, 10, 15, 12 };
var result = listOne.ToList();
foreach (var n in listTwo)
{
if (result.IndexOf(n) == -1)
result.Add(n);
}
The closest solution I see would be to allocate an array knowing that integers are bounded to some value.
int[] values = new int[ Integer.MAX ]; // initialize with 0
int size1 = list1.size();
int size2 = list2.size();
for( int pos = 0; pos < size1 + size2 ; pos++ )
{
int val = pos > size1 ? list2[ pos-size1 ] : list1[ pos ] ;
values[ val ]++;
}
Then you can argue that you have the sorted array in a "special" form :-) To get a clean sorted array, you need to traverse the values array, skip all position with 0 count, and build the final list.
This will only work for lists of integers, but happily that is what you have!
List<int> sortedList = new List<int>();
foreach (int x in listOne)
{
sortedList<x> = x;
}
foreach (int x in listTwo)
{
sortedList<x> = x;
}
This is using the values in each list as the index position at which to store the value. Any duplicate values will overwrite the previous entry at that index position. It meets the requirement of only one iteration over the values.
It does of course mean that there will be 'empty' positions in the list.
I suspect the job position has been filled by now though.... :-)

Categories