3

I have a c# class like so

internal class QueuedMinimumNumberFinder : ConcurrentQueue<int>
{
    private readonly string _minString;
    public QueuedMinimumNumberFinder(string number, int takeOutAmount)
    {
        if (number.Length < takeOutAmount)
        {
            throw new Exception("Error *");
        }
        var queueIndex = 0;
        var queueAmount = number.Length - takeOutAmount;
        var numQueue = new ConcurrentQueue<int>(number.ToCharArray().Where(m => (int) Char.GetNumericValue(m) != 0).Select(m=>(int)Char.GetNumericValue(m)).OrderBy(m=>m));
        var zeroes = number.Length - numQueue.Count;
        while (queueIndex < queueAmount)
        {
            int next;
            if (queueIndex == 0)
            {
                numQueue.TryDequeue(out next);
                Enqueue(next);
            } else
            {
                if (zeroes > 0)
                {
                    Enqueue(0);
                    zeroes--;
                } else
                {
                    numQueue.TryDequeue(out next);
                    Enqueue(next);
                }
            }
            queueIndex++;
        }
        var builder = new StringBuilder();
        while (Count > 0)
        {
            int next = 0;
            TryDequeue(out next);
            builder.Append(next.ToString());
        }
        _minString = builder.ToString();
    }

    public override string ToString() { return _minString; }
}

The point of the program is to find the minimum possible integer that can be made by taking out any x amount of characters from a string(example 100023 is string, if you take out any 3 letters, the minimum int created would be 100). My question is, is this the correct way to do this? Is there a better data structure that can be used for this problem?

First Edit:

