Been tackling this all day now and I am really fed up with it. I am new to sorting and I was working on bubble sort, quick sort and finally a bucket sort for my school exercise.
Here is what I have for bucket sort for a list of Objects(T-shirts) sorted by Cost. Now T-shirts Have also Size, Fabric and Color and that is why I need to sort them as well. I know this falls in the sort strings by bubble sort category but I can not find anything about it and everything I tried went bad
public List<Tshirt> BucketSort(Tshirt[] array)
{
List<Tshirt> result = new List<Tshirt>();
// Determine how many buckets you want to create
//Create buckets
int numOfBuckets = 5;
List<Tshirt>[] buckets = new List<Tshirt>[numOfBuckets];
for (int i = 0; i < 5; i++)
buckets[i] = new List<Tshirt>();
//Iterate through the passed array and add each tshirt to the appropriate bucket
for (int i = 0; i < array.Length; i++)
{
int buckitChoice = ((int)array[i].Cost / numOfBuckets);
buckets[buckitChoice].Add(array[i]);
}
//Sort each bucket and add it to the result List
//Each sublist is sorted using Bubblesort, but you could substitute any sorting algo you would like
for (int i = 0; i < numOfBuckets; i++)
{
Tshirt[] temp = BubbleSortList(buckets[i]);
result.AddRange(temp);
}
return result;
}
public static Tshirt[] BubbleSortList(List<Tshirt> input)
{
for (int i = 0; i < input.Count; i++)
{
for (int j = 0; j < input.Count; j++)
{
if (input[i].Cost < input[j].Cost)
{
decimal temp = input[i].Cost;
input[i].Cost = input[j].Cost;
input[j].Cost = temp;
}
}
}
return input.ToArray();
}
public Tshirt[] ReturnContexttoArray()
{
var tshirts = _context.Tshirts;
Tshirt[] TshirtArr = new Tshirt[tshirts.Count()];
var count = 0;
foreach (var tshirt in tshirts)
{
TshirtArr[count++] = tshirt;
}
return TshirtArr;
}
public List<Tshirt> ImplementBucketSortAsc()
{
return BucketSort(ReturnContexttoArray());
}
I'm new to algorithms and I tried to write a selection sort. With some help from the internet I have a script that should work, but doesn't. The result after the sort method is a list that is still unsorted.
I'm not sure if I missed anything and my code looks the same as the ones online.
Product.cs
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
Order.cs
public class Order
{
public List<Product> listOfProducts = new List<Product>(){
new Product(){ Name="Item1", Price=2.55 },
new Product(){ Name="Item2", Price=1.92 },
new Product(){ Name="Item3", Price=2.12 }
};
public List<Product> GetAllProducts(){
return this.listOfProducts;
}
public void SortProductsByPrice(){
int min = 0;
for (int i = 0; i < this.listOfProducts.Count - 1; i++)
{
min = i;
for (int j = 0; j < this.listOfProducts.Count; j++)
{
if (listOfProducts[j].Price < listOfProducts[min].Price)
{
min = j;
}
}
Product temporary = listOfProducts[min];
listOfProducts[min] = listOfProducts[i];
listOfProducts[i] = temporary;
}
}
}
Program.cs
static void Main(string[] args)
{
Order order = new Order();
// unsorted list
foreach (Product pro in order.GetAllProducts())
{
Console.WriteLine(pro.Price);
}
Console.WriteLine("------------------------------------------");
order.SortProductsByPrice();
// sorted list
foreach (Product pro in order.GetAllProducts())
{
Console.WriteLine(pro.Price);
}
Console.ReadLine();
}
The problem in your code is in the nested loop.
If you take a closer look at the algorithm, you'll see that:
Selection sort is a simple sorting algorithm. This sorting algorithm is an in-place comparison-based algorithm in which the list is divided into two parts, the sorted part at the left end and the unsorted part at the right end. Initially, the sorted part is empty and the unsorted part is the entire list.
You're re-comparing your values with what you've already sorted, which you should not do. You're not getting a sorted list because by the end of your code, the values are being swapped over and over until they get back to their original order. A simple fix is by changing the nested for loop like this:
public void SortProductsByPrice()
{
int min = 0;
for (int i = 0; i < this.listOfProducts.Count - 1; i++)
{
min = i;
for (int j = i + 1; j < this.listOfProducts.Count; j++)
{
if (listOfProducts[j].Price < listOfProducts[min].Price)
{
min = j;
}
}
Product temporary = listOfProducts[min];
listOfProducts[min] = listOfProducts[i];
listOfProducts[i] = temporary;
}
}
So precisely, we just changed 1 line:
for (int j = i + 1; j < this.listOfProducts.Count; j++)
^^^^^
If you take another look at the pseudo-code in the above link, you'll see that this function now resembles it:
procedure selection sort
list : array of items
n : size of list
for i = 1 to n - 1
/* set current element as minimum*/
min = i
/* check the element to be minimum */
for j = i+1 to n
if list[j] < list[min] then
min = j;
end if
end for
/* swap the minimum element with the current element*/
if indexMin != i then
swap list[min] and list[i]
end if
end for
end procedure
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 am trying to create a draft order for a game I'm working on but I cannot get it to function how I need it to. I need 5 rounds with each round number properly and each pick should be numbered 1-10 for each round. Here is the code I'm working with:
List<Draft> _draftorder = new List<Draft>();
foreach (Team t in teams)
{
for (int i = 1; i <= 5; i++)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = i++,
pick = i++,
});
}
}
Any help is appreciated!
I find the logic is easier to follow when the code matches the stepwise workflow of the requirements, and the variable names match the concepts in the requirements.
List<Draft> _draftorder = new List<Draft>();
for (int currentRound=1; currentRound<=10; currentRound++)
{
int pickOrder = 1;
foreach (Team t in teams)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = currentRound,
pick = pickOrder++
});
}
}
You need to add an additional for loop and put the foreach loop inside the first for loop:
List<Draft> _draftorder = new List<Draft>();
// Allow 5 rounds
for (int i = 1; i <= 5; i++)
{
// Allow every team to pick some drafts
foreach (Team t in teams)
{
// Limit them to a specific amount of picks per round
for (int j = 1; j <= 10; j++)
{
_draftorder.Add(new Draft()
{
city = t.city,
round = i,
pick = j,
});
}
}
}
Is there a way to use a loop that takes the first 100 items in a big list, does something with them, then the next 100 etc but when it is nearing the end it automatically shortens the "100" step to the items remaining.
Currently I have to use two if loops:
for (int i = 0; i < listLength; i = i + 100)
{
if (i + 100 < listLength)
{
//Does its thing with a bigList.GetRange(i, 100)
}
else
{
//Does the same thing with bigList.GetRange(i, listLength - i)
}
}
Is there a better way of doing this? If not I will at least make the "thing" a function so the code does not have to be copied twice.
You can make use of LINQ Skip and Take and your code will be cleaner.
for (int i = 0; i < listLength; i=i+100)
{
var items = bigList.Skip(i).Take(100);
// Do something with 100 or remaining items
}
Note: If the items are less than 100 Take would give you the remaining ones.
I didn't like any of the answers listed, so I made my own extension:
public static class IEnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> MakeGroupsOf<T>(this IEnumerable<T> source, int count)
{
var grouping = new List<T>();
foreach (var item in source)
{
grouping.Add(item);
if(grouping.Count == count)
{
yield return grouping;
grouping = new List<T>();
}
}
if (grouping.Count != 0)
{
yield return grouping;
}
}
}
Then you can use it:
foreach(var group in allItems.MakeGroupsOf(100))
{
// Do something
}
You can keep an explicit variable for the end point:
for (int i = 0, j; i < listLength; i = j)
{
j = Math.min(listLength, i + 100);
// do your thing with bigList.GetRange(i, j)
}
In dotnet 6 you can use chunk:
//Child collections will be comprised of 10 elements each.
IEnumerable<int[]> sublists = numbers.Chunk(10);
https://exceptionnotfound.net/bite-size-dotnet-6-chunk-in-linq/
There is also a reference to use a group by to do this, which is quite an interesting solution for older versions of the framework:
Split a collection into `n` parts with LINQ?
List<int> list = null;
int amount_of_hundreds = Math.Floor(list.Count/100);
int remaining_number = list.Count - (amount_of_hundreds * 100);
for(int i = 0; i < amount_of_hundreds; ++i)
{
for(int j = 0; j < 100; ++j)
{
int item = list[(i * 100) + j];
// do what you want with item
}
}
for(int i = 0; i < remaining_number; ++i)
{
int item = list[(amount_of_hundreds * 100) + i];
// do what you want with item
}
you can try below approach also:
int i = 1;
foreach (item x in bigList)
{
batchOperation.Insert(x); //replace it with your action; create the batch
i++;
if (i >100)
{
table.ExecuteBatch(batchOperation); //execute the batch
batchOperation.Clear();
i = 1; // re-initialize
}
}
if (batchOperation.Count >= 1 )
{
table.ExecuteBatch(batchOperation); //do this for the residue items
}
This is my solution, before all, i skip the last rows (rows - skiplast) after i get top of the array.
[TestMethod]
public void SkipTake()
{
var rows = 100;
var skip = 1;
var skiplast = 95;
var result = Enumerable.Range(1, rows).Take(rows - skiplast).Skip(skip - 1 == 0 ? 1 : skip).ToList();
}
You can split one big List to many small lists by limit, like this:
var limit = 100;
foreach (var smallList in bigList.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / limit)
.Select(x => x.Select(v => v.Value).ToList())
.ToList())
{
Console.WriteLine($"{smallList.Count}");
Console.WriteLine(String.Join("\r\n", smallList));
}