Need to understand the behaviour of BinarySearch and IndexOf methods - c#

I have List and its values is ("Brandenburg","Alabama" and "Alberta"). When i used BinarySearch("Brandenburg") method, it returns -4 instead of 0. but i can get the correct index, when sorted this list. Why it returns wrong value if I use the unsorted list?. And I have also get the correct index from IndexOf("Brandenburg") method. Which method is useful that i can use?.
Thanks in Advance,
Prithivi

It MUST be sorted, to use binary search. The reason you're getting -4 is;
Your collection isn't sorted and for binary search the list will 'cut' in half each iteration. So:
When it starts, the topIndex == 0 and bottom = 2
TopIndex -> (0) "Brandenburg",
(1) "Alabama"
BottomIndex -> (2) "Alberta
The binarysearch will check the item in the middle: (2-0) / 2 = 1. If you're searching for Brandenburg. It will compare Alabama with your search item. The letter B is 'bigger' than letter 'A'. So it moves the topIndex to index 1.
(0) "Brandenburg",
TopIndex -> (1) "Alabama"
BottomIndex -> (2) "Alberta
Then it will compare to the next 'middle' item. In this case again Alabama. (2-1) / 2 = 1. It will also be compare to the bottomIndex, but this is the last one.
When binarysearch returns a negative number, it means that the item cannot be found. The negative number is the Index where it should be inserten. (-result -1) So if you want the new item added, it should be inserted on index (--4 -1) == 3

Let me explain how binary search works.
Say you have this array:
{1, 3, 5, 7, 10, 15, 20}
And I want to find the index of 15. What binary search will do is that it looks at the middle of the array, 7. Is 7 greater or less than 15? If it is less than 15, do the same thing again on the second half of the array (10, 15, 20). If it is greater than 15, do it on the first half (1, 3, 5). If it is equal to 15, then that means 15 is found.
This means that the array must be sorted for binary search to work. This explains why doing a binary search on your array returns a negative number. Because obviously, the method can't find the string you requested using the binary search algorithm.
You can get the correct index with IndexOf. This is because IndexOf uses a linear search to find the item. It looks at each element in the array and compare to the one that you're finding. Therefore, whether the array is sorted doesn't matter.
Note: I have not read the source code of IndexOf. It might use a binary search if it finds that the array is sorted. This is only my guess.

Related

Sparse matrix (next element)

I have to do a home project, but I can't do it with my current knowledge. I searched for it, but I haven't found any useful tips. Here is a sparse matrix, and I have to get the "Next elements" The next elements are the element who are the second in a 2d arrays column, if there is no second element, for that column the output is "-1" . In the output I need to write out the serial number of the element.
Here is the 2d array, the red numbers are the "serial numbers"
The output should be this : 8, -1, -1, 6, -1, -1, 9, -1, -1, -1, -1.
To clarify: if you scan left-to-right (and continue on the next row). Replace each value (greater than zero) with it's ordinal position (shown in red). Then scan again (left-to-right, top-to-bottom) when you find any number (greater than zero), search down in that column. If there is another number (red) (greater than zero) in that column, print that (red) number. Otherwise print -1.
Second-pass, finding "Next elements".
0 finds 8, 1 finds nothing, 2 finds nothing, 3 finds 6, etc.
I got the answer, i just overthinked the algorytm of the program.
for(int i = 0; i < oszlop.Length; i++)
{
kov[i] = Array.IndexOf(oszlop, oszlop[i], i + 1);
}
The "oszlop" array is an 1d array and contains the column indexes of the rare elements. If i got a rare element, i check if is there any other element with the same column index, if there is, i will save in the "kov" array, otherwise save "-1" in to the "kov" array.

What is the logic behind the .. range selection operator?

I am trying to understand why the .. operator works the way it does, for example:
var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[1..4]; // This returns array with 1,2,3
Logically, I would assume the result would be either 1,2,3,4 or 2,3 (if the last index isn't included then the first one shouldn't also)
or
var test = data[0..]; // This returns array with 0,1,2,3,4,5,6,7,8,9,10 (zero at index 0 is also included)
var test = data[^0..]; // This returns array with nothing, where i would expect 10, since 10 is at index zero if we traverse the array backwards
I know there must be a reason why it was designed to work like that, but I can't seem to figure it out, so what is the purpose of this behavior?
Thank you.
Why is the end index not included, but the start index is?
This is known as a half open range, and there are already questions asking about this in Python and C++. Essentially, the main advantage of this is that:
The length of the range is exactly (end - start).
You don't need to add/minus 1 as much in range-based algorithms
To slice something in half at an index, you can use the same index: x[..i] gives you the first half, and x[i..] gives you the second half. i.e. x[..i] concatenated with i[i..] is equal to x itself.
If a range's end is equal to another's start, the two ranges are immediately next to each other, and no overlapping.
Why is data[^0..] empty?
This is documented clearly. ^n means Length - n, so ^0 means data.Length - 0 here, which is just data.Length. data[data.Length..] is clearly empty.
It's simple enough. Consider the following:
var data = new []{0,1,2,3,4,5,6,7,8,9,10};
var test = data[0..10];
The 0 to 10, implies that it will select 11 items, if the last was inclusive. You have to start at 0, as C# indexes are zero based, and you'd need a -1 to include the first, if they were both exlusive.
As mentioned in the documentation:
A range specifies the start and end of a range. Ranges are exclusive, meaning the end isn't included in the range. The range [0..^0] represents the entire range, just as [0..sequence.Length] represents the entire range.
The data[^0..]; statements means, starting from the 0 position, bring whatever is below it. The ^ operator works inversly for the same reasons. (^0 is not inclusive of 0)

invert a specific part of a list C#

I need a program to reverse part of a list between two terminals.
Example :
List: 1, 2, 3, 3, 5, 4
Output: 1, 2, 3, 3, 4, 5 (Only the 4 and 5 are inverted)
I found this:
positionCrepe.Reverse(indexOfMaxToSearch, positionCrepe.Count);
But it doesn't work because I have a mistake:
System.ArgumentException: The offset and length were out of bounds for this table or the number is greater than the number of index elements at the end of the source collection.
However
indexOfMaxToSearch = 2
and
positionCrepe.count = 5
and so it does not exceed the index of the table
Anyone have a solution?
Thank you.
The second argument is how many elements you want to reverse, not how many elements there are in the list.
So if you want to reverse everything starting from indexOfMaxToSearch, you want to reverse positionCrepe.Count - indexOfMaxToSearch elements:
positionCrepe.Reverse(indexOfMaxToSearch, positionCrepe.Count - indexOfMaxToSearch);
The error message is actually saying that the first argument plus the second argument is out of range of the array.
if you look at the definition of Reverse,
index: The zero-based starting index of the range to reverse.
count: The number of elements in the range to reverse.
You can use the following to make it work. Count must be less then the remaining indecies
positionCrepe.Reverse(2, positionCrepe.Count - 2);

Generate Number Range in a List of Numbers

I am using C# and have a list of int numbers which contains different numbers such as {34,36,40,35,37,38,39,4,5,3}. Now I need a script to find the different ranges in the list and write it on a file. for this example it would be: (34-40) and (3-5). What is the quick way to do it?
thanks for the help in advance;
The easiest way would be to sort the array and then do a single sequential pass to capture the ranges. That will most likely be fast enough for your purposes.
Two techniques come to mind: histogramming and sorting. Histogramming will be good for dense number sets (where you have most of the numbers between min and max) and sorting will be good if you have sparse number sets (very few of the numbers between min and max are actually used).
For histogramming, simply walk the array and set a Boolean flag to True in the corresponding position histogram, then walk the histogram looking for runs of True (default should be false).
For sorting, simply sort the array using the best applicable sorting technique, then walk the sorted array looking for contiguous runs.
EDIT: some examples.
Let's say you have an array with the first 1,000,000 positive integers, but all even multiples of 191 are removed (you don't know this ahead of time). Histogramming will be a better approach here.
Let's say you have an array containing powers of 2 (2, 4, 8, 16, ...) and 3 (3, 9, 27, 81, ...). For large lists, the list will be fairly sparse and sorting should be expected to do better.
As Mike said, first sort the list. Now, starting with the first element, remember that element, then compare it with the next one. If the next element is 1 greater than the current one, you have a contiguous series. Continue this until the next number is NOT contiguous. When you reach that point, you have a range from the first remembered value to the current value. Remember/output that range, then start again with the next value as the first element of a new series. This will execute in roughly 2N time (linear).
I would sort them and then check for consecutive numbers. If the difference > 1 you have a new range.

Getting i-th value from a SortedList or SortedDictionary

I have a sorted collection of objects (it can be either SortedList or SortedDictionary, I will use it mainly for reading so add performance is not that important). How can I get the i-th value?
So e.g. when I have numbers 1, 2, 3, 4, 5 in the collection and I want the median (so 3 in this example), how can I do it?
You can use code like
list.Values[index]
for a sorted list.
The easiest way with a SortedDictonary would be to use the ElementAt() method:
dict.ElementAt(index).Value
However, this is slower than in the list case.
In either case, you need to check your count. If it is odd, take index = (list.length-1) / 2 ). If it is even, take index1 = list.length/2 AND index2 = list.length/2 - 1 and average the values.
Try something like this:
list.Values[list.Count / 2];
Note that a true median would average the two numbers in the middle if Count is even.
You can extract value at a particular position by using the below syntax:
sortedDictionaryName.ElementAt(index);
If you want extract key or value of an element at a desired index:
sortedDictionaryName.ElementAt(index).Key //For only Key
sortedDictionaryName.ElementAt(index).Value //For only Value
If you need to get an element by index in a SortedDictionary many times, the performance is miserable. Make a new SortedList with the SortedDictionary as input and access the SortedList. Runs many, many times faster.

Categories

Resources