Here's how it looks now

 internal class QueuedMinimumNumberFinder
    {
        private readonly string _minString;
        public QueuedMinimumNumberFinder(string number, int takeOutAmount)
        {
            var queue = new Queue<int>();
            if (number.Length < takeOutAmount)
            {
                throw new Exception("Error *");
            }
            var queueIndex = 0;
            var queueAmount = number.Length - takeOutAmount;
            var numQueue = new List<int>(number.Where(m=>(int)Char.GetNumericValue(m)!=0).Select(m=>(int)Char.GetNumericValue(m))).ToList();
            var zeroes = number.Length - numQueue.Count;
            while (queueIndex < queueAmount)
            {
                if (queueIndex == 0)
                {
                    var nextMin = numQueue.Min();
                    numQueue.Remove(nextMin);
                    queue.Enqueue(nextMin);
                } else
                {
                    if (zeroes > 1)
                    {
                        queue.Enqueue(0);
                        zeroes--;
                    } else
                    {
                        var nextMin = numQueue.Min();
                        numQueue.Remove(nextMin);
                        queue.Enqueue(nextMin);
                    }
                }
                queueIndex++;
            }
            var builder = new StringBuilder();
            while (queue.Count > 0)
            {
                builder.Append(queue.Dequeue().ToString());
            }
            _minString = builder.ToString();
        }

        public override string ToString() { return _minString; }
    }
  • 6
    Why wouldn't the minimum be 000 in your example? – Lucas Trzesniewski Apr 08 '15 at 15:14
  • I have to assume that the minimum number must not contain leading zeroes –  Apr 08 '15 at 15:14
  • Why are you using `ConcurrentQueue` when you have only a single thread involved? – Scott Chamberlain Apr 08 '15 at 15:15
  • Old habit, i used queue on another program with multiple queuing thread and i kind of started using it in general –  Apr 08 '15 at 15:16
  • 1
    Also inheriting from `ConcurrentQueue` is kind of strange. [You should really wrap it](http://stackoverflow.com/questions/21692193/why-not-inherit-from-listt). – Sriram Sakthivel Apr 08 '15 at 15:16
  • As far as I can see, the code doesn't do at all what you describe. It just takes the first non-zero digit and puts all zeroes after it followed by the other non-zero digits, and uses the first (length-takeOutAmount) characters. For a string like `9999903040404` and takeOutAmount=3 the result would be `9000099993`, which is not anything that you can get by just taking out three digits, and not the smallest number that you can get by using all but three digits. – Guffa Apr 08 '15 at 15:24
  • @Guffa I tried the number and i did not get the result described, i instead got 3000044499 –  Apr 08 '15 at 15:28
  • @ravingheaven: Right, I didn't consider that the digits were sorted. Still, that's not a number that you can get just by taking out three letters from the string. – Guffa Apr 08 '15 at 15:32
  • Does the performance of the algorithm matter ?? because you're sorting all the list for this, while you could loop through the list only the amout of number you need to take out – Othman Benchekroun Apr 08 '15 at 15:35
  • DO you have to use queue ? or any other data structure is OK? – IndieTech Solutions Apr 08 '15 at 15:42
  • Any data structure would work, as long as it can solve the problem –  Apr 08 '15 at 15:43
  • @Othman If i have a pre sorted list, i can redo the algorithm on multiple take out amounts without having to traverse the list every time for next min. Would there be a significant performance reason to do a one-by one traversal? –  Apr 08 '15 at 15:45
  • @ravingheaven You don't have to traverse the list every time when you have a pre sorted list, but in order to sort it, your program has to traverse the list many times, I don't know which sorting algorithm is used by `orderBy`, but your algorithm can be done in less time than any of them – Othman Benchekroun Apr 09 '15 at 07:13

6 Answers6

1

Here's my solution using LINQ:

public string MinimumNumberFinder(string number, int takeOutAmount)
{
    var ordered = number.OrderBy(n => n);
    var nonZero = ordered.SkipWhile(n => n == '0');
    var zero = ordered.TakeWhile(n => n == '0');

    var result = nonZero.Take(1)
                        .Concat(zero)
                        .Concat(nonZero.Skip(1))
                        .Take(number.Length - takeOutAmount);   

    return new string(result.ToArray());
}
AlexG
  • 96
  • 3
  • I tried your solution and it didn't work for starting number 100023. It resulted in 000 – Shar1er80 Apr 08 '15 at 16:15
  • I don't think using OrderBy is what he wants, the OP is asking for an algorithm – IndieTech Solutions Apr 08 '15 at 16:20
  • @Alundrathedreamwalker - perhaps the OP is asking for an algorithm, but I don't see any evidence of that in the question. Any proposed solution involves some kind of sorting, why should you have to implement that manually when C# provides (various) library implementations? – AlexG Apr 09 '15 at 20:40
  • @Shar1er80 - agreed, the original implementation didn't work for 100023, I initially misunderstood the requirements. Edited to cater for this. – AlexG Apr 09 '15 at 20:41
1

Just keep a count of how many times each digit appears. An array of size 10 will do. Count[i] gives the count of digit i.

Then pick the smallest non-zero i first, then pick the smallest etc and form your number.

1

A pretty simple and efficient implementation can be made, once you realize that your input string digits map to the domain of only 10 possible values: '0' .. '9'.

This can be encoded as the number of occurrences of a specific digit in your input string using a simple array of 10 integers: var digit_count = new int[10];

@MasterGillBates describes this idea in his answer.

You can then regard this array as your priority queue from which you can dequeue the characters you need by iteratively removing the lowest available character (decreasing its occurrence count in the array).

The code sample below provides an example implementation for this idea.

public static class MinNumberSolver
{
    public static string GetMinString(string number, int takeOutAmount)
    {
        // "Add" the string by simply counting digit occurrance frequency.
        var digit_count = new int[10];
        foreach (var c in number)
            if (char.IsDigit(c))
                digit_count[c - '0']++;

        // Now remove them one by one in lowest to highest order.

        // For the first character we skip any potential leading 0s
        var selected = new char[takeOutAmount];
        var start_index = 1;
        selected[0] = TakeLowest(digit_count, ref start_index);

        // For the rest we start in digit order at '0' first.
        start_index = 0;
        for (var i = 0; i < takeOutAmount - 1; i++)
            selected[1 + i] = TakeLowest(digit_count, ref start_index);

        // And return the result.
        return new string(selected);
    }

    private static char TakeLowest(int[] digit_count, ref int start_index)
    {
        for (var i = start_index; i < digit_count.Length; i++)
        {
            if (digit_count[i] > 0)
            {
                start_index = ((--digit_count[i] > 0) ? i : i + 1);
                return (char)('0' + i);
            }
        }
        throw new InvalidDataException("Input string does not have sufficient digits");
    }
}
Community
  • 1
  • 1
Alex
  • 13,024
  • 33
  • 62
0

You could place every integer into a list and find all possible sequences of these values. From the list of sequences, you could sort through taking only the sets which have the number of integers you want. From there, you can write a quick function which parses a sequence into an integer. Next, you could store all of your parsed sequences into an array or an other data structure and sort based on value, which will allow you to select the minimum number from the data structure. There may be simpler ways to do this, but this will definitely work and gives you options as far as how many digits you want your number to have.

Community
  • 1
  • 1
Joe Urc
  • 487
  • 5
  • 19
0

If I'm understanding this correctly, why don't you just pick out your numbers starting with the smallest number greater than zero. Then pick out all zeroes, then any remaining number if all the zeroes are picked up. This is all depending on the length of your ending result

In your example you have a 6 digit number and you want to pick out 3 digits. This means you'll only have 3 digits left. If it was a 10 digit number, then you would end up with a 7 digit number, etc...

So have an algorithm that knows the length of your starting number, how many digits you plan on removing, and the length of your ending number. Then just pick out the numbers.

This is just quick and dirty code:

string startingNumber = "9999903040404"; // "100023";
int numberOfCharactersToRemove = 3;

string endingNumber = string.Empty;
int endingNumberLength = startingNumber.Length - numberOfCharactersToRemove;

while (endingNumber.Length < endingNumberLength)
{
    if (string.IsNullOrEmpty(endingNumber))
    {
        // Find the smallest digit in the starting number
        for (int i = 1; i <= 9; i++)
        {
            if (startingNumber.Contains(i.ToString()))
            {
                endingNumber += i.ToString();
                startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
                break;
            }
        }
    }
    else if (startingNumber.Contains("0"))
    {
        // Add any zeroes
        endingNumber += "0";
        startingNumber = startingNumber.Remove(startingNumber.IndexOf("0"), 1);
    }
    else
    {
        // Add any remaining numbers from least to greatest
        for (int i = 1; i <= 9; i++)
        {
            if (startingNumber.Contains(i.ToString()))
            {
                endingNumber += i.ToString();
                startingNumber = startingNumber.Remove(startingNumber.IndexOf(i.ToString()), 1);
                break;
            }
        }
    }
}

Console.WriteLine(endingNumber);

100023 starting number resulted in 100 being the end result

9999903040404 starting number resulted in 3000044499 being the end result

Shar1er80
  • 9,001
  • 2
  • 20
  • 29
0

Here's my version to fix this problem:


DESIGN:

  • You can sort your list using a binary tree , there are a lot of implementations , I picked this one
  • Then you can keep track of the number of the Zeros you have in your string Finally you will end up with two lists: I named one SortedDigitsList and the other one ZeroDigitsList
  • perform a switch case to determine which last 3 digits should be returned

Here's the complete code:

 class MainProgram2
{
    static void Main()
    {
        Tree theTree = new Tree();
        Console.WriteLine("Please Enter the string you want to process:"); 
        string input = Console.ReadLine();
        foreach (char c in input)
        {
            // Check if it's a digit or not
            if (c >= '0' && c <= '9')
            {
                theTree.Insert((int)Char.GetNumericValue(c));
            }

        }
        //End of for each (char c in input)


        Console.WriteLine("Inorder traversal resulting Tree Sort without the zeros");
        theTree.Inorder(theTree.ReturnRoot());
        Console.WriteLine(" ");

        //Format the output depending on how many zeros you have
        Console.WriteLine("The final 3 digits are");
        switch (theTree.ZeroDigitsList.Count)
        {
            case 0:
                {
                    Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], theTree.SortedDigitsList[1], theTree.SortedDigitsList[2]);
                    break;
                }
            case 1:
                {
                    Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, theTree.SortedDigitsList[2]);
                    break;
                }
            default:
                {
                    Console.WriteLine("{0}{1}{2}", theTree.SortedDigitsList[0], 0, 0);
                    break;
                }

        }


        Console.ReadLine();
    }

}//End of main()
 }

