Building list with distinct elements that total defined sum - c#

I am looking to build a list with distinct elements that total a defined sum. For example, given the following list with a sum of 61. How would I build a new list to have the same sum of 61, but with distinct elements as opposed to having duplicate values 4, 7, and 9?
List<int> ElementList = new List<int> { 4, 4, 4, 4, 6, 7, 7, 7, 9, 9}
static void BuildDistinctList(List<int> InputList)
{
InputList.Sort();
Dictionary<int, int> DistinctValues = new Dictionary<int, int>();
for (int i = 0; i < InputList.Count; i++)
{
if(!DistinctValues.ContainsValue(InputList[i]))
{
DistinctValues.Add(i, InputList[i]);
}
else
{
//My thinking at this point would be to no longer mess with
//the value stored inside of the dictionary as it is fixed from this point
//forward, and to borrow from the remaining list values.
FindNewDistinctValue(MIN, MAX, DistinctValues, InputList, i, InputList.Count - i);
}
}
}
Could someone share some insight as to if I am following an appropriate direction by going this route. Or provide some tips on how to solve this.
Edit: I created the function below, and added it to the else statement in the original code provided. It seems to kind of be working but I appear to be borrowing too much from the values toward the end of the list, and am short on the total sum. This paints a better picture as to what I am looking to do. If you need me to clarify anything feel free to ask.
static void FindNewDistinctValue(int Min, int Max, Dictionary<int, int> DistinctValues, List<int> InputList, int CurrentIndex, int BorrowIndex)
{
for (int i = Min + 1; i < Max; i++)
{
if (!DistinctValues.ContainsValue(i))
{
int ValueToSubtractFromBorrowIndex = InputList[CurrentIndex] - i;
DistinctValues.Add(CurrentIndex, i);
InputList[BorrowIndex] = InputList[BorrowIndex] - ValueToSubtractFromBorrowIndex;
break;
}
}
}

Related

I am trying to Insert a Node in Sorted Manner in a List using Insertion Sort

I am trying to implement Bucket Sort Algorithm in C# on Linked List , but I am getting In correct results. This function should take List and Node in argument and than insert that node at correct ascending order.
I have a list [4, 7, 12, 15] and I want new Node with data 20 to be Inserted in Ascending order such as this list becomes [4,7,12,15,20].But in result I am getting this list[4,7,12,20,15] Please Help me, What's wrong in it. I guess there is some problem inside while loop. All other functions called by this function are totally working perfect
static public void InsertionSort(LinkedList list, Node s)
{
int data = s.getData();
if (list.start.getNext() == null)
list.InsertAtEnd(data);
else
{
int key = 0;
Node temp = list.start;
while (temp.getNext() != null)
{
temp = temp.getNext();
if (data >= temp.getData())
key++;
}
list.InsertAt(key, data);
}
}
Derived Function (Main Function):
LinkedList list = new LinkedList();
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtEnd(array[i]);
System.Console.WriteLine("Before Insertion");
list.Display();
Node n = new Node(20);
InsertionSort(list, n);
System.Console.WriteLine("After Insertion");
list.Display();
Output:
1: Output of The Code
The setup in the beginning is wrong.
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtBegin(array[i]);
Because you go over the initial array and add all the items at the beginning, the resulting list is sorted in the wrong order.
To start with the correct sorting, either add at the end:
int[] array = { 4, 7, 12, 15 };
for (int i = 0; i < array.Length; i++)
list.InsertAtEnd(array[i]);
or go over the initial array in the other direction:
int[] array = { 4, 7, 12, 15 };
for (int i = array.length - 1; i >= 0; i--)
list.InsertAtBegin(array[i]);
Also, your insertion algorithm isn't optimal.
With a linked list, to get to a certain element in the list is expensive because you always have to travel from the start to the correct position.
Your code first tries to find the correct place to insert the new element and then calls another method InsertAt which will have to restart traveling the list from the beginning to find the same position again.
Instead, change your code so that you directly insert the new node as soon as you have found an element with a higher value.
Thanks to all of you who tried to help me. I have correct my code. Now It's producing 100% accurate result.
static public void InsertionSort(LinkedList list, int data)
{
Node n = new Node(data);
Node temp = list.start;
Node sNode = null;
while(temp.getNext() !=null)
{
if (data >= temp.getNext().getData())
sNode = temp.getNext();
temp = temp.getNext();
}
if (sNode == null)
list.InsertAtBegin(data);
else
list.InsertAfter(sNode,n);
}

SortedSet.Remove () not working with custom comparer class

