How to search for a range of values using binary search? - java

I am trying to write a program that will take an ArrayList of sorted integers, and there will be a binary search method where you specify the range and the values that you want to be returned from the ArrayList.
import java.util.ArrayList;
public class ArraySearch {
ArrayList<Integer> myArrayList = new ArrayList<Integer>();
static ArrayList<Integer> range = new ArrayList<Integer>();
public static ArrayList<Integer> binarySearch(ArrayList<Integer> arrayList, int min, int max, int first, int last)
throws NotFoundException {
if(first > last) {
throw new NotFoundException("Elements not found.");
}
else {
int middle = (first + last) /2;
int mid_number = arrayList.get(middle);
if(mid_number >= min && mid_number <= max)
{
range.add(middle);
}
if(mid_number <= min) {
if(mid_number == min) {
range.add(arrayList.get(middle));
return binarySearch(arrayList, min, max, first, middle-1);
}
return binarySearch(arrayList, min, max, first, middle-1);
}
else {
if(mid_number == max) {
range.add(arrayList.get(middle));
return binarySearch(arrayList, min, max, middle+1,last);
}
return binarySearch(arrayList, min, max, middle+1,last);
}
}
}
public static void main (String [] args) throws NotFoundException {
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(0);
a.add(1);
a.add(2);
a.add(3);
a.add(6);
a.add(7);
a.add(7);
a.add(10);
a.add(10);
a.add(10);
binarySearch(a, 3, 7, 0, 9);
}
}
Could I please get some help?
I have no idea what the base case condition should be that should return the ArrayList range. And I think I might have got the logic in the binary search method wrong.

First , find the lower index range of the element. then , find the upper index range of the element .
To find lower index range, when you find the element, instead of breaking out of the loop, like we do so in normal search. put the value in index and make the mid one less than the current index you are checking. This will make the loop go again to find the lower index and not stop at the first index.
Similarly for the upper index , update start to the current index we are checking + 1.
Have a look at the code.
public ArrayList<Integer> searchRange(ArrayList<Integer> a, int b) {
int first=lowerIndex(a,b);
int last=upperIndex(a,b);
ArrayList<Integer> result= new ArrayList<Integer>();
result.add(first);
result.add(last);
return result;
}
public int upperIndex(ArrayList<Integer> a, int b) {
int index=-1; //will return -1 if element not found.
int start=0;
int end=a.size()-1;
while(start<=end){
int mid=(start+end)/2;
if(a.get(mid)==b){
index=mid; //Update Index, which is the upper index here.
start=mid+1; // This is the main step.
}
else if(a.get(mid)>b){
end=mid-1;
}
else{
start=mid+1;
}
}
return index;
}
public int lowerIndex(ArrayList<Integer> a, int b) {
int index=-1; // will return -1 if element not found
int start=0;
int end=a.size()-1;
while(start<=end){
int mid=(start+end)/2;
if(a.get(mid)==b){
index=mid; //Update Index, which is the lower index here.
end=mid-1; // This is the main step .
}
else if(a.get(mid)>b){
end=mid-1;
}
else{
start=mid+1;
}
}
return index;
}

Related

What mistake am I making while memoizing this solution for Leetcode problem jump game 4

So the question is
You are given a 0-indexed integer array nums and an integer k.
You are initially standing at index 0. In one move, you can jump at most k steps forward without going outside the boundaries of the array. That is, you can jump from index i to any index in the range [i + 1, min(n - 1, i + k)] inclusive.
You want to reach the last index of the array (index n - 1). Your score is the sum of all nums[j] for each index j you visited in the array.
Return the maximum score you can get.
Example 1:
Input: nums = [1,-1,-2,4,-7,3], k = 2
Output: 7
Explanation: You can choose your jumps forming the subsequence [1,-1,4,3] (underlined above). The sum is 7.
I wrote a recursive solution in which we explore all the possibilies . If the function is called at index ind , then the value at index ind is added to a variable curSum for the next function call.
The base condition is when we reach the last index , we will return curSum+value of last index.
Here is the code:
class Solution {
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
if(ind==nums.length-1)
return curSum+nums[ind];
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,curSum+nums[ind]));
return max;
}
}
Code works fine except the exponential complexity ofcourse.
I tried memoizing it as
class Solution {
static int[] dp;
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
dp=new int[nums.length];
Arrays.fill(dp,min);
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
if(ind==nums.length-1)
return curSum+nums[ind];
if(dp[ind]!=min)
return dp[ind];
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,curSum+nums[ind]));
return dp[ind]=max;
}
}
But this solution gives the wrong max everytime and I am not quite able to figure out why.
Any hints will be appreciated.
Thanks
You only need to memoize dp[index] instead of calling f(index, currSum).
Take for example present arr.length = 5, k = 2
For index-2 you need wether index 3 or 4 gives better points to you irrespective of your present point.
Better way would be :
class Solution {
static int[] dp;
static int min= Integer.MIN_VALUE;
public int maxResult(int[] nums, int k) {
dp=new int[nums.length];
Arrays.fill(dp,min);
return max(nums,0,k,0);
}
public int max(int [] nums, int ind, int k, int curSum)
{
// base-case
if(ind==nums.length-1)
return curSum+nums[ind];
// if already memoized
if(dp[ind]!=min)
return dp[ind] + curSum;
// if not memoized, calculate value now
int max=Integer.MIN_VALUE;
for(int i=ind+1;i<=Math.min(nums.length-1,ind+k);i++)
max=Math.max(max, max(nums,i,k,nums[ind]);
// memoize here
dp[ind] = max
return dp[ind] + curSum;
}
}

Unable to count subsets with a given sum via memoization when array has 0 as an item in it

I have been given an array along with a sum . I have to return the count of number of subsets of array such that sum of elements of the subset is equal to the given sum.
My code works if all array elements are greater than zero but fails even if one item is zero.
eg : {0,0,1} and sum = 1.
I am using memoization in java. Can anyone guide me on this
static int[][] dp;
public static int count(int[] arr,int sum){
int size=arr.length;
dp=new int[size+1][sum+1];
for(int i=0;i<=size;i++){
for(int j=0;j<=sum;j++){
dp[i][j]=-1;
}
}
return helper(arr,sum,size);
}
public static int helper(int[] arr,int sum,int size){
if(sum==0){
return 1;
}
if(size==0){
return 0;
}
if(dp[size][sum]!=-1){
return dp[size][sum];
}
if(arr[size-1]>sum){
return dp[size][sum]=helper(arr,sum,size-1);
}else{
return dp[size][sum]=helper(arr,sum-arr[size-1],size-1)+helper(arr,sum,size-1);
}
}

Finding maximum using recursion

It gives me out of bound exception. And it seems not to recognize the first element in the array.
public class MaximumRec {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] A = {90, 45,12,19,72,55,15,19};
int x = maximumR(A,8);
System.out.print(x);
}
static int maximumR(int[] A, int n)
{
int max;
if (n == 1)
{
return A[n];
}
else
{
max = maximumR(A, n-1);
if(max < A[n] )
{
max = A[n];
}
}
return max;
}
}
It's because of the 8 you assign to n. And then you ask for the 8th element in the array, but the array starts counting at 0 so the A[8] doesn't exists. The max is A[7].
Java follows C-style indexing or Zero-based numbering in which index starts from 0 instead of 1. Also a good practice would be to dynamically assign the number instead of hard coding it.
e.g.
int x = maximumR(A, A.length - 1);

