I've been going though www.testdome.com to test my skills and opened a list of public questions. One of the practice questions was:
Implement function CountNumbers that accepts a sorted array of
integers and counts the number of array elements that are less than
the parameter lessThan.
For example, SortedSearch.CountNumbers(new int[] { 1, 3, 5, 7 }, 4)
should return 2 because there are two array elements less than 4.
And my answer was:
using System;
public class SortedSearch
{
public static int CountNumbers(int[] sortedArray, int lessThan)
{
int count = 0;
int l = sortedArray.Length;
for (int i = 0; i < l; i++) {
if (sortedArray [i] < lessThan)
count++;
}
return count;
}
public static void Main(string[] args)
{
Console.WriteLine(SortedSearch.CountNumbers(new int[] { 1, 3, 5, 7 }, 4));
}
}
It seems that I've failed on two counts:
Performance test when sortedArray contains lessThan: Time limit exceeded
and
Performance test when sortedArray doesn't contain lessThan: Time limit exceeded
To be honest I'm not sure what to optimize there? Maybe I'm using a wrong method and there is a similar way to speed up the calculation?
If someone could point out my mistake or explain what I'm going wrong, I'd really appreciate it!
Because the array is sorted, you can stop counting as soon as you reach or exceed the lessThan parameter.
else break would probably do it.
Does it have to be really a loop? You could do Lambda exp for that
public static int CountNumbers(int[] sortedArray, int lessThan)
{
return sortedArray.ToList().Where(x=>x < lessThan).Count();
}
Harold's answer and approach is spot on.
Find below another code sample in case you're practicing for technical interviews. It handles cases when the array is null or empty, when lessThan is presented in the array (including duplicates), etc.
private static int CountNumbers(int[] sortedArray, int lessThan)
{
if (sortedArray == null)
{
throw new ArgumentNullException("Sorted array cannot be null.");
}
if (sortedArray.Length == 0)
{
throw new ArgumentException("Sorted array cannot be empty.");
}
int start = 0;
int end = sortedArray.Length;
int middle = int.MinValue;
while (start < end)
{
middle = (start + end) / 2;
if (sortedArray[middle] == lessThan)
{
break; // Found the "lessThan" number in the array, we can stop and move left
}
else if (sortedArray[middle] < lessThan)
{
start = middle + 1;
}
else
{
end = middle - 1;
}
}
// Adjust the middle pointer based on the "current" and "lessThan" numbers in the sorted array
while (middle >= 0 && sortedArray[middle] >= lessThan)
{
middle--;
}
// +1 because middle is calculated through 0-based (e.g. start)
return middle + 1;
}
Related
Question: You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N.
Examples
[2, 4, 0, 100, 4, 11, 2602, 36]
Should return: 11 (the only odd number)
[160, 3, 1719, 19, 11, 13, -21]
Should return: 160 (the only even number)
Problem: I can find the target number. It is "6" in this instance. But when i try to write it, it is written 3 times. I did not understand why it is not written once.
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}
if (even>1)
{
foreach (var item in integers)
{
if (item%2!=0)
{
Console.WriteLine(item);
}
}
}
else if (odd>1)
{
foreach (var item in integers)
{
if (item%2==0)
{
Console.WriteLine(item);
}
}
}
}
}
}
}```
Since we are doing this assignment for a good grade :)
We really don't need to count both odd and even elements and can just count odd once as it more convenient - just add up all remainders ( odd % 2 = 1, even % 2 = 0). If we would need number of even elements it would be array.Length - countOdd.
int countOdd = 0;
for (int i = 0; i < array.Length; i++) // or foreach
{
countOdd += array[i] % 2;
}
If you feel that trick with counting is too complicated and only needed for A+ mark on the assignment than variant of your original code is fine:
int countOdd = 0;
foreach (int element in array)
{
if (element % 2 == 1)
countOdd ++; // which is exactly countOdd += 1, just shorter.
}
Now for the second part - finding the outlier. We don't even need to know if it is Odd or Even but rather just reminder from "% 2". We now know how many odd element - which could be either 1 (than desired remainder is 1 - we are looking for odd element) or more than one (then desired remainder is 0 as we are looking for even one).
int desiredRemainder = countOdd == 1 ? 1 : 0;
Now all its left is to check which element has desired remainder and return from our method:
foreach (int element in array)
{
if (element % 2 == desiredRemainder)
{
return element;
}
}
Please note that assignment asks for "return" and not "print". Printing the value does not allow it to be used by the caller of the method unlike returning it. Using early return also saves some time to iterate the rest of array.
Complete code:
int FindOutlier(int[] array)
{
int desiredReminder = array.Sum(x => x % 2) == 1 ? 1 : 0;
return array.First(x => x % 2 == desiredReminder);
}
Alternative solution would be to iterate array only once - since we need to return first odd or first even number as soon as we figure out which one repeats similar how your tried with nested foreach. Notes since we are guaranteed that outlier present and is only one we don't need to try to remember first number - just current is ok.
int FindOutlier(int[] array)
{
int? odd = null; // you can use separate int + bool instead
int? even = null;
int countOdd = 0;
int countEven = 0;
foreach (int element in array)
{
bool isEven = element % 2 == 0;
if (isEven)
{
countEven++;
even = element;
}
else
{
countOdd++;
odd = element;
}
if (countEven > 1 && odd.HasValue)
return odd.Value;
if (countOdd > 1 && even.HasValue)
return even.Value;
}
throw new Exception("Value must be found before");
}
And if you ok to always iterate whole list code will be simple as again we'd need to only keep track of count of odd numbers (or even, just one kind) and return would not need to check if we found the value yet as we know that both type of numbers are present in the list:
int FindOutlier(int[] array)
{
int odd = 0;
int even = 0;
int countEven = 0;
foreach (int element in array)
{
if (element % 2 == 0)
{
countEven++;
even= element;
}
else
{
odd = element;
}
}
return countEven > 1 ? odd : even;
}
You are mixing everything into a single algorithm. In most cases it's a better idea to have separate steps. In your case, the steps could be:
Count odds and evens
Find the outlier
Print the result
That way you can make sure that the output does not interfere with the loops.
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
// Count odds and evens
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}
}
// Find the outlier
var outlier = 0;
foreach (var item in integers)
{
if (even == 1 && item%2==0)
{
outlier = item;
break;
}
if (odd == 1 && item%2!=0)
{
outlier = item;
break;
}
}
// Print the result
Console.WriteLine(outlier);
}
}
You could even make this separate methods. It also makes sense due to SoC (separation of concerns) and if you want to achieve testability (unit tests).
You want a method anyway and use the return statement, as mentioned in the assignment
Write a method that takes the array as an argument and returns this "outlier" N.
Maybe you want to check the following code, which uses separate methods:
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] { 1, 3, 5, 6 };
var outlier = FindOutlier(integers);
Console.WriteLine(outlier);
}
private static int FindOutlier(int[] integers)
{
if (integers.Length < 3) throw new ArgumentException("Need at least 3 elements.");
bool isEvenOutlier = IsEvenOutlier(integers);
var outlier = FindOutlier(integers, isEvenOutlier ? 0:1);
return outlier;
}
static bool IsEvenOutlier(int[] integers)
{
// Count odds and evens
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num % 2 == 0)
{
even += 1;
}
else
{
odd += 1;
}
}
// Check which one occurs only once
if (even == 1) return true;
if (odd == 1) return false;
throw new ArgumentException("No outlier in data.", nameof(integers));
}
private static int FindOutlier(int[] integers, int remainder)
{
foreach (var item in integers)
{
if (item % 2 == remainder)
{
return item;
}
}
throw new ArgumentException("No outlier in argument.", nameof(integers));
}
}
The reason it is printing '6' 3 times is because you iterate over the inetegers inside a loop where you iterate over the integers. Once with num and again with item.
Consider what happens when you run it against [1,3,5,6].
num=1, you get odd=1 and even=0. Neither of the if conditions are true so we move to the next iteration.
num=3, you get odd=2 and even=0. odd > 1 so we iterate over the integers (with item) again and print '6' when item=6.
num=5, you get odd=3 and even=0. Again, this will print '6' when item=6.
num=6, you get odd=3 and even=1. Even though we incremented the even count, we still have odd > 3 so will print '6' when item=6.
It is best to iterate over the values once and store the last even and last odd numbers along with the count (it is more performant as well).
You can then print the last odd number if even > 0 (as there will only be one odd number, which will be the last one) or the last even number if odd > 0.
I.e.
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] { 1, 3, 5, 6 };
int even = 0;
int? lastEven = null;
int odd = 0;
int? lastOdd = null;
foreach (var num in integers)
{
if (num % 2 == 0)
{
even += 1;
lastEven = num;
}
else
{
odd += 1;
lastOdd = num;
}
}
if (even > 1)
{
Console.WriteLine(lastOdd);
}
else if (odd > 1)
{
Console.WriteLine(lastEven);
}
}
}
}
because you didn't close the the first foreach loop before printing...
namespace sayitoplamları
{
class Program
{
static void Main(string[] args)
{
int[] integers;
integers = new int[] {1,3,5,6};
int even = 0;
int odd = 0;
foreach (var num in integers)
{
if (num%2==0)
{
even += 1;
}
else
{
odd += 1;
}//you need to close it here
if (even>1)
{
foreach (var item in integers)
{
if (item%2!=0)
{
Console.WriteLine(item);
}
}
}
else if (odd>1)
{
foreach (var item in integers)
{
if (item%2==0)
{
Console.WriteLine(item);
}
}
}
}
}//not here
}
}
so in my application , I read some files into it and ask the user for a number , in these files there a lot of numbers and I am trying to find the nearest value when the number they enter is not in the file. So far I have as following
static int nearest(int close_num, int[] a)
{
foreach (int bob in a)
{
if ((close_num -= bob) <= 0)
return bob;
}
return -1;
}
Console.WriteLine("Enter a number to find out if is in the selected Net File: ");
int i3 = Convert.ToInt32(Console.ReadLine());
bool checker = false;
//Single nearest = 0;
//linear search#1
for (int i = 0; i < a.Length; i++)//looping through array
{
if(a[i] == i3)//checking to see the value is found in the array
{
Console.WriteLine("Value found and the position of it in the descending value of the selected Net File is: " + a[i]);
checker = true;
}
else
{
int found = nearest(i3,a);
Console.WriteLine("Cannot find this number in the Net File however here the closest number to that: " + found );
//Console.WriteLine("Cannot find this number in the Net File however here the closest number to that : " + nearest);
}
}
When a value that is in the file is entered the output is fine , but when it comes to the nearest value I cannot figure a way. I can't use this such as BinarySearchArray for this. a = the array whilst i3 is the value the user has entered. Would a binary search algorithm just be simpler for this?
Any help would be appreciated.
You need to make a pass over all the elements of the array, comparing each one in turn to find the smallest difference. At the same time, keep a note of the current nearest value.
There are many ways to do this; here's a fairly simple one:
static int nearest(int close_num, int[] a)
{
int result = -1;
long smallestDelta = long.MaxValue;
foreach (int bob in a)
{
long delta = (bob > close_num) ? (bob - close_num) : (close_num - bob);
if (delta < smallestDelta)
{
smallestDelta = delta;
result = bob;
}
}
return result;
}
Note that delta is calculated so that it is the absolute value of the difference.
Well, first we should define, what is nearest. Assuming that,
int nearest for given int number is the item of int[] a such that Math.Abs(nearest - number) is the smallest possible value
we can put it as
static int nearest(int number, int[] a)
{
long diff = -1;
int result = 0;
foreach (int item in a)
{
// actual = Math.Abs((long)item - number);
long actual = (long)item - number;
if (actual < 0)
actual = -actual;
// if item is the very first value or better than result
if (diff < 0 || actual < diff) {
result = item;
diff = actual;
}
}
return result;
}
The only tricky part is long for diff: it may appear that item - number exceeds int range (and will either have IntegerOverflow exceprion thrown or *invalid answer), e.g.
int[] a = new int[] {int.MaxValue, int.MaxValue - 1};
Console.Write(nearest(int.MinValue, a));
Note, that expected result is 2147483646, not 2147483647
what about LINQ ?
var nearestNumber = a.OrderBy(x => Math.Abs(x - i3)).First();
Just iterate through massive and find the minimal delta between close_num and array members
static int nearest(int close_num, int[] a)
{
// initialize as big number, 1000 just an example
int min_delta=1000;
int result=-1;
foreach (int bob in a)
{
if (Math.Abs(bob-close_num) <= min_delta)
{
min_delta = bob-close_num;
result = bob;
}
}
return result;
}
So I was looking through interview questions that unfortunately didn't have any solutions, and one of them seemed interesting (mostly because I had no idea how to do it). However now I have hit a roadblock and am not sure how to proceed. The objective of the program is to take any amount of strings and spit out the periods of the lowest repeated characters to form the substring in an integer array.
Ex: "abbabbabba","abcdabcddabcdabc","aeiouuaeioouaei" would be 5, 8, 5.
I kind of looked at it as doing Indian runs. The first character runs to the front, if it equals the next character than continue running, etc.
This is the code I have compiled and I tried to take a look at the algorithm for finding periods as well (http://monge.univ-mlv.fr/~mac/REC/text-algorithms.pdf#page=345). I just can't seem to pinpoint the problem in the code
namespace Interview36{
public class PeriodicStrings
{
public static int[] process(string[] input)
{
if (input == null || input.Length == 0 || input[0].Length == 0)
{
Console.WriteLine("Result is null");
}
int length = 0;
int[] list = new int[input.Length];
for (int i = 0; i < input.Length; i++) {
while (i < input[i].Length)
{
if (input[i] == input[length])
{
length++;
list[i] = length;
i++;
}
else
{
if (length != 0)
{
length = list[length - 1];
}
else
{
list[i] = 0;
i++;
}
}
}
}
return list;
}
}
}
My best guess is that my logic is WAY off, or the loop isn't running through properly and I'm just not catching it in debug. Thank you in advance for any insight!
Length = input Long(can be 2550, 2880, 2568, etc)
List<long> = {618, 350, 308, 300, 250, 232, 200, 128}
The program takes a long value, for that particular long value we have to find the possible combination from the above list which when added give me a input result(same value can be used twice). There can be a difference of +/- 30.
Largest numbers have to be used most.
Ex:Length = 868
For this combinations can be
Combination 1 = 618 + 250
Combination 2 = 308 + 232 + 200 +128
Correct Combination would be Combination 1
But there should also be different combinations.
public static void Main(string[] args)
{
//subtotal list
List<int> totals = new List<int>(new int[] { 618, 350, 308, 300, 250, 232, 200, 128 });
// get matches
List<int[]> results = KnapSack.MatchTotal(2682, totals);
// print results
foreach (var result in results)
{
Console.WriteLine(string.Join(",", result));
}
Console.WriteLine("Done.");
}
internal static List<int[]> MatchTotal(int theTotal, List<int> subTotals)
{
List<int[]> results = new List<int[]>();
while (subTotals.Contains(theTotal))
{
results.Add(new int[1] { theTotal });
subTotals.Remove(theTotal);
}
if (subTotals.Count == 0)
return results;
subTotals.Sort();
double mostNegativeNumber = subTotals[0];
if (mostNegativeNumber > 0)
mostNegativeNumber = 0;
if (mostNegativeNumber == 0)
subTotals.RemoveAll(d => d > theTotal);
for (int choose = 0; choose <= subTotals.Count; choose++)
{
IEnumerable<IEnumerable<int>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);
results.AddRange(from combo in combos where combo.Sum() == theTotal select combo.ToArray());
}
return results;
}
public static class Combination
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int choose)
{
return choose == 0 ?
new[] { new T[0] } :
elements.SelectMany((element, i) =>
elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
}
}
I Have used the above code, can it be more simplified, Again here also i get unique values. A value can be used any number of times. But the largest number has to be given the most priority.
I have a validation to check whether the total of the sum is greater than the input value. The logic fails even there..
The algorithm you have shown assumes that the list is sorted in ascending order. If not, then you shall first have to sort the list in O(nlogn) time and then execute the algorithm.
Also, it assumes that you are only considering combinations of pairs and you exit on the first match.
If you want to find all combinations, then instead of "break", just output the combination and increment startIndex or decrement endIndex.
Moreover, you should check for ranges (targetSum - 30 to targetSum + 30) rather than just the exact value because the problem says that a margin of error is allowed.
This is the best solution according to me because its complexity is O(nlogn + n) including the sorting.
V4 - Recursive Method, using Stack structure instead of stack frames on thread
It works (tested in VS), but there could be some bugs remaining.
static int Threshold = 30;
private static Stack<long> RecursiveMethod(long target)
{
Stack<long> Combination = new Stack<long>(establishedValues.Count); //Can grow bigger, as big as (target / min(establishedValues)) values
Stack<int> Index = new Stack<int>(establishedValues.Count); //Can grow bigger
int lowerBound = 0;
int dimensionIndex = lowerBound;
long fail = -1 * Threshold;
while (true)
{
long thisVal = establishedValues[dimensionIndex];
dimensionIndex++;
long afterApplied = target - thisVal;
if (afterApplied < fail)
lowerBound = dimensionIndex;
else
{
target = afterApplied;
Combination.Push(thisVal);
if (target <= Threshold)
return Combination;
Index.Push(dimensionIndex);
dimensionIndex = lowerBound;
}
if (dimensionIndex >= establishedValues.Count)
{
if (Index.Count == 0)
return null; //No possible combinations
dimensionIndex = Index.Pop();
lowerBound = dimensionIndex;
target += Combination.Pop();
}
}
}
Maybe V3 - Suggestion for Ordered solution trying every combination
Although this isn't chosen as the answer for the related question, I believe this is a good approach - https://stackoverflow.com/a/17258033/887092(, otherwise you could try the chosen answer (although the output for that is only 2 items in set being summed, rather than up to n items)) - it will enumerate every option including multiples of the same value. V2 works but would be slightly less efficient than an ordered solution, as the same failing-attempt will likely be attempted multiple times.
V2 - Random Selection - Will be able to reuse the same number twice
I'm a fan of using random for "intelligence", allowing the computer to brute force the solution. It's also easy to distribute - as there is no state dependence between two threads trying at the same time for example.
static int Threshold = 30;
public static List<long> RandomMethod(long Target)
{
List<long> Combinations = new List<long>();
Random rnd = new Random();
//Assuming establishedValues is sorted
int LowerBound = 0;
long runningSum = Target;
while (true)
{
int newLowerBound = FindLowerBound(LowerBound, runningSum);
if (newLowerBound == -1)
{
//No more beneficial values to work with, reset
runningSum = Target;
Combinations.Clear();
LowerBound = 0;
continue;
}
LowerBound = newLowerBound;
int rIndex = rnd.Next(LowerBound, establishedValues.Count);
long val = establishedValues[rIndex];
runningSum -= val;
Combinations.Add(val);
if (Math.Abs(runningSum) <= 30)
return Combinations;
}
}
static int FindLowerBound(int currentLowerBound, long runningSum)
{
//Adjust lower bound, so we're not randomly trying a number that's too high
for (int i = currentLowerBound; i < establishedValues.Count; i++)
{
//Factor in the threshold, because an end aggregate which exceeds by 20 is better than underperforming by 21.
if ((establishedValues[i] - Threshold) < runningSum)
{
return i;
}
}
return -1;
}
V1 - Ordered selection - Will not be able to reuse the same number twice
Add this very handy extension function (uses a binary algorithm to find all combinations):
//Make sure you put this in a static class inside System namespace
public static IEnumerable<List<T>> EachCombination<T>(this List<T> allValues)
{
var collection = new List<List<T>>();
for (int counter = 0; counter < (1 << allValues.Count); ++counter)
{
List<T> combination = new List<T>();
for (int i = 0; i < allValues.Count; ++i)
{
if ((counter & (1 << i)) == 0)
combination.Add(allValues[i]);
}
if (combination.Count == 0)
continue;
yield return combination;
}
}
Use the function
static List<long> establishedValues = new List<long>() {618, 350, 308, 300, 250, 232, 200, 128, 180, 118, 155};
//Return is a list of the values which sum to equal the target. Null if not found.
List<long> FindFirstCombination(long target)
{
foreach (var combination in establishedValues.EachCombination())
{
//if (combination.Sum() == target)
if (Math.Abs(combination.Sum() - target) <= 30) //Plus or minus tolerance for difference
return combination;
}
return null; //Or you could throw an exception
}
Test the solution
var target = 858;
var result = FindFirstCombination(target);
bool success = (result != null && result.Sum() == target);
//TODO: for loop with random selection of numbers from the establishedValues, Sum and test through FindFirstCombination
Basically comparing a string that is entered, and trying to get that position from the array.
If I initialize position to 0 then it returns the position zero of the array, if I initialize to 1 then it gives me the item in slot 1, so it's skipping the compare statement.
I also tried using (custStatus == cardStatus[i])
public static int discount(string []cardStatus, int []pDiscount, string custStatus)
{
int position= 0;
int discount;
for(int i = 0; i < 2; i++)
{
if (string.Equals(custStatus, cardStatus[i]))
position = i;
}
discount = pDiscount[position];
return discount;
}
With your code, there's no way to tell if position = 0 means custStatus was found in your cardStatus array or if no match was made at all and the default value is being used. I'd recommend either using a boolean matchFound variable or setting position = -1 and adding an extra if statement at the end either way. Either:
boolean matchFound = false;
...
if(matchFound)
{
discount = pDiscount[position];
}
or else
int position = -1;
...
if(position >= 0)
{
discount = pDiscount[position];
}
Give this a try:
public static int discount(string[] cardStatus, int[] pDiscount, string custStatus) {
var position = Array.IndexOf(cardStatus, custStatus);
return (position == -1) ? -1 : pDiscount[position];
}
public static int discount(string []cardStatus, int []pDiscount, string custStatus)
{
for(int i = 0; i < Math.Min(cardStatus.Length, pDiscount.Length); i++)
{
if (string.Equals(custStatus, cardStatus[i]))
{
return pDiscount[i];
}
}
return -1;
}
Don't be afraid to return directly from FOR-loop, it is old-school that teaches to have only one return point from method. You can have as many returns as it helps you to keep your code clean and easy to read.
And perhaps it would be better to use the following expression in for-loop as it will guard you from possible different lengths of arrays:
for (int i = 0; i < Math.Min(cardStatus.Length, pDiscount.Length; i++)
This looks ok, even though this is somewhat more straightforward:
for(int i = 0; i < cardStatus.Length; i++)
{
if (custStatus == cardStatus[i])
{
position = i;
break;
}
}
Given your question it appears to be the case that all cardStatus[i] match custStatus - did you check the input?
Also given your code what happens if there is no match? Currently you would return pDiscount[0] - that doesn't seem to be correct.