I am trying to solve a leetcode problem, where I want to get top k frequent numbers. I am trying to solve it using SortedSet for O(log n) time complexity.
My code is working for all inputs except one particular input.
public class FreqNode
{
public int number;
public int freq;
public FreqNode(int n, int f)
{
number = n;
freq = f;
}
}
class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 3, 2, 3, 1, 2, 4, 5, 5, 6, 7, 7, 8, 2, 3, 1, 1, 1, 10, 11, 5, 6, 2, 4, 7, 8, 5, 6};
TopKFrequent(arr, 10);
Console.Read();
}
static void TopKFrequent(int[] nums, int k)
{
SortedSet<FreqNode> sl = new SortedSet<FreqNode>(new MyComparer());
Dictionary<int, FreqNode> ht = new Dictionary<int, FreqNode>();
foreach (int i in nums)
{
if (ht.ContainsKey(i))
{
sl.Remove(ht[i]);
ht[i].freq += 1;
}
else
{
ht[i] = new FreqNode(i, 1);
}
sl.Add(ht[i]);
}
for (int i = 0; i < k; i++)
{
FreqNode f = sl.ElementAt(i);
Console.WriteLine(f.number);
}
}
}
public class MyComparer : IComparer<FreqNode>
{
public int Compare(FreqNode fn1, FreqNode fn2)
{
//Remove entry with same number
//Retain entries with same frequencies.
if (fn1.number == fn2.number)
{
return 0;
}
else
{
int res = fn2.freq.CompareTo(fn1.freq);
if (res == 0)
{
return 1;
}
else
{
return res;
}
}
}
}
It prints output as - 1,2,5,3,7,6,6,4,8,10
instead of - 1,2,5,3,6,7,4,8,10,11
During debugging, I noticed that Comparer code does not compare with existing entry of number 6. After further investigation, I found that, SortedSet is implemented using Red-Black tree, but I could not resolve this bug in my code.
This problem was interesting to me because I thought it might be a SortedSet bug and had me stepping through the SortedSet source. Alas, not a SortedSet bug...sorry but your IComparer is a bit wonky.
Your IComparer implementation does weird things to the comparison by first changing the criteria of sort comparison, then by inverting the comparison objects, then by changing the return value.
First your comparer is saying,
if "number" properties are equal, the nodes are equal (this is fine)
if (fn1.number == fn2.number)
{
return 0;
}
then it's saying
sort on the inverse of the freq property (switching fn2 with fn1 is probably not a good idea, and the criteria of sort has changed to the 'freq' property (probably ok))
int res = fn2.freq.CompareTo(fn1.freq);
then it's changing equal freqs to not be equal
if the result of the 'freq' comparison was equal, let's pretend like it's not equal (probably not a good idea)
if (res == 0)
{
return 1;
}
The poor SortedSet's root must be rotating!! (HAHAHA!! I made a Data Structures joke!! get it "root"? "rotating"?)
In the end, the Remove algorithm looks for the '6' as a right child because your IComparer tells it that's where it should be, meanwhile '6' is actually a left child, so the Remove algorithm misses it.
As an aside, keep in mind that the 2 '6' nodes in your SortedSet and the '6' node in your dictionary are all actually the same FreqNode reference, so you can change all of them at the same time if you needed to.
You can look at (and download) the .NET source here
then set up Visual Studio to debug sources by following the instructions here
Lastly, if you insist solving the top-k problem this way, try this comparer:
public class MyComparer : IComparer<FreqNode>
{
public int Compare(FreqNode fn1, FreqNode fn2)
{
return fn1.number == fn2.number ? fn1.freq.CompareTo(fn2.freq) : fn1.number.CompareTo(fn2.number);
}
}
Cheers and thanks for a fun debug!

Compute the lowest int which is not in a List<int>

I have a quite short List<int>:
var list = new List<int> {0, 4, 1, 3};
The list is not sorted.
I need to find the lowest integer, starting from 0, which does not belong to the list.
At the moment I use the following algorithm:
int x = 0;
while (list.Contains(x))
x++;
// In this example it must be: x = 2
The algorithm is pretty simple, but it is not linear O(n), and I need to compute this value a huge number of times using different lists.
How can I speed up this method?
Without knowing too much about your actual limitations, maybe this could be a solution:
int breakpoint = 153; // Or whatever number you've found is the breakpoint
int FirstMissingNumber(List<int> list)
IEnumerable<int> toIterateOver = list;
if (list.Count > breakpoint)
toIterateOver = new HashSet<int>(list);
int i = 0;
while (toIterateOver.Contains(i))
i++;
return i;
}
Note though that for smaller lists the overhead of creating the hashset surely is larger than the O(1) speed gain on Contains().
EDIT:
Added a breakpoint "switch", you have to manually find out where the breakpoint is in your environment though.
Why not just sort the list and loop through it to find out the missing number like below
var list = new List<int> { 0, 4, 1, 3 };
list.Sort();
for (int i = 0; i < list.Count; i++)
{
if (!list.Contains(i))
{
Console.WriteLine("Missing {0}", i);
break;
}
}

