I have this problem from school:
You get a list of N students and a list of grades for each student. Every student gets coins. A student which has bigger grade than his neighbours from list earns more coins than them. Find the coins which every student must recieve.
Input:
6
Ionel
Mihai
Elena
Maria
George
Irina
9
10
8
6
9
10
Output:
Ionel 1
Mihai 3
Elena 2
Maria 1
George 2
Irina 3
Here is my code:
static void Main()
{
int number = Convert.ToInt32(Console.ReadLine());
string[] studentsList = FillTheList(number);
int[] studentsGrades = ConvertList(FillTheList(number));
PrintResult(studentsList, CalculateCoins(studentsGrades, number));
}
static string[] FillTheList(int number)
{
string[] result = new string[number];
for (int i = 0; i < number; i++)
{
result[i] = Console.ReadLine();
}
return result;
}
static int[] ConvertList(string[] array)
{
int[] result = new int[array.Length];
for (int i = 0; i < array.Length; i++)
{
result[i] = Convert.ToInt32(array[i]);
}
return result;
}
static int[] GiveEachStudentCoins(int number)
{
int[] result = new int[number];
result[0] = 1;
return result;
}
static void CheckArray(int[] array, int[] theResult, int number, int counter)
{
int i = number;
int count = counter;
int[] result = theResult;
for (int j = 0; j < array.Length; j++)
{
if (array[i] < array[j])
{
result[j] += count;
}
else
{
break;
}
}
}
static int[] CalculateCoins(int[] array, int number)
{
int[] result = GiveEachStudentCoins(number);
const int count = 1;
for (int i = 1; i < array.Length; i++)
{
if (array[i - 1] < array[i])
{
result[i] += result[i - 1] + count;
}
else
{
result[i] += count;
CheckArray(array, result, i, count);
}
}
return result;
}
static void PrintResult(string[] array, int[] array2)
{
for (int i = 0; i < array.Length; i++)
{
array[i] += " " + array2[i];
}
foreach (string student in array)
{
Console.WriteLine(student);
}
}
It works for ascending and descending order of grades, and also for equal grades. But it doesn't work for mixed order. Here is the result I get:
Ionel 3
Mihai 4
Elena 2
Maria 1
George 2
Irina 3
My problem is on the CheckArray method I think, and I don't know how to change the logic for this thing to work.
We can access the neighboring elements while walking through the array in a for loop by subtracting or adding 1 to the index of the current item. Of course before we do this, we have to make sure that the neighbor we want to check actually exists (if i == 0 then we can't access index i - 1, and if i == array.Length - 1, then we can't access index i + 1).
Given this knowledge, we can follow a simple formula, just as if we were doing it "by hand" on a piece of paper:
Start all values in the coins array with 1
Loop through each item in the input array.
If there's an item to the left and it's smaller than this item, then set the corresponding coins item to be one more than the coins item to it's left
If there's an item to the right and it's smaller than this item, and if the corresponding coins item is equal to the coins item to its right, then increment this item by 1
If we incremented this item after comparing to the right, then we need to re-check the item to the left and increment it if necessary (based on the corresponding values in the items array).
If that didn't make complete sense, try it on paper and it may be more obvious. Otherwise, hopefully this code sample will help:
public static int[] GetCoins(int[] input)
{
int[] coins = new int[input.Length];
// Initialize coins array with all '1's (similar to 'GiveEachStudentCoins')
for (int i = 0; i < coins.Length; i++)
{
coins[i] = 1;
}
// Examine the neighbors of each item in the array
for (int i = 0; i < input.Length; i++)
{
// Check left neighbor (if there is one)
if (i > 0 && (input[i] > input[i - 1]))
{
if (coins[i] <= coins[i - 1])
{
coins[i] = coins[i - 1] + 1;
}
}
// Check right neighbor (if there is one)
if (i < input.Length - 1 && input[i] > input[i + 1])
{
if (coins[i] == coins[i + 1])
{
coins[i]++;
// Recheck left since we incremented this one
if (i > 0 && input[i - 1] > input[i])
{
coins[i - 1]++;
}
}
}
}
return coins;
}
Here's some sample code that can be used for testing, so you don't have to manually enter numbers into the console each time (this requires adding using System.Linq to the top of the file):
public static void Main(string[] args)
{
// Populate the grades you want to test here
int[] studentsGrades = {9, 10, 8, 6, 9, 10};
// Student names are automatically generated
string[] studentsList = Enumerable.Range(1, studentsGrades.Length)
.Select(i => $"Student #{i} grade: {studentsGrades[i - 1]}\tcoins: ")
.ToArray();
// Call the new method to get the coins array
int[] coins = GetCoins(studentsGrades);
// Show the results
PrintResult(studentsList, coins);
Console.WriteLine("Done! Press any key to exit...");
Console.ReadKey();
}
Related
I have this problem and I'm stuck. I'm a beginner in software development.
You get a list of N students and a list of grades for each student. Every student gets coins. A student which has bigger grade than his neighbors from list earns more coins than them. Find the coins which every student must receive. I have to give students coins which has bigger grade.
Data input
3
Ionel
Mihai
Elena
9
10
8
Data output
Ionel 1
Mihai 3
Elena 1.
Here is my code.
static void Main(string[] args)
{
int number = Convert.ToInt32(Console.ReadLine());
string[] studentsList = FillArray(number);
int[] studentsGrades = ConvertList(FillArray(number));
PrintResult(studentsList, GetCoins(studentsGrades));
}
static string[] FillArray(int number) // filling array with names of students
{
string[] result = new string[number];
for (int i = 0; i < number; i++)
{
result[i] = Console.ReadLine();
}
return result;
}
static int[] ConvertList(string[] array)
{
int[] result = new int[array.Length];
for (int i = 0; i < array.Length; i++)
{
result[i] = Convert.ToInt32(array[i]);
}
return result;
}
static int[] GetGrades(int number) // give grades of each student
{
int[] result = new int[number];
for (int i = 0; i < number; i++)
{
result[i] = Convert.ToInt32(Console.ReadLine());
}
return result;
}
static int[] GetCoins(int[] input) // this is the method where I'm stuck.
{
int[] coins = new int[input.Length];
for (int i = 0; i < coins.Length; i++)
{
coins[i] = 1;
}
for (int i = 0; i < input.Length; i++)
{
if (i > 0 && (input[i] > input[i - 1]))
{
if (coins[i] <= coins[i - 1])
{
coins[i] = coins[i - 1] + 1;
}
}
if (i < input.Length - 1 && input[i] > input[i + 1])
{
if (coins[i] == coins[i + 1])
{
coins[i]++;
}
if (i > 0 && input[i] > input[i-1])
{
coins[i - 1]++;
}
}
}
return coins;
}
static void PrintResult(string[] array, int[] array2) // print the final result
{
for (int i = 0; i < array.Length; i++)
{
array[i] += " " + array2[i];
}
foreach (string student in array)
{
Console.WriteLine(student);
}
}
}
}
The program displays
Ionel 1
Mihai 2
Elena 1.
// this is wrong.
The correct answer is
Ionel 1
Mihai 3
Elena 1
// the correct answer
I'm not sure, but the problem is at the GetCoins method. Please help.
Thanks!
So I have a method which returns an array of the factors of a number
Tools.cs
public static long[] GetFactors(long number)
{
long range = number / 2;
long potentialFactors = 2;
long[] factors = new long[range + 1];
factors[0] = 1;
factors[range] = number;
for (long i = 1; i < range; i++)
{
if (number % potentialFactors == 0)
{
factors[i] = potentialFactors;
potentialFactors++;
} else
{
potentialFactors++;
}
}
Console.WriteLine($"here are the factors for the number {number}:\n"+string.Join("\n", factors));
return factors;
}
program.cs
static void Main(string[] args)
{
Tools.GetFactors(24);
Console.ReadLine();
}
But when I run my code, this appears:
here are the factors for the number 24:
1
2
3
4
0
6
0
8
0
0
0
12
24
How can I stop 0 from appearing, should I rewrite the "for" loop, or is there a way to remove 0 from the array?
You are allocating a fixed size array and setting the element to non-zero only when it is a factor.
You should instead use a var factors = new List<long>(); and call factors.Add(potentialFactor); to only store those numbers which are valid factors.
This code is using List instead of Array and also there are some other changes.
public static List<long> GetFactors(long number)
{
long range = number / 2;
List<long> factors = new List<long>() { 1 };
for (long i = 2; i <= range; i++)
{
if (number % i == 0)
{
factors.Add(i);
}
}
factors.Add(number);
Console.WriteLine($"here are the factors for the number {number}:\n" + string.Join("\n", factors));
return factors;
}
I agree a list is better when you dont know before hand how many factors you will gote. But if you really want to use an Array. You can loop throught this array, find the 0's one by one and them swith Left the numbers. Like so :
// Extension Method of array
public static void RemoveAllZeros(this long[] array) // O(N^2)
{
for (int i = 0; i < array.Length; i++)
{
if (array[i] == 0)
{
// Grab the index override value and continue swift left
for (int j = i; j < array.Length - 1; j++)
{
array[j] = array[j + 1];
}
}
}
}
OR using a bit of LINQ and List :p :
// Extension Method of array
public static void RemoveAllZeros(this long[] numbers)
{
int zeroPst = numbers.ToList().IndexOf(0);
if (zeroPst == -1)
return;
for (int i = zeroPst; i < numbers.Length - 1; i++)
{
numbers[i] = numbers[i + 1];
}
numbers.RemoveAllZeros(); //Recursion, carefull
}
I'm currently working on a LeetCode question called TwoSums.
It requires me to:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
My code works with different lengths of arrays. However, I found that when the numbers aren't in order, it gives the wrong answers.
My test case was [3,2,4] Target number = 6.
public class Solution {
public int[] TwoSum(int[] nums, int target)
{
int[] ret = new int[2];
int start = 0
int end = nums.Length - 1;
while(start < end)
{
if (nums[start] + nums[end] == target)
{
ret[0] = start;
ret[1] = end;
break;
}
else if(nums[start] + nums[end] < target)
{
start++;
}
else
{
end--;
}
}return(ret);
}
}
Here's a trivial brute-force solution which checks every pair of numbers. This has time complexity O(n^2):
public int[] TwoSum(int[] nums, int target)
{
for (int i = 0; i < nums.Length - 1; i++)
{
for (int j = i + 1; j < nums.Length; j++)
{
if (nums[i] + nums[j] == target)
{
return new[] { i, j };
}
}
}
throw new Exception("Solution not found");
}
We take care not to try any pair more than once - j always starts above i.
You can use 2 nested loops. :
public static int[] TwoSum(int[] nums, int target)
{
int[] ret = new int[2];
for (int i = 0; i < nums.Length - 1; i++)
{
for (int j = i + 1; j < nums.Length; j++)
{
if (nums[i] + nums[j] == target)
{
ret[0] = i;
ret[1] = j;
return ret;
}
}
}
return null; // This avoid compiler to complain about "not all path returns a value"
}
Your implementation assumes the list is sorted, because in the second if you check if the sum of start and end is smaller than target. Try this approach:
public class Solution
{
public int[] TwoSum(int[] nums, int target)
{
int[] ret = new int[2];
if (nums.Length < 2)
return ret;
int start = 0;
int end = 1;
while (start < (nums.Length -1))
{
if (nums[start] + nums[end] == target)
{
ret[0] = start;
ret[1] = end;
break;
}
if (end < nums.Length -1))
{
end++
}
else
{
start++;
end = start + 1
}
}
return ret;
}
}
It starts with start looking at index 0 and end at the next element. If it matches the target it returns the result. If not, it increments end until it reaches the end of the array. Than it wil increment start and reset end to start + 1, to look at the next element again. And so on until start reaches the last element. It then has checked all combinations.
There are numerous ways to approach this but the most direct - as already pointed out by the other answers - is nested loops.
What they all missed, including myself until I read your question again, was this:
You may assume that each input would have exactly one solution,
While it seems unimportant, this actually gives us a small optimization: reverse the search.
Using the forward method for your given inputs there are 3 comparisons done to find the answer:
[0, 1] => 3 + 2 != 6
[0, 2] => 3 + 4 != 6
[1, 2] => 2 + 4 == 6
For each pass through the outer loop the number of passes through the inner loop decreases. Unless the answer includes the first item in the array that means that you're testing more than you need to.
If we reverse the search we eliminate pairs from each outer loop faster:
static int[] TwoSums(int target, int[] array)
{
for (int i1 = array.Length - 2; i1 >= 0; i1--)
{
int v1 = array[i1];
for (int i2 = i1 + 1; i2 < array.Length; i2++)
{
if (v1 + array[i2] == target)
return new[] { i1, i2 };
}
}
return null;
}
This way the sequence for your example is:
[1, 2] => 2 + 4 == 6
That's a little bit of a cheat though, since the answer in this case is the last pair. Let's try something different and compare:
Array: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
Test value: 24
With a little instrumentation we can see how many times we run through the loop. For this particular case the forward search does 22 comparisons before it finds the one that works while the reverse does only 11. If the test value were 48 (19 + 29) the numbers would be: Forward=49, Reverse=5.
Of course if the answer includes one of the first couple of values then the numbers flip. It's a game of averages.
class Program
{
static void Main(string[] args)
{
int[] indexes = GetSumIndexes(new int[] { 3, 2, 4 }, 6);
}
public static int[] GetSumIndexes(int[] numbers, int target)
{
for (int i = 0; i < numbers.Length; i++)
{
for (int j = 0; j < numbers.Length; j++)
{
if (i != j)
{
if (numbers[i] + numbers[j] == target)
{
return new int[] { i, j };
}
}
}
}
return new int[] { };
}
}
Output:
[1,2]
Optimized version you can find in the other answers:
class Program
{
static void Main(string[] args)
{
int[] indexes = GetSumIndexes(new int[] { 3, 2, 4 }, 6);
}
public static int[] GetSumIndexes(int[] numbers, int target)
{
for (int i = 0; i < numbers.Length; i++)
{
for (int j = i + 1; j < numbers.Length; j++)
{
if (numbers[i] + numbers[j] == target)
{
return new int[] { i, j };
}
}
}
return new int[] { };
}
}
Output:
[1,2]
i have a deal with a hackerrank algorithm problem.
It works at all cases, except 6-7-8-9. It gives timeout error. I had spent so much time at this level. Someone saw where is problem?
static long[] climbingLeaderboard(long[] scores, long[] alice)
{
//long[] ranks = new long[scores.Length];
long[] aliceRanks = new long[alice.Length]; // same length with alice length
long lastPoint = 0;
long lastRank;
for (long i = 0; i < alice.Length; i++)
{
lastPoint = scores[0];
lastRank = 1;
bool isIn = false; // if never drop in if statement
for (long j = 0; j < scores.Length; j++)
{
if (lastPoint != scores[j]) //if score is not same, raise the variable
{
lastPoint = scores[j];
lastRank++;
}
if (alice[i] >= scores[j])
{
aliceRanks[i] = lastRank;
isIn = true;
break;
}
aliceRanks[i] = !isIn & j + 1 == scores.Length ? ++lastRank : aliceRanks[i]; //drop in here
}
}
return aliceRanks;
}
This problem can be solved in O(n) time, no binary search needed at all. First, we need to extract the most useful piece of data given in the problem statement, which is,
The existing leaderboard, scores, is in descending order.
Alice's scores, alice, are in ascending order.
An approach that makes this useful is to create two pointers, one at the start of alice array, let's call it "i", and the second is at the end of scores array, let's call it "j". We then loop until i reaches the end of alice array and at each iteration, we check for three main conditions. We increment i by one if alice[i] is less than scores[j] because the next element of alice may be also less than the current element of scores, or we decrement j if alice[i] is greater than scores[j] because we are sure that the next elements of alice are also greater than those elements discarded in scores. The last condition is that if alice[i] == scores[j], we only increment i.
I solved this question in C++, my goal here is to make you understand the algorithm, I think you can easily convert it to C# if you understand it. If there are any confusions, please tell me. Here is the code:
// Complete the climbingLeaderboard function below.
vector<int> climbingLeaderboard(vector<int> scores, vector<int> alice) {
int j = 1, i = 1;
// this is to remove duplicates from the scores vector
for(i =1; i < scores.size(); i++){
if(scores[i] != scores[i-1]){
scores[j++] = scores[i];
}
}
int size = scores.size();
for(i = 0; i < size-j; i++){
scores.pop_back();
}
vector<int> ranks;
i = 0;
j = scores.size()-1;
while(i < alice.size()){
if(j < 0){
ranks.push_back(1);
i++;
continue;
}
if(alice[i] < scores[j]){
ranks.push_back(j+2);
i++;
} else if(alice[i] > scores[j]){
j--;
} else {
ranks.push_back(j+1);
i++;
}
}
return ranks;
}
I think this may help you too:
vector is like an array list that resizes itself.
push_back() is inserting at the end of the vector.
pop_back() is removing from the end of the vector.
Here is my solution with c#
public static List<int> climbingLeaderboard(List<int> ranked, List<int> player)
{
List<int> result = new List<int>();
ranked = ranked.Distinct().ToList();
var pLength = player.Count;
var rLength = ranked.Count-1;
int j = rLength;
for (int i = 0; i < pLength; i++)
{
for (; j >= 0; j--)
{
if (player[i] == ranked[j])
{
result.Add(j + 1);
break;
}
else if(player[i] < ranked[j])
{
result.Add(j + 2);
break;
}
else if(player[i] > ranked[j]&&j==0)
{
result.Add(1);
break;
}enter code here
}
}
return result;
}
Here is a solution that utilizes BinarySearch. This method returns the index of the searched number in the array, or if the number is not found then it returns a negative number that is the bitwise complement of the index of the next element in the array. Binary search only works in sorted arrays.
public static int[] GetRanks(long[] scores, long[] person)
{
var defaultComparer = Comparer<long>.Default;
var reverseComparer = Comparer<long>.Create((x, y) => -defaultComparer.Compare(x, y));
var distinctOrderedScores = scores.Distinct().OrderBy(i => i, reverseComparer).ToArray();
return person
.Select(i => Array.BinarySearch(distinctOrderedScores, i, reverseComparer))
.Select(pos => (pos >= 0 ? pos : ~pos) + 1)
.ToArray();
}
Usage example:
var scores = new long[] { 100, 100, 50, 40, 40, 20, 10 };
var alice = new long[] { 5, 25, 50, 120 };
var ranks = GetRanks(scores, alice);
Console.WriteLine($"Ranks: {String.Join(", ", ranks)}");
Output:
Ranks: 6, 4, 2, 1
I was bored so i gave this a go with Linq and heavily commented it for you,
Given
public static IEnumerable<int> GetRanks(long[] scores, long[] person)
// Convert scores to a tuple
=> scores.Select(s => (scores: s, isPerson: false))
// convert persons score to a tuple and concat
.Concat(person.Select(s => (scores: s, isPerson: true)))
// Group by scores
.GroupBy(x => x.scores)
// order by score
.OrderBy(x => x.Key)
// select into an indexable tuple so we know everyones rank
.Select((groups, i) => (rank: i, groups))
// Filter the person
.Where(x => x.groups.Any(y => y.isPerson))
// select the rank
.Select(x => x.rank);
Usage
static void Main(string[] args)
{
var scores = new long[]{1, 34, 565, 43, 44, 56, 67};
var alice = new long[]{578, 40, 50, 67, 6};
var ranks = GetRanks(scores, alice);
foreach (var rank in ranks)
Console.WriteLine(rank);
}
Output
1
3
6
8
10
Based on the given constraint brute-force solution will not be efficient for the problem.
you have to optimize your code and the key part here is to look up for exact place which can be effectively done by using binary search.
Here is the solution using binary search:-
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int n = scores.length;
int m = alice.length;
int res[] = new int[m];
int[] rank = new int[n];
rank[0] = 1;
for (int i = 1; i < n; i++) {
if (scores[i] == scores[i - 1]) {
rank[i] = rank[i - 1];
} else {
rank[i] = rank[i - 1] + 1;
}
}
for (int i = 0; i < m; i++) {
int aliceScore = alice[i];
if (aliceScore > scores[0]) {
res[i] = 1;
} else if (aliceScore < scores[n - 1]) {
res[i] = rank[n - 1] + 1;
} else {
int index = binarySearch(scores, aliceScore);
res[i] = rank[index];
}
}
return res;
}
private static int binarySearch(int[] a, int key) {
int lo = 0;
int hi = a.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] == key) {
return mid;
} else if (a[mid] < key && key < a[mid - 1]) {
return mid;
} else if (a[mid] > key && key >= a[mid + 1]) {
return mid + 1;
} else if (a[mid] < key) {
hi = mid - 1;
} else if (a[mid] > key) {
lo = mid + 1;
}
}
return -1;
}
You can refer to this link for a more detailed video explanation.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int[] uniqueScores = IntStream.of(scores).distinct().toArray();
int [] rank = new int [alice.length];
int startIndex=0;
for(int j=alice.length-1; j>=0;j--) {
for(int i=startIndex; i<=uniqueScores.length-1;i++) {
if (alice[j]<uniqueScores[uniqueScores.length-1]){
rank[j]=uniqueScores.length+1;
break;
}
else if(alice[j]>=uniqueScores[i]) {
rank[j]=i+1;
startIndex=i;
break;
}
else{continue;}
}
}
return rank;
}
My solution in javascript for climbing the Leaderboard Hackerrank problem. The time complexity of the problem can be O(i+j), i is the length of scores and j is the length of alice. The space complexity is O(1).
// Complete the climbingLeaderboard function below.
function climbingLeaderboard(scores, alice) {
const ans = [];
let count = 0;
// the alice array is arranged in ascending order
let j = alice.length - 1;
for (let i = 0 ; i < scores.length ; i++) {
const score = scores[i];
for (; j >= 0 ; j--) {
if (alice[j] >= score) {
// if higher than score
ans.unshift(count+1);
} else if (i === scores.length - 1) {
// if smallest
ans.unshift(count+2);
} else {
break;
}
}
// actual rank of the score in leaderboard
if (score !== scores[i-1]) {
count++;
}
}
return ans;
}
Here is my solution
List<int> distinct = null;
List<int> rank = new List<int>();
foreach (int item in player)
{
ranked.Add(item);
ranked.Sort();
ranked.Reverse();
distinct = ranked.Distinct().ToList();
for (int i = 0; i < distinct.Count; i++)
{
if (item == distinct[i])
{
rank.Add(i + 1);
break;
}
}
}
return rank;
This can be modified by removing the inner for loop also
List<int> distinct = null;
List<int> rank = new List<int>();
foreach (int item in player)
{
ranked.Add(item);
ranked.Sort();
ranked.Reverse();
distinct = ranked.Distinct().ToList();
var index = ranked.FindIndex(x => x == item);
rank.Add(index + 1);
}
return rank;
This is my solution in c# for Hackerrank Climbing the Leaderboard based on C++ answer here.
public static List<int> climbingLeaderboard(List<int> ranked, List<int> player)
{
List<int> _ranked = new List<int>();
_ranked.Add(ranked[0]);
for(int a=1; a < ranked.Count(); a++)
if(_ranked[_ranked.Count()-1] != ranked[a])
_ranked.Add(ranked[a]);
int j = _ranked.Count()-1;
int i = 0;
while(i < player.Count())
{
if(j < 0)
{
player[i] = 1;
i++;
continue;
}
if(player[i] < _ranked[j])
{
player[i] = j+2;
i++;
}
else
if(player[i] == _ranked[j])
{
player[i] = j+1;
i++;
}
else
{
j--;
}
}
return player;
}
My solution in Java for climbing the Leaderboard Hackerrank problem.
// Complete the climbingLeaderboard function below.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
Arrays.sort(scores);
HashSet<Integer> set = new HashSet<Integer>();
int[] ar = new int[alice.length];
int sc = 0;
for(int i=0; i<alice.length; i++){
sc = 1;
set.clear();
for(int j=0; j<scores.length; j++){
if(alice[i] < scores[j] && !set.contains(scores[j])){
sc++;
set.add(scores[j]);
}
}
ar[i] = sc;
}return ar;
}
I have this array of integers:-
int[] numbers = new int[] { 10, 20, 30, 40 };
I am trying to create an array which will have first element, last element, second element, second-last element and so on..
So, my resulting output will be:-
int[] result = {10,40,20,30};
This was my approach, in one loop start from first and go till the middle & in second loop start from last and get to the middle and select items accordingly, but I totally messed it up. Here is my attempted code:-
private static IEnumerable<int> OrderedArray(int[] numbers)
{
bool takeFirst = true;
if (takeFirst)
{
takeFirst = false;
for (int i = 0; i < numbers.Length / 2; i++)
{
yield return numbers[i];
}
}
else
{
takeFirst = true;
for (int j = numbers.Length; j < numbers.Length / 2; j--)
{
yield return numbers[j];
}
}
}
Need Help.
You might try this:
int[] result = numbers.Zip(numbers.Reverse(), (n1,n2) => new[] {n1, n2})
.SelectMany(x =>x)
.Take(numbers.Length)
.ToArray();
Explanation: This approach basically pairs up the elements of the original collection with the elements of its reverse ordered collection (using Zip). So you get a collection of pairs like [first, last], [second, second from last], etc.
It then flattens those collection of pairs into a single collection (using SelectMany). So the collection becomes [first, last, second, second from last,...].
Finally, we limit the number of elements to the length of the original array (n). Since we are iterating through twice as many elements (normal and reverse), it works out that iterating through n elements allow us to stop in the middle of the collection.
As a different approach, this is a modification on your existing method:
private static IEnumerable<int> OrderedArray(int[] numbers)
{
var count = (numbers.Length + 1) / 2;
for (int i = 0; i < count; i++)
{
yield return numbers[i];
int reverseIdx = numbers.Length - 1 - i;
if(i != reverseIdx)
yield return numbers[reverseIdx];
}
}
ok,
public static class Extensions
{
public static IEnumerable<T> EndToEnd<T>(this IReadOnlyList<T> source)
{
var length = source.Count;
var limit = length / 2;
for (var i = 0; i < limit; i++)
{
yield return source[i];
yield return source[length - i - 1];
}
if (length % 2 > 0)
{
yield return source[limit];
}
}
}
Which you could use like this,
var result = numbers.EndToEnd().ToArray();
more optimally,
public static class Extensions
{
public static IEnumerable<T> EndToEnd<T>(this IReadOnlyList<T> source)
{
var c = source.Count;
for (int i = 0, f = 0, l = c - 1; i < c; i++, f++, l--)
{
yield return source[f];
if (++i == c)
{
break;
}
yield return source[l];
}
}
}
no divide or modulus required.
With a simple for;
int len = numbers.Length;
int[] result = new int[len];
for (int i = 0, f = 0, l = len - 1; i < len; f++, l--)
{
result[i++] = numbers[f];
if (f != l)
result[i++] = numbers[l];
}
Based on Selman22's now deleted answer:
int[] numbers = new int[] { 10, 20, 30, 40 };
int[] result = numbers
.Select((x,idx) => idx % 2 == 0
? numbers[idx/2]
: numbers[numbers.Length - 1 -idx/2])
.ToArray();
result.Dump();
(The last line is LinqPad's way of outputting the results)
Or in less LINQy form as suggested by Jeppe Stig Nielsen
var result = new int[numbers.Length];
for (var idx = 0; idx < result.Length; idx++) {
result[idx] = idx % 2 == 0 ? numbers[idx/2] : numbers[numbers.Length - 1 -idx/2];
}
The principle is that you have two sequences, one for even elements (in the result) and one for odd. The even numbers count the first half of the array and the odds count the second half from the back.
The only modification to Selman's code is adding the /2 to the indexes to keep it counting one by one in the right half while the output index (which is what idx basically is in this case) counts on.
Came up with this
static void Main(string[] args)
{
List<int> numbers = new List<int>() { 10, 20, 30, 40, 50, 60, 70};
List<int> numbers2 = new List<int>();
int counter1 = 0;
int counter2 = numbers.Count - 1;
int remainder = numbers.Count % 2 == 0 ? 1: 0;
while (counter1-1 < counter2)
{
if (counter1 + counter2 % 2 == remainder)
{
numbers2.Add(numbers[counter1]);
counter1++;
}
else
{
numbers2.Add(numbers[counter2]);
counter2--;
}
}
string s = "";
for(int a = 0; a< numbers2.Count;a++)
s+=numbers2[a] + " ";
Console.Write(s);
Console.ReadLine();
}
This late answer steals a lot from the existing answers!
The idea is to allocate the entire result array at once (since its length is known). Then fill out all even-indexed members first, from one end of source. And finally fill out odd-numbered entries from the back end of source.
public static TElement[] EndToEnd<TElement>(this IReadOnlyList<TElement> source)
{
var count = source.Count;
var result = new TElement[count];
for (var i = 0; i < (count + 1) / 2; i++)
result[2 * i] = source[i];
for (var i = 1; i <= count / 2; i++)
result[2 * i - 1] = source[count - i];
return result;
}
Came up with this
public int[] OrderedArray(int[] numbers)
{
int[] final = new int[numbers.Length];
var limit=numbers.Length;
int last = numbers.Length - 1;
var finalCounter = 0;
for (int i = 0; finalCounter < numbers.Length; i++)
{
final[finalCounter] = numbers[i];
final[((finalCounter + 1) >= limit ? limit - 1 : (finalCounter + 1))] = numbers[last];
finalCounter += 2;
last--;
}
return final;
}