The Question:
Write a function, that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.
For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [−1, −3], the function should return 1.
Write an efficient algorithm for the following assumptions:
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [−1,000,000..1,000,000].
What I'm not understanding is how I'm failing these two cases, while also having 100% correctness
Also I'm not sure what cases I'm not covering, I managed for the case of 1 not existing in the first if statement, if there is a missing element in my for each loop, and finally, in the case of no missing element, to return the largest int + 1
Any help in clearing the confusion (and improvements for my time complexity) would be greatly appreciated
import java.lang.*;
import java.util.*;
class Solution {
public int solution(int[] A) {
// write your code in Java SE 8
//int counter = 0;
Set<Integer> set = new HashSet<Integer>();
Arrays.sort(A);
for(int n : A){
if(n > 0){
// counter++;
set.add(n);
}
}
//returns if set does not contain 1
if(!set.contains(1)){
return 1;
}
//returns missing int if it does not exist in set
for(int n: set){
if(!set.contains(n+1)){
return n+1;
}
}
//if no missing ints, returns end of array + 1
return A[A.length-1];
}
}
Answer Results:
I think you over-killed. first, using a hashset can lead to O(N) algorithm, which you have to avoid sort (O(NlogN)). But due to large memory footprint, I'd rather go with a sort and it is practically faster. Second, if you choose a sort of A, then a simple for-loop with detect any gap in the positive part of the array, which is now your result. You should avoid Hashset and the many loops in your code
I would use a BitSet.
Advantage: Fast scan for missing value. Performance: O(n)
public static int solution(int... values) {
BitSet bitSet = new BitSet();
for (int v : values)
if (v > 0)
bitSet.set(v);
return bitSet.nextClearBit(1);
}
Test
System.out.println(solution(1, 3, 6, 4, 1, 2));
System.out.println(solution(1, 2, 3));
System.out.println(solution(-1, -3));
Output
5
4
1
Related
For an assignment, I have to write some Bogosort code, and empirically determine the program's Big O notation.
However, I am unsure of whether the code works, because even though it sorts it for 3 and 4 element arrays of type int, I don't think it should be doing it in 0 ms.
Conversely, it's taking really long for 5 elements (still haven't gotten a successful case within 15 minutes yet), which indicates to me that there may be something wrong with the program. Since there are no errors being thrown, I believe any problem found would be a logic error.
I've tried running the IDE debugger on the program. Each of the methods used for the bogosort seemed to be working as intended, although I was not able to reach the case where it sorted an array properly while using the debugger.
However, by changing the values of the array to have it already sorted, I was able to test the case where the array was sorted, and the code was executed successfully.
This seems to indicate that the problem if there is any, would have to do with a logic error in sorting, where the sort method is somehow never getting to the correct solution.
The file is as shown below, and is commented.
Any suggestions for the program will have to pertain to the current structure (no adding methods, no using ArrayLists) since this is a homework assignment.
public class BogoSort {
public static void main(String[] args) {
int[] myArray = {20, 142, 115, 120, 140};
//sets start time
long start = System.currentTimeMillis();
bogoSort(myArray);
//sets end time
long end = System.currentTimeMillis();
printArray(myArray);
//print time (end time - start time)
System.out.println((end - start) + "ms");
}
// Places the elements of a into sorted order.
public static void bogoSort(int[] a) {
while(!isSorted(a)){
//calls the shuffle method if it's not sorted
shuffle(a);
}
}
// Returns true if a's elements are in sorted order.
public static boolean isSorted(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i+1]) {
//returns false if the number in this index is greater than
//the number in the next index aka not sorted
return false;
}
}
//else return true
return true;
}
// Shuffles an array of ints by randomly swapping each
// element with an element ahead of it in the array.
public static void shuffle(int[] a){
Random r = new Random();
for(int i = a.length - 1;i > 0;i--){
//random number between 0 and i
int j = r.nextInt(i);
//calls swap method
swap(a, i, j);
}
}
// Swaps a[i] with a[j].
public static void swap(int[] a, int i, int j) {
//temp variable to hold value of a[i] for swap
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void printArray(int[] a)
{
for(int i = 0; i < a.length; i++)
{
System.out.println(a[i]);
}
}
}//end of BogoSort class
Results should be as follows:
20
115
120
140
142
???ms
??? is a value for how long the program runs for, maybe about 720 ms, if I understand bogosort's Big O notation correctly.
Currently, I have not gotten a result for an array above a size of 4.
The time it takes for an array of 3 or 4 elements to sort is 0 ms, which is a bit odd to me, I feel like it should be about 24 ms for 3 elements, and 120 ms for 4 elements.
The result of the sorting of a 3 or 4 element array is that the numbers are sorted correctly, as per the expected result.
Your shuffle algorithm is broken due to an off-by-1 error. If you try it with int[] myArray = {2,1,3};, you'll see that it fails to complete for 3 elements as well.
When dealing with randomness, it's better to use statistics than eyeballing, because it's hard to notice this at a glance:
$ java BogoSort | head -n 100000 > file
$ sort file | uniq -c
33325 [1, 3, 2]
33315 [2, 1, 3]
33360 [3, 2, 1]
As you can see, you only ever generate 3 out of 6 possible permutations.
When you shuffle, like your comment indicates, you swap each element with one earlier in the array. You need to additionally allow the element to stay in place. You can do this by adding 1 to the index you choose:
// Shuffles an array of ints by randomly swapping each
// element with an element ahead of it in the array **or leave it in place**.
public static void shuffle(int[] a){
Random r = new Random();
for(int i = a.length - 1;i > 0;i--){
//random number between 0 and i
int j = r.nextInt(i+1); // <-- +1 here to select the current element
//calls swap method
swap(a, i, j);
}
}
The result now looks better (I rigged the program to keep printing even when it's sorted):
$ sort file | uniq -c
16807 [1, 2, 3]
16579 [1, 3, 2]
16745 [2, 1, 3]
16697 [2, 3, 1]
16361 [3, 1, 2]
16811 [3, 2, 1]
and indeed, it now finishes in 0-1ms. Running it on 8 numbers takes ~10ms, and 10 numbers take ~150ms, in line with the expected factorial curve.
The accepted answer correctly identified the fault and a straight forward solution.
I attempted to dig a bit deeper into why there were missing permutations. From that answers suggested starting point, [2,1,3], the incorrect shuffle would result can only produce two outcomes: [1,3,2] and [3,2,1]. This is already a mistake, since you expect a shuffle to be able to produce any of the 6 permutations. However, in addition, under the incorrect shuffle, those outcomes can only produce each other on another iteration of the bad shuffle.
So, to think about it differently, the only way for [2,1,3] to shuffle into [1,2,3] would be if the third element was allowed to stay in place. The only way for [1,3,2] to shuffle into [1,2,3] would be if the first element was allowed to stay in place. Finally, the only way for [3,2,1] to shuffle into [1,2,3] would be if the second element was allowed to stay in place. But the algorithm does not allow elements to stay, and any moved element during the shuffle iteration is not moved again.
The bad shuffle only produces the permutations that cause all the elements to be in a different position. In other words, it can only produce rotations!Only for the 3 element case
So, if the starting point is not a rotation of the sorted array, the algorithm will never terminate.
In comments, I had suggested an alternative shuffle implementation:
public static void shuffle(int[] a){
Random r = new Random();
int x = r.nextInt(a.length);
for(int i = a.length-1;i > 0;i--){
int j = r.nextInt(i);
if (j < x) break;
swap(a, i, j);
}
}
However, the weakness of this shuffle is that itself still lacks the ability to generate any possible permutation. The ability of the bogosort to eventually see all possible permutations using this implementation depends on each successive call to shuffle producing slightly different inputs for the next call.
I have an integer array of size 4. I am adding elements to it via the add method. This is as an unsorted array. I am sorting it via the sort method shown in the code below. The sort method places the smallest number in the position a[0]. When I try to add elements after I call the sort method I always get a return value of 0. Is there a way around this?
import java.util.Arrays;
public class Scrap {
private static int[] array = new int[4];
private static int i = 0;
public static void main(String[] args) {
Scrap pq = new Scrap();
pq.add(4);
pq.insert(3);
pq.add(5);
pq.sort();// smallest to largest sort method.
// System.out.println(array[0]);
pq.insert(1);
pq.sort();
int test = pq.Minimum();
System.out.println("The smallest element of the array is " + test);
pq.sort();
}
//
public void add(int input) {
insert(input);
}
// Method to insert number into the array.
public void insert(int input) {
array[i] = input;
i++;
}
// Finding smallest number of the array.
public int Minimum() {
int a = array[0];
return a;
}
// Sorts the array from smallest to largest integer
public void sort() {
int first, temp;
for (int i = array.length - 1; i > 0; i--) {
first = 0;
for (int j = 1; j <= 1; j++) {
if (array[j] > array[first])
first = j;
}
temp = array[first];
array[first] = array[i];
array[i] = temp;
}
}
public int remove() {
return delete();
}
public int delete() {
return remove();
}
// Method to convert the array into a string for output
}
The problem in a nutshell:
You start with an array of length 4.
At this point the array contains 4 zeros, that is: [0, 0, 0, 0]
You add 4, 3, and 5. These operations update the content of the array to [4, 3, 5, 0].
You sort the array. This should change the content of the array to [0, 3, 4, 5]. In fact it changes to [0, 5, 3, 4], which means your implementation of sort is clearly broken.
You probably didn't expect the 0 value to move. You can fix this by sorting only the first 3 values. (And, of course, you should also fix your implementation of sort.)
Then when you insert 1, the program updates the value at index 3, so the content changes to [0, 5, 3, 1].
If you implement the fix I suggested above, and sort only the first size elements, then the content after the first call to sort should become [3, 4, 5, 0], and the content after the insert 1 should become [3, 4, 5, 1]. And when you sort that again, the content should become [1, 3, 4, 5] and the smallest value will be 1 as expected, instead of 0.
More concretely:
First of all, change private static int i = 0; to private int size = 0;. The name i is extremely inappropriate here, and will surely confuse you. size is appropriate. It also doesn't make sense to make it static, so I suggest to drop that keyword.
Fix the implementation of sort. There are many basic sorting algorithms that are easy to implement. In the implementation, instead of going until array.size, go until size. Do you see the difference? size is the field in Scrap, essentially it's the number of elements you added using the add or insert methods.
Some cleaning up would be good too:
Delete the add method and rename insert to add.
Delete the remove and delete methods. They are not used, and you will get a stack overflow if you try to use them as they are now (the methods call each other, forever)
Look at the content of the array after each step in the program.
After Scrap pq is created, this is the content of its array:
[0, 0, 0, 0]
Then a couple of modifications:
pq.add(4);
pq.insert(3);
pq.add(5);
The content at this point:
[4, 3, 5, 0]
So far so good.
Then you sort it:
pq.sort();
The content at this point:
[0, 5, 3, 4]
Ouch. The sort implementation doesn't work very well, does it. But let's ignore that for now. Next step:
pq.insert(1);
The content at this point:
[0, 5, 3, 1]
None of this behavior makes sense, probably this is not how you intended the program to work. Review the program, verify the content after each step. Do not proceed to the next step until the current step is working correctly.
I am assuming that you will be using a correct sort method (because, this is not correct, you can use Arrays.sort). But still with a correct sort, there is a logical problem in your code.
At the beginning, the array contains all 0s. After adding the first 3 int, when you call the sort method, the array contains the values in following order:
0,3,4,5
Note that, the value of i is not changed. At this state the value of i is 3. So when you insert 1, the new values become
0,3,4,1
So after sorting again, the values of arrays become
0,1,3,4
So obviously the minimum will retrun 0
I don't think that is the most effective way to sort an array. It is possible to do this with just 1 for loop. Try this to sort your array from smallest to largest.
int temp;
for(int i=0;i<array.length-1;i++){
if(array[i]>array[i+1]){
temp=array[i];
array[i]=array[i+1];
array[i+1]=temp;
i=-1;
}
}
My algorithm to find the maximum number of unique integers among all possible contiguous subarrays doesn't work for larger amounts of Integers and subarrays.
For instance, I have to read a total amount of 6 Integers from the console and each subarray has a size of 3.
So, for this kind of input 5 3 5 2 3 2
my program should print 3 and this works fine.
The first subarray stores 5 3 5 so the number of unique Integers is 2.
The second subarray stores 3 5 2 so the number of unique Integers is 3.
The third subarray would also print 3 because it stores 5 2 3 and so on...
But, it seems like my algorithm can't handle a total amount of 100000 Integers with a subarray size of 99877.
Can anyone explain me, what I have done wrong?
FYI: I have to use a Deque implementation like LinkedList or ArrayDeque
for (int i = 0; i < totalAmountOfIntegers; i++) {
int anyIntegerNumber = consoleInput.nextInt();
arrayDequeToStoreAllIntegers.addLast(anyIntegerNumber);
hashSetToStoreUniqueIntegers.add(anyIntegerNumber);
if (arrayDequeToStoreAllIntegers.size() == sizeOfEachArrayDequeAsSubArray) {
if (hashSetToStoreUniqueIntegers.size() > quantityOfUniqueIntegersInSubarray) {
quantityOfUniqueIntegersInSubarray = hashSetToStoreUniqueIntegers.size();
}
int firstNumberInDeque = arrayDequeToStoreAllIntegers.remove();
if (hashSetToStoreUniqueIntegers.size() == sizeOfEachArrayDequeAsSubArray) {
hashSetToStoreUniqueIntegers.remove(firstNumberInDeque);
}
}
}
The answer would be simply the unique integers in the whole array, since the array is the superset of all subarrays, all numbers would be present in it
Just find how many unique element exist
To be honest, i don't understand your algorithm. I don't really get what the variables are referring to (although they seem to be named in a semantic way).
But what about this:
import java.util.HashSet;
import java.util.Set;
public class UniqueIntegers {
public static void main(String[] args) {
UniqueIntegers ui = new UniqueIntegers();
Integer[][] integers = {
{3,5,3,4,6},
{1,6,3,2,4},
{2,3,4},
{3,3,6,9,2}
};
Set<Integer> unique = ui.uniqueIntegers(integers);
System.out.println("Unique Integers: " + unique.size());
System.out.println("Integers: " + unique);
}
private Set<Integer> uniqueIntegers(Integer[][] ints){
Set<Integer> result = new HashSet<Integer>();
for (Integer[] iSub : ints){
for (Integer i : iSub){
result.add(i);
}
}
return result;
}
}
It prints:
Unique Integers: 7
Integers: [1, 2, 3, 4, 5, 6, 9]
After a day of researching, I have found my mistake.
My third IF-Statement is wrong. I am comparing, if the size of my HashSet variable is equal to the maximum size of elements each subarray can hold.
Instead, I should compare, if my int variable firstNumberInDeque, which I remove first from my ArrayDeque variable, contains another int variable with the same value. So if this is true, my HashSet variable remains unchanged.
But, if my ArrayDeque variable doesn't contain another int with the same value of firstNumberInDeque than firstNumberInDeque should be removed from my HashSet variable.
Here is the right code:
int firstNumberInDeque = arrayDequeToStoreAllIntegers.remove();
if (!arrayDequeToStoreAllIntegers.contains(firstNumberInDeque)) {
hashSetToStoreUniqueIntegers.remove(firstNumberInDeque);
}
I am trying to figure out a solution to calculate the highest sum of numbers in an array. However, my limitation is that I cannot use adjacent values in the array.
If I am given the array int [] blocks = new int[] {15, 3, 6, 17, 2, 1, 20}; the highest sum calculated is 52 (15+17+20).
My goal is to go from a recursive solution to a solution that uses dynamic programming, however, I am having trouble with the recursive solution.
The base cases that I have initialized:
if(array.length == 0)
return 0;
if(array.length == 1)
return array[0];
After creating the base cases, I am unsure of how to continue the recursive process.
I initially tried to say that if the array was of certain length, then I can calculate the max(Math.max) of the calculations:
e.g. if array.length = 3
return Math.max(array[0], array[1], array[2], array[0]+ array[2])
The problem I then run into is that I could be given an array of length 100.
How can I use recursion in this problem?
I think recursive solution to your problem is (in pseudocode):
maxsum(A,0) = 0
maxsum(A,1) = A[0]
maxsum(A,k) = max(maxsum(A,k-2)+A[k-1], maxsum(A,k-1)), for k >= 2
Where maxsum(A,k) means the maximal sum for a subarray of array A starting from 0 and having length k. I'm sure you'll easily translate that into Java, it translates almost literally.
The problem is given an unsorted array, give subsets of array that can produce target sum:
For eg:
target = 15
data = {3,4,5,7,1,2,9};
Expected results (note the results are sorted for simplicity. not a requirement) :
[1, 2, 3, 4, 5]
[1, 2, 3, 9]
[1, 2, 5, 7]
[1, 3, 4, 7]
[1, 5, 9]
[2, 4, 9]
[3, 5, 7]
Here is my naive approach to this problem - simple and brute force.
public static void naiveSubset(int[] arr, int target){
int sum=0;
List<Integer> result = new ArrayList<>();
for (int i=0; i< arr.length;i++){
sum =arr[i];
result.add(arr[i]);
for (int j=0;j<arr.length;i++){
if (sum==target){
System.out.println(result);
result.clear();
break;
}
else if (i!=j && sum+arr[j] <= target){
sum+=arr[j];
result.add(arr[j]);
}
}
}
}
For some reasons, I am not expecting the results. I tried browsing through the code to dig out any issues. But I could not find any. please algo experts, point me in correct direction!!
The results I get (for same input as above)
[3, 3, 3, 3, 3]
[9, 3, 3]
Your solution is wrong because it's a greedy approach. It decides if you should add a number or not based on the fact that adding it does not violate the sum, at the moment.
However, this greedy approach does not work, with a simple example of the following array: [1,9,6,5] and with sum=11.
Note that for any element you choose in the outer loop, next you will add 1 to the current set. But that will deny you the possibility to get the sum of 5+6.
Once you choose 5, you start adding number, starting with '1', and adding it. Once it is added - you will never get the correct solution.
Also note: Your double loop approach can generate at most O(n^2) different subsets, but there could be exponential number of subsets - so something must be wrong.
If you want to get all possible subsets that sum to the given sum, you can use a recursive solution.
At each step "guess" if the current element is in the set or not, and recurse for both options for the smaller problem - if the data is in the set, or if it's not.
Here is a simple java code that does it:
public static void getAllSubsets(int[] elements, int sum) {
getAllSubsets(elements, 0, sum, new Stack<Integer>());
}
private static void getAllSubsets(int[] elements, int i, int sum, Stack<Integer> currentSol) {
//stop clauses:
if (sum == 0 && i == elements.length) System.out.println(currentSol);
//if elements must be positive, you can trim search here if sum became negative
if (i == elements.length) return;
//"guess" the current element in the list:
currentSol.add(elements[i]);
getAllSubsets(elements, i+1, sum-elements[i], currentSol);
//"guess" the current element is not in the list:
currentSol.pop();
getAllSubsets(elements, i+1, sum, currentSol);
}
Note that if you are looking for all subsets, there could be exponential number of those - so an inefficient and exponential time solution is expected.
If you are looking for finding if such a set exist, or finding only one such set, this can be done much more efficiently using Dynamic Programming. This thread explains the logic of how it can be done.
Note that the problem is still NP-Hard, and the "efficient" solution is actually only pseudo-polynomial.
I think the major issue in your previous approach is that simply doing loops based upon the input array will not cover all the combinations of numbers matching the target value. For example, if your major loop is in ith, and after you iterate through the jth element in your secondary loop, your future combination based on what you have collected through ith element will never include jth one anymore. Intuitively speaking, this algorithm will collect all the visible combinations through numbers near each other, but not far away from each other.
I wrote a iterative approach to cope with this subset sum problem through C++ (sorry, not have a java environment at hand:P), the idea is basically the same as the recurrsive approach, which means you would record all the existing number combinations during each iteration in your loop. I have one vector<vector> intermediate used to record all the encountered combination whose value is smaller than target, and vector<vector> final used to record all the combinations whose sum is equal to target.
The detailed explanation is recorded inline:
/* sum the vector elements */
int sum_vec(vector<int> tmp){
int sum = 0;
for(int i = 0; i < tmp.size(); i++)
sum += tmp[i];
return sum;
}
static void naiveSubset(vector<int> arr, int target){
/* sort the array from big to small, easier for us to
* discard combinations bigger than target */
sort(arr.begin(), arr.end(), greater<int>());
int sum=0;
vector<vector<int> > intermediate;
vector<vector<int> > final;
for (int i=0; i< arr.size();i++){
int curr_intermediate_size = intermediate.size();
for(int j = 0; j < curr_intermediate_size; j++){
int tmpsum = sum_vec(intermediate[j]);
/* For each selected array element, loop through all
* the combinations at hand which are smaller than target,
* dup the combination, put it into either intermediate or
* final based on the sum */
vector<int> new_comb(intermediate[j]);
if(tmpsum + arr[i] <= target){
new_comb.push_back(arr[i]);
if(tmpsum + arr[i] == target)
final.push_back(new_comb);
else
intermediate.push_back(new_comb);
}
}
/* finally make the new selected element a separate entry
* and based on its value, to insert it into either intermediate
* or final */
if(arr[i] <= target){
vector<int> tmp;
tmp.push_back(arr[i]);
if(arr[i] == target)
final.push_back(tmp);
else
intermediate.push_back(tmp);
}
}
/* we could print the final here */
}
Just wrote it so please bear with me if there is any corner case that I did not consider well. Hope this helps:)