Get all sub arrays of a particular length in an array

I want help with getting the subsets of an array in C#.
All other examples could not help me much.
I want to get all the subsets of a particular size of an array.
for example if input array is {1,2,3,4} and i want all subsets of size 3,
all the unique subsets {1,2,3},{1,2,4},{2,3,4},{1,3,4} must be returned.
I am new to C# and any kind of help is much appreciated.
Check this article. This is described in detail with examples in all sorts of programming languages. I don't feel the need to copy others solutions so I will leave this as a link for you to choose from the MANY examples which should help you
Algorithm to return all combinations of k elements from n
Sounds suspiciously like a homework assignment....
Since I presume the size is variable, you'll want to use recursion. Something like:
static void Main(string[] args)
{
int[] originalList = new int[] { 1, 2, 3, 4 };
Stack<int> currentList = new Stack<int>();
List<int[]> listOfSubsets = new List<int[]>();
BuildListOfSubsets(originalList, listOfSubsets, 3, 0, currentList);
}
private static void BuildListOfSubsets(int[] originalList, List<int[]> listOfSubsets, int sizeOfSubsetList, int currentLevel, Stack<int> currentList)
{
if (currentList.Count == sizeOfSubsetList)
{
int[] copy = new int[sizeOfSubsetList];
currentList.CopyTo(copy, 0);
listOfSubsets.Add(copy);
}
else
for (int ix = currentLevel; ix < originalList.Length; ix++)
{
currentList.Push(originalList[ix]);
BuildListOfSubsets(originalList, listOfSubsets, sizeOfSubsetList, ix + 1, currentList);
currentList.Pop();
}
}
The result will be in the listOfSubsets list. See if you can find an optimization to leave the for loop early.
If you are looking for a LINQ solution, try this:
int[] originalList = { 1,2,1 };
var srcl = originalList.ToList();
List<List<int>> ans = Enumerable.Range(0, srcl.Count).SelectMany(start => Enumerable.Range(1, srcl.Count - start).Select(count => srcl.GetRange(start, count))).ToList();
If you are looking for a simpler solution, without Recursion, then this will help:
(This is a common solution to print all the subsets, to print specific scenarios, like having only subsets with length as 3, use the innermost loop)
class Program
{
static void Main(string[] args)
{
int[] arr = { 1, 2, 3, 4, 5 };
int length = arr.Length;
for (int start = 0; start < length; start++)
{
for (int end = start; end < length; end++)
{
Console.Write("{");
// printing the subset, use this to add any condition,
// like create a array and match the length, or anything
for (int print = start; print <= end; print++)
{
Console.Write(arr[print]);
}
Console.WriteLine("}");
}
}
}
}

in C#, how do I order items in a list where the "largest" values are in the middle of the list