What's wrong with my solution of SearchInsert?

puzzle:
Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
And my code here:
public class Solution {
public static int searchInsert(int[] nums, int target) {
return searchInsert(nums, target, 0, nums.length-1);
}
private static int searchInsert(int[] nums, int target, int start, int end) {
if(start <= end) {
return target <= nums[start] ? start : (start+1);
}
int m = (start + end) / 2;
if(nums[m] == target) {
return m;
} else if(nums[m] < target) {
return searchInsert(nums, target, m+1, end);
} else {
return searchInsert(nums, target, start, m-1);
}
}
public static void main(String[] args) {
int[] nums = {1, 3};
System.out.print(searchInsert(nums, 4);
}
}
The result turns out like this:
Input:
[1,3]
4
Output:
1
Expected:
2
I've simulate the process of this input over and over again on paper, but just cannot figure out how my code could output 2.
Please help me with this, thx in advance.
The condition:
start <= end
is incorrect. The condition is true immediately for a non-zero length array, because start == 0 and end == <something which is at least 0>, so it will return either start or start+1 straight away - in your case, start+1.

binary search, count duplicates

I have sorted array
{1,2,3,5,5,5,7,8,8}
I would like to count how many times the number that i am sending is found in the array in longn only.
for example:
public static int count(int[] array,5)
will reply 3
public static int count(int[] array,8)
will reply 2
so my plan is:
1) to do a binary search to find the number
2) binary search the top border index and the bottom border index.
3) print (top index - bottom index) will give me the time of target number in the array.
Is my code is logn ?
Please help! :)
public class binarySearch
{
public static void main(String[]args)
{
System.out.println("d");
int[]data={1,1,2,3,1,1,1};
System.out.println(count(data,1));
}
public static int count(int[] a, int x)
{
int low=0;
int high = a.length-1;
int count=0;
while(low <=high)
{
int mid=((low+high)/2);
if(x>a[mid])
low=mid+1;
if(x<a[mid])
high=mid-1;
if(x==a[mid])
{
int top=findTopIndex(a,x,mid);
int bottom=findBottomIndex(a,x,mid);
return (top-bottom);
}
}
return 111111111;
}
public static int findTopIndex(int[] a, int x, int index)
{
int low=index;
int high = a.length-1;
int mid;
if(x==a[high])
return high;
while(low <= high)
{
mid=((low+high)/2);
if(x<a[mid]&&x==a[mid-1])
return mid-1;
else if(x==a[mid])
low=mid+1;
else if(a[mid]>x && a[mid-1]!=x)
high=mid-1;
}
return 11111111;
}
public static int findBottomIndex(int[] a, int x, int index)
{
int low=0;
int high = index-1;
int mid;
if(x==a[low])
return low-1;
while(low <= high)
{
mid=((low+high)/2);
if(x>a[mid]&&x==a[mid+1])
return mid;
else if(x==a[mid])
high=mid-1;
else if(a[mid]<x && a[mid+1]!=x)
low=mid+1;
}
return 111;
}
}
What you have written is really close to the solution you need. You first do a binary search to find a single instance of the number you are searching for(let's say its found on position index) and then you do two more binary searches -one for the sequence 0, index, and one for index, size to find up to where in both sequences is the number found.
So I suggest you simply pass an index to both findTopIndex and findBottomIndex and make use of it. I can write the whole solution but it will be better for you to come to it on your own.

Categories

Resources