class Node
{
    public int item;
    public Node leftChild;
    public Node rightChild;
    public void displayNode()
    {
        Console.Write("[");
        Console.Write(item);
        Console.Write("]");
    }
}
class Tree 
{

public List<int> SortedDigitsList { get; set; }
public List<int> ZeroDigitsList { get; set; }
public Node root;
public Tree()
{

    root = null;
    SortedDigitsList = new List<int>();
    ZeroDigitsList = new List<int>();


}
public Node ReturnRoot()
{
    return root;
}
public void Insert(int id)
{
    Node newNode = new Node();
    newNode.item = id;
    if (root == null)
        root = newNode;
    else
    {
        Node current = root;
        Node parent;
        while (true)
        {
            parent = current;
            if (id < current.item)
            {
                current = current.leftChild;
                if (current == null)
                {
                    parent.leftChild = newNode;
                    return;
                }
            }
            else
            {
                current = current.rightChild;
                if (current == null)
                {
                    parent.rightChild = newNode;
                    return;
                }
            }
        }
    }
}
//public void Preorder(Node Root)
//{
//    if (Root != null)
//    {
//        Console.Write(Root.item + " ");
//        Preorder(Root.leftChild);
//        Preorder(Root.rightChild);
//    }
//}

public void Inorder(Node Root)
{
    if (Root != null)
    {
        Inorder(Root.leftChild);
        if (Root.item > 0)
        {
            SortedDigitsList.Add(Root.item);
            Console.Write(Root.item + " ");
        }
        else
        {
            ZeroDigitsList.Add(Root.item);
        }
        Inorder(Root.rightChild);
    }

}
IndieTech Solutions
  • 2,527
  • 1
  • 21
  • 36