I have been stumped on this one for a while. I want to take a List and order the list such that the Products with the largest Price end up in the middle of the list. And I also want to do the opposite, i.e. make sure that the items with the largest price end up on the outer boundaries of the list.
Imagine a data structure like this.. 1,2,3,4,5,6,7,8,9,10
In the first scenario I need to get back 1,3,5,7,9,10,8,6,4,2
In the second scenario I need to get back 10,8,6,4,2,1,3,5,7,9
The list may have upwards of 250 items, the numbers will not be evenly distributed, and they will not be sequential, and I wanted to minimize copying. The numbers will be contained in Product objects, and not simple primitive integers.
Is there a simple solution that I am not seeing?
Any thoughts.
So for those of you wondering what I am up to, I am ordering items based on calculated font size. Here is the code that I went with...
The Implementation...
private void Reorder()
{
var tempList = new LinkedList<DisplayTag>();
bool even = true;
foreach (var tag in this) {
if (even)
tempList.AddLast(tag);
else
tempList.AddFirst(tag);
even = !even;
}
this.Clear();
this.AddRange(tempList);
}
The Test...
[TestCase(DisplayTagOrder.SmallestToLargest, Result=new[]{10,14,18,22,26,30})]
[TestCase(DisplayTagOrder.LargestToSmallest, Result=new[]{30,26,22,18,14,10})]
[TestCase(DisplayTagOrder.LargestInTheMiddle, Result = new[] { 10, 18, 26, 30, 22, 14 })]
[TestCase(DisplayTagOrder.LargestOnTheEnds, Result = new[] { 30, 22, 14, 10, 18, 26 })]
public int[] CalculateFontSize_Orders_Tags_Appropriately(DisplayTagOrder sortOrder)
{
list.CloudOrder = sortOrder;
list.CalculateFontSize();
var result = (from displayTag in list select displayTag.FontSize).ToArray();
return result;
}
The Usage...
public void CalculateFontSize()
{
GetMaximumRange();
GetMinimunRange();
CalculateDelta();
this.ForEach((displayTag) => CalculateFontSize(displayTag));
OrderByFontSize();
}
private void OrderByFontSize()
{
switch (CloudOrder) {
case DisplayTagOrder.SmallestToLargest:
this.Sort((arg1, arg2) => arg1.FontSize.CompareTo(arg2.FontSize));
break;
case DisplayTagOrder.LargestToSmallest:
this.Sort(new LargestFirstComparer());
break;
case DisplayTagOrder.LargestInTheMiddle:
this.Sort(new LargestFirstComparer());
Reorder();
break;
case DisplayTagOrder.LargestOnTheEnds:
this.Sort();
Reorder();
break;
}
}
The appropriate data structure is a LinkedList because it allows you to efficiently add to either end:
LinkedList<int> result = new LinkedList<int>();
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Array.Sort(array);
bool odd = true;
foreach (var x in array)
{
if (odd)
result.AddLast(x);
else
result.AddFirst(x);
odd = !odd;
}
foreach (int item in result)
Console.Write("{0} ", item);
No extra copying steps, no reversing steps, ... just a small overhead per node for storage.
C# Iterator version
(Very simple code to satisfy all conditions.)
One function to rule them all! Doesn't use intermediate storage collection (see yield keyword). Orders the large numbers either to the middle, or to the sides depending on the argument. It's implemented as a C# iterator
// Pass forward sorted array for large middle numbers,
// or reverse sorted array for large side numbers.
//
public static IEnumerable<long> CurveOrder(long[] nums) {
if (nums == null || nums.Length == 0)
yield break; // Nothing to do.
// Move forward every two.
for (int i = 0; i < nums.Length; i+=2)
yield return nums[i];
// Move backward every other two. Note: Length%2 makes sure we're on the correct offset.
for (int i = nums.Length-1 - nums.Length%2; i >= 0; i-=2)
yield return nums[i];
}
Example Usage
For example with array long[] nums = { 1,2,3,4,5,6,7,8,9,10,11 };
Start with forward sort order, to bump high numbers into the middle.
Array.Sort(nums); //forward sort
// Array argument will be: { 1,2,3,4,5,6,7,8,9,10,11 };
long[] arrLargeMiddle = CurveOrder(nums).ToArray();
Produces: 1 3 5 7 9 11 10 8 6 4 2
Or, Start with reverse sort order, to push high numbers to sides.
Array.Reverse(nums); //reverse sort
// Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = CurveOrder(nums).ToArray();
Produces: 11 9 7 5 3 1 2 4 6 8 10
Significant namespaces are:
using System;
using System.Collections.Generic;
using System.Linq;
Note: The iterator leaves the decision up to the caller about whether or not to use intermediate storage. The caller might simply be issuing a foreach loop over the results instead.
Extension Method Option
Optionally change the static method header to use the this modifier public static IEnumerable<long> CurveOrder(this long[] nums) { and put it inside a static class in your namespace;
Then call the order method directly on any long[ ] array instance like so:
Array.Reverse(nums); //reverse sort
// Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 };
long[] arrLargeSides = nums.CurveOrder().ToArray();
Just some (unneeded) syntactic sugar to mix things up a bit for fun. This can be applied to any answers to your question that take an array argument.
I might go for something like this
static T[] SortFromMiddleOut<T, U>(IList<T> list, Func<T, U> orderSelector, bool largestInside) where U : IComparable<U>
{
T[] sortedArray = new T[list.Count];
bool add = false;
int index = (list.Count / 2);
int iterations = 0;
IOrderedEnumerable<T> orderedList;
if (largestInside)
orderedList = list.OrderByDescending(orderSelector);
else
orderedList = list.OrderBy(orderSelector);
foreach (T item in orderedList)
{
sortedArray[index] = item;
if (add)
index += ++iterations;
else
index -= ++iterations;
add = !add;
}
return sortedArray;
}
Sample invocations:
int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] sortedArray = SortFromMiddleOut(array, i => i, false);
foreach (int item in sortedArray)
Console.Write("{0} ", item);
Console.Write("\n");
sortedArray = SortFromMiddleOut(array, i => i, true);
foreach (int item in sortedArray)
Console.Write("{0} ", item);
With it being generic, it could be a list of Foo and the order selector could be f => f.Name or whatever you want to throw at it.
The fastest (but not the clearest) solution is probably to simply calculate the new index for each element:
Array.Sort(array);
int length = array.Length;
int middle = length / 2;
int[] result2 = new int[length];
for (int i = 0; i < array.Length; i++)
{
result2[middle + (1 - 2 * (i % 2)) * ((i + 1) / 2)] = array[i];
}
Something like this?
public IEnumerable<int> SortToMiddle(IEnumerable<int> input)
{
var sorted = new List<int>(input);
sorted.Sort();
var firstHalf = new List<int>();
var secondHalf = new List<int>();
var sendToFirst = true;
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current);
}
else
{
secondHalf.Add(current);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
return firstHalf.Concat(secondHalf);
}
For your specific (general) case (assuming unique keys):
public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
var sorted = new List<TU>(input.Select(getSortKey));
sorted.Sort();
var firstHalf = new List<TU>();
var secondHalf = new List<TU>();
var sendToFirst = true;
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current);
}
else
{
secondHalf.Add(current);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
sorted = new List<TU>(firstHalf.Concat(secondHalf));
//This assumes the sort keys are unique - if not, the implementation
//needs to use a SortedList<TU, T>
return sorted.Select(s => input.First(t => s.Equals(getSortKey(t))));
}
And assuming non-unique keys:
public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey)
{
var sendToFirst = true;
var sorted = new SortedList<TU, T>(input.ToDictionary(getSortKey, t => t));
var firstHalf = new SortedList<TU, T>();
var secondHalf = new SortedList<TU, T>();
foreach (var current in sorted)
{
if (sendToFirst)
{
firstHalf.Add(current.Key, current.Value);
}
else
{
secondHalf.Add(current.Key, current.Value);
}
sendToFirst = !sendToFirst;
}
//to get the highest values on the outside just reverse
//the first list instead of the second
secondHalf.Reverse();
return(firstHalf.Concat(secondHalf)).Select(kvp => kvp.Value);
}
Simplest solution - order the list descending, create two new lists, into the first place every odd-indexed item, into the other every even indexed item. Reverse the first list then append the second to the first.
Okay, I'm not going to question your sanity here since I'm sure you wouldn't be asking the question if there weren't a good reason :-)
Here's how I'd approach it. Create a sorted list, then simply create another list by processing the keys in order, alternately inserting before and appending, something like:
sortedlist = list.sort (descending)
biginmiddle = new list()
state = append
foreach item in sortedlist:
if state == append:
biginmiddle.append (item)
state = prepend
else:
biginmiddle.insert (0, item)
state = append
This will give you a list where the big items are in the middle. Other items will fan out from the middle (in alternating directions) as needed:
1, 3, 5, 7, 9, 10, 8, 6, 4, 2
To get a list where the larger elements are at the ends, just replace the initial sort with an ascending one.
The sorted and final lists can just be pointers to the actual items (since you state they're not simple integers) - this will minimise both extra storage requirements and copying.
Maybe its not the best solution, but here's a nifty way...
Let Product[] parr be your array.
Disclaimer It's java, my C# is rusty.
Untested code, but you get the idea.
int plen = parr.length
int [] indices = new int[plen];
for(int i = 0; i < (plen/2); i ++)
indices[i] = 2*i + 1; // Line1
for(int i = (plen/2); i < plen; i++)
indices[i] = 2*(plen-i); // Line2
for(int i = 0; i < plen; i++)
{
if(i != indices[i])
swap(parr[i], parr[indices[i]]);
}
The second case, Something like this?
int plen = parr.length
int [] indices = new int[plen];
for(int i = 0; i <= (plen/2); i ++)
indices[i] = (plen^1) - 2*i;
for(int i = 0; i < (plen/2); i++)
indices[i+(plen/2)+1] = 2*i + 1;
for(int i = 0; i < plen; i++)
{
if(i != indices[i])
swap(parr[i], parr[indices[i]]);
}

Categories

Resources