If I search through my sorted array it only displays one of the values even if there are multiple of the same value in the array. I don't want it to tell me how many duplicates there are, I want it to display all of the duplicate values in the array that I search for. Or is there are different search I need to use to do this?
So if I have array1{1,2,3,4,4,5,5,5,5,6} and I search for 5 I want it to output:
5
5
5
5
This is is my code with binary search.
public class search
{
public static void Main(string[] args)
{
//Arrays are created here. e.g. array1{1,2,3,4,4,5,5,5,5,6}
int Input;
Console.WriteLine("Enter the number you would like to search for.");
Input = Convert.ToInt32(Console.ReadLine());
int y = BinarySearch(array1, Input);
Console.WriteLine("array1 {0} : array2{1} : array3 {2} : array4 {3} : array5 {4}",array1[y], array2[y], array3[y], array4[y], array5[y]);
}
public static int BinarySearch(double[] Array, int Search)
{
int x = Array.Length;
int low = 0;
int high = x - 1;
while (low <= high)
{
while (low <= high)
{
int mid = (low + high) / 2;
if (Search < Array[mid])
{
high = mid - 1;
}
else if (Search > Array[mid])
{
low = mid + 1;
}
else if (Search == Array[mid])
{
Console.WriteLine("{0}", Search);
return mid;
}
}
Console.WriteLine("{0} was not found.", Search);
}
return high;
}
}
Sure, you can use binary search for this. But your code is kinda weird. Because when you find the first element here else if (Search == array[mid]) ... you immediately return from the function and never call it again. That is why you get only one result.
To make it work, when you find such element, you need to search in the array through indices low ... mid-1 and then through indices mid+1 ... high.
Here is the code, but I strongly advise you not just to copy that and maybe try to rewrite this into while loop (every recursion can be rewritten as a loop)
static void BinarySearch(int[] array, int low, int high, int searchedValue)
{
if (low > high)
return;
int mid = (low + high) / 2;
if (searchedValue < array[mid])
{
high = mid - 1;
BinarySearch(array, low, high, searchedValue);
}
else if (searchedValue > array[mid])
{
low = mid + 1;
BinarySearch(array, low, high, searchedValue);
}
else if (searchedValue == array[mid])
{
Console.WriteLine(array[mid]);
BinarySearch(array, low, mid - 1, searchedValue);
BinarySearch(array, mid + 1, high, searchedValue);
}
}
Related
Another problem here!
I've been coding a Binary Search with recursive algorithm.
Now it seems to be some problem when it search in upper half of the array. I can't really find whats wrong.
//=====Binary Search=====//
static int BinarySearch(City[] cities, int key, int low, int high)
{
int mid;
if (low > high)
{
return -1;
}
mid = low + high / 2;
if (key == cities[mid].temp)
{
return mid;
}
else if (key < cities[mid].temp)
{
return BinarySearch(cities, key, low, mid - 1);
}
else
{
return BinarySearch(cities, key, mid +1, high);
}
}
When I search for a number that can't be found it will print: "can't find temperature".
It is doing its work as long as I don't search for a number in the upper half.
Console.WriteLine("\n\tBINÄR SÖKNING\n");
do
{
loop = true;
Console.Write("search temperature:");
str = Console.ReadLine();
try
{
key = Convert.ToInt32(str);
index = BinarySearch(cities, key, low, high);
if (index == -1)
{
Console.WriteLine($"can't find temperature: {key}°C");
}
else
{
Console.WriteLine("");
Console.WriteLine(cities[index].ToString());
loop = false;
}
}
catch
{
Console.WriteLine("Only numbers, please");
}
} while (loop);
If I search for a number in the upper half, the console will print "Only numbers, please". It goes to the catch-part. as it should if I search for something that can NOT convert to int.
Operator precedence bites again.
How is the expression low + high / 2 parsed?
Probably not the way you think. Multiplicative operators have higher precedence than additive operators, so
low + high / 2
gets parsed as
low + (high / 2)
rather than your intended
(low + high) / 2
I learnt about quick sort and how it can be implemented in both Recursive and Iterative method.
In Iterative method:
Push the range (0...n) into the stack
Partition the given array with a pivot
Pop the top element.
Push the partitions (index range) onto a stack if the range has more than one element
Do the above 3 steps, till the stack is empty
And the recursive version is the normal one defined in wiki.
I learnt that recursive algorithms are always slower than their iterative counterpart.
So, Which method is preferred in terms of time complexity (memory is not a concern)?
Which one is fast enough to use in Programming contest?
Is c++ STL sort() using a recursive approach?
In terms of (asymptotic) time complexity - they are both the same.
"Recursive is slower then iterative" - the rational behind this statement is because of the overhead of the recursive stack (saving and restoring the environment between calls).
However -these are constant number of ops, while not changing the number of "iterations".
Both recursive and iterative quicksort are O(nlogn) average case and O(n^2) worst case.
EDIT:
just for the fun of it I ran a benchmark with the (java) code attached to the post , and then I ran wilcoxon statistic test, to check what is the probability that the running times are indeed distinct
The results may be conclusive (P_VALUE=2.6e-34, https://en.wikipedia.org/wiki/P-value. Remember that the P_VALUE is P(T >= t | H) where T is the test statistic and H is the null hypothesis). But the answer is not what you expected.
The average of the iterative solution was 408.86 ms while of recursive was 236.81 ms
(Note - I used Integer and not int as argument to recursiveQsort() - otherwise the recursive would have achieved much better, because it doesn't have to box a lot of integers, which is also time consuming - I did it because the iterative solution has no choice but doing so.
Thus - your assumption is not true, the recursive solution is faster (for my machine and java for the very least) than the iterative one with P_VALUE=2.6e-34.
public static void recursiveQsort(int[] arr,Integer start, Integer end) {
if (end - start < 2) return; //stop clause
int p = start + ((end-start)/2);
p = partition(arr,p,start,end);
recursiveQsort(arr, start, p);
recursiveQsort(arr, p+1, end);
}
public static void iterativeQsort(int[] arr) {
Stack<Integer> stack = new Stack<Integer>();
stack.push(0);
stack.push(arr.length);
while (!stack.isEmpty()) {
int end = stack.pop();
int start = stack.pop();
if (end - start < 2) continue;
int p = start + ((end-start)/2);
p = partition(arr,p,start,end);
stack.push(p+1);
stack.push(end);
stack.push(start);
stack.push(p);
}
}
private static int partition(int[] arr, int p, int start, int end) {
int l = start;
int h = end - 2;
int piv = arr[p];
swap(arr,p,end-1);
while (l < h) {
if (arr[l] < piv) {
l++;
} else if (arr[h] >= piv) {
h--;
} else {
swap(arr,l,h);
}
}
int idx = h;
if (arr[h] < piv) idx++;
swap(arr,end-1,idx);
return idx;
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String... args) throws Exception {
Random r = new Random(1);
int SIZE = 1000000;
int N = 100;
int[] arr = new int[SIZE];
int[] millisRecursive = new int[N];
int[] millisIterative = new int[N];
for (int t = 0; t < N; t++) {
for (int i = 0; i < SIZE; i++) {
arr[i] = r.nextInt(SIZE);
}
int[] tempArr = Arrays.copyOf(arr, arr.length);
long start = System.currentTimeMillis();
iterativeQsort(tempArr);
millisIterative[t] = (int)(System.currentTimeMillis()-start);
tempArr = Arrays.copyOf(arr, arr.length);
start = System.currentTimeMillis();
recursvieQsort(tempArr,0,arr.length);
millisRecursive[t] = (int)(System.currentTimeMillis()-start);
}
int sum = 0;
for (int x : millisRecursive) {
System.out.println(x);
sum += x;
}
System.out.println("end of recursive. AVG = " + ((double)sum)/millisRecursive.length);
sum = 0;
for (int x : millisIterative) {
System.out.println(x);
sum += x;
}
System.out.println("end of iterative. AVG = " + ((double)sum)/millisIterative.length);
}
Recursion is NOT always slower than iteration. Quicksort is perfect example of it. The only way to do this in iterate way is create stack structure. So in other way do the same that the compiler do if we use recursion, and propably you will do this worse than compiler. Also there will be more jumps if you don't use recursion (to pop and push values to stack).
That's the solution i came up with in Javascript. I think it works.
const myArr = [33, 103, 3, 726, 200, 984, 198, 764, 9]
document.write('initial order :', JSON.stringify(myArr), '<br><br>')
qs_iter(myArr)
document.write('_Final order :', JSON.stringify(myArr))
function qs_iter(items) {
if (!items || items.length <= 1) {
return items
}
var stack = []
var low = 0
var high = items.length - 1
stack.push([low, high])
while (stack.length) {
var range = stack.pop()
low = range[0]
high = range[1]
if (low < high) {
var pivot = Math.floor((low + high) / 2)
stack.push([low, pivot])
stack.push([pivot + 1, high])
while (low < high) {
while (low < pivot && items[low] <= items[pivot]) low++
while (high > pivot && items[high] > items[pivot]) high--
if (low < high) {
var tmp = items[low]
items[low] = items[high]
items[high] = tmp
}
}
}
}
return items
}
Let me know if you found a mistake :)
Mister Jojo UPDATE :
this code just mixes values that can in rare cases lead to a sort, in other words never.
For those who have a doubt, I put it in snippet.
I have the following code which fetches a country ID based on the IP address:
countryID = GetAllCountryIPRanges().Single(c => c.BeginIPNum <= intIp && intIp <= c.EndIPNum).CountryID;
It's unfortunately quite slow as there's ~200,000 records. The ranges do not overlap, and GetAllCountryIPRanges() is in ascending order by BeginIPNum.
How do I implement .BinarySearch() on this list to find the correct record?
List has a binary search method but since binary search is so easy to implement and since the IComparator you would need to define is so complex due to the range thing I suggest you implement the binary search method
Something like this (NOT TESTED!)
public static IPRange BinarySearch(List<IPRange> source, int intIp)
{
int startIndex = 0;
int endIndex = source.Count;
while (endIndex >= startIndex)
{
int middleIndex = startIndex + (endIndex - startIndex) / 2;
if (source[middleIndex].BeginIPNum <= intIp && intIp <= source[middleIndex].EndIPNum)
{
return source[middleIndex];
}
else if (source[middleIndex].BeginIPNum < intIp)
{
startIndex = middleIndex + 1;
}
else
{
endIndex = middleIndex - 1;
}
}
return null;
}
Assuming the List is sorted and there are no overlapping ranges.
Gotta love the elegance of recursion... (not tested)
public static IPRange BinarySearch(IList<IPRange> ipList, int ip)
{
return BinarySearch(source, ip, 0, ipList.Count - 1);
}
public static IPRange BinarySearch(IList<IPRange> ipList, int ip, int min, int max)
{
if (min > max)
{
throw new Assertion("Error: ipList is empty, out-of-order, or does not contain an element that includes the IP");
}
int mid = (min + max) / 2;
var midIp = ipList[mid];
if (ip < midIp.BeginIpNum)
{
return BinarySearch(ipList, ip, min, mid-1);
}
if (ip > midIp.EndIpNum)
{
return BinarySearch(ipList, ip, mid+1, max);
}
return midIp;
}
(sorry if I'm asking too many questions)
Right now I'm just trying to figure out how to restructure the program I've written to meet criteria. I want to break it apart into different methods to make it easier to read, but I'm having trouble getting the different methods to play with each other (eg. variables scope errors).
Right now my code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Scoring {
class Program {
static int highOccurrence = 0;
static int lowOccurrence = 0;
static void Main(string[] args) {
int[] scores = { 4, 7, 9, 3, 8, 6 };
findScore(scores);
ExitProgram();
}
static int findOccurrence(int[] scores) { //find the number of times a high/low occurs
for (int i = 0; i < scores.Length; i++) {
if (low == scores[i]) {
lowOccurrence++;
//record number of time slow occurs
}
if (high == scores[i]) {
highOccurrence++;
//record number of times high occurs
}
}
}
static int findScore(int[] scores) {
int[] arrofNormal = new int[scores.Length];
int low = scores[0];
int high = scores[0];
int total = 0;
//int highOccurrence = 0;
//int lowOccurrence = 0;
for (int i = 0; i < scores.Length; i++) {
// if (low == scores[i]) {
// lowOccurrence++;
// //record number of time slow occurs
// }
// if (high == scores[i]) {
// highOccurrence++;
// //record number of times high occurs
// }
if (low > scores[i]) {
low = scores[i];
} //record lowest value
if (high < scores[i]) {
high = scores[i];
//record highest value
}
}
for (int x = 0; x < scores.Length; x++) {
if (scores[x] != low && scores[x] != high) {
arrofNormal[x] = scores[x];
//provides the total of the scores (not including the high and the low)
}
total += arrofNormal[x];
}
if (highOccurrence > 1) { //if there is more than 1 high (or 1 low) it is added once into the total
total += high;
if (lowOccurrence > 1) {
total += low;
}
}
Console.WriteLine("Sum = " + total);
return total; //remove not all code paths return.. error
}
static void ExitProgram() {
Console.Write("\n\nPress any key to exit program: ");
Console.ReadKey();
}//end ExitProgram
}
}
As you can see its still very much a work on progress. I get error such as "variable name" is does not exist in current context", I know this is a scope error, how can I fix it? Advice would be greatly appreciated :)
Yes, you could write this in Linq in 10 seconds but I think that you are trying to learn fundamental aspects of C# and Linq won't help with that.
Your problem is that you're trying to reach for variables that are in a different method (even though it's static, it doesn't matter). You can add parameters to the findOccurrence method if you need them in that particular scope.
private static void findOccurrence(int[] scores, int low, int high)
{ //find the number of times a high/low occurs
for (int i = 0; i < scores.Length; i++)
{
if (low == scores[i])
{
lowOccurrence++;
//record number of time slow occurs
}
if (high == scores[i])
{
highOccurrence++;
//record number of times high occurs
}
}
}
And in findScore() you can call the method above like this:
findOccurrence(scores, low, high);
if (highOccurrence > 1)
{ //if there is more than 1 high (or 1 low) it is added once into the total
total += high;
if (lowOccurrence > 1)
{
total += low;
}
}
I hope this will work as expected. Have fun learning C#
This should get you started :)
You'll need to move high and low outside of the findScore method and initialize with 0 instead of scores[0]. Then you'd have to call findScore before you call findOccurrence to allow the two variables to contain the values you want when you need them.
Also it looks like you come from a Java background. In C# all methods should start with a capital letter, as per naming convention.
You can benefit a lot from Linq. For example you can find low with scores.Min() and high with scores.Max().
findOccurrence could be written more briefly:
static int FindOccurence(int[] scores)
{
lowOccurrence = scores.Count(s => s == low);
highOccurrence = scores.Count(s => s == high);
}
Things like this will improve readability to me.
Here's an Introduction to LINQ
Below is my Generic Binary Search. It works okay with the integers type array (it finds all the elements in it). But the problem arises when I use a string array to find any string data. It runs okay for the first index and last index elements but I can't find the middle elements.
Stringarray = new string[] { "b", "a", "ab", "abc", "c" };
public static void BinarySearch<T>(T[] array, T searchFor, Comparer<T> comparer) {
int high, low, mid;
high = array.Length - 1;
low = 0;
if (array[0].Equals(searchFor))
Console.WriteLine("Value {0} Found At Index {1}",array[0],0);
else if (array[high].Equals(searchFor))
Console.WriteLine("Value {0} Found At Index {1}", array[high], high);
else
{
while (low <= high)
{
mid = (high + low) / 2;
if (comparer.Compare(array[mid], searchFor) == 0)
{
Console.WriteLine("Value {0} Found At Index {1}", array[mid], mid);
break;
}
else
{
if (comparer.Compare(searchFor, array[mid]) > 0)
high = mid + 1;
else
low = mid + 1;
}
}
if (low > high)
{
Console.WriteLine("Value Not Found In the Collection");
}
}
}
A binary search requires that the input be sorted. How is "b, a, ab, abc, c" sorted? It does not appear to be sorted on any obvious sort key. If you are trying to search unsorted data you should be using a hash set, not a binary search on a list.
Also, your calculation of midpoint is subtly wrong because the addition of high + low can overflow. It then becomes a negative number, which is divided by two.
This is extremely unlikely for realistically-sized arrays but it is entirely possible that you'll want to use this algorithm someday for data types that support indexing with large integers, like a memory-mapped file of sorted data.
The best practice for writing a binary search algorithm is to do (high - low) / 2 + low when calculating the midpoint, because that stays in range the whole time.
The two lines are suspect:
high = mid + 1
low = mid + 1
Hmm. Look at the offsets. Of course this is well documented Binary Search Algorithm on Wikipedia. You also do extra work. Examine the pseudo-code and examples closely.
pst Your advice really worked. :) this code is working for both int and string.
public static int BinarySearch<T>(T[] array, T searchFor, Comparer<T> comparer)
{
int high, low, mid;
high = array.Length - 1;
low = 0;
if (array[0].Equals(searchFor))
return 0;
else if (array[high].Equals(searchFor))
return high;
else
{
while (low <= high)
{
mid = (high + low) / 2;
if (comparer.Compare(array[mid], searchFor) == 0)
return mid;
else if (comparer.Compare(array[mid], searchFor) > 0)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
}
//Binary search recursive method
public void BinarySearch(int[] input,int key,int start,int end)
{
int index=-1;
int mid=(start+end)/2;
if (input[start] <= key && key <= input[end])
{
if (key < input[mid])
BinarySearch(input, key, start, mid);
else if (key > input[mid])
BinarySearch(input, key, mid + 1, end);
else if (key == input[mid])
index = mid;
if (index != -1)
Console.WriteLine("got it at " + index);
}
}
int[] input4 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
BinarySearch(input4, 1, 0, 8);