The classical Two Sum problem is described in LeetCode.
I know how to solve it with a hash table, which results in O(n) extra space. Now I want to solve it with O(1) space, so I'll first sort the array and then use two pointers to find the two integers, as shown in the (incorrect) code below.
public int[] twoSum(int[] numbers, int target) {
java.util.Arrays.sort(numbers);
int start = 0, end = numbers.length - 1;
while(start < end) {
if(numbers[start] + numbers[end] < target) {
start++;
}
else if(numbers[start] + numbers[end] > target) {
end--;
}
else {
int[] result = {start + 1, end + 1};
return result;
}
}
return null;
}
This code is incorrect: I'm returning the indices after sorting. So how will I keep track of the original indices of the selected integers? Or are there other O(1) space solutions? Thank you.
If you are only worried about space complexity, and not the time complexity, then you don't need to sort. That way, the whole issue of keeping track of original indices goes away.
int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length-1; i++) {
for (int j = i+1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] == target)
return new int[]{i+1, j+1};
}
}
return null;
}
If you want to return all such pairs, not just the first one, then just continue with the iterations instead of returning immediately (of course, the return type will have to change to a list or 2-d array or ... ).
There are certain limits what can be achieved and what can't be. There are some parameters that depend on each other. Time & Space complexities are two such parameters when it comes to algorithms.
If you want to optimize your problem for space, it will increase the time complexity in most of the cases except in some special circumstances.
In this problem, if you don't want to increase the space complexity and want to preserve the original indices, the only way to do it is to not sort the array and take every two numbers combinations from the array and check if their sum is your target. This means the code becomes something similar to below.
while(i < n)
{
while(j < n)
{
if(i!=j && arr[i]+arr[j]==target)
{
int[] result = {i, j};
return result;
}
j++;
}
i++;
}
As you can see this obviously is an O(n^2) algorithm. Even in the program you have written the sorting will be something like O(nlogn).
So, the bottom line is if you want to reduce space complexity, it increases time complexity.
Related
I solved a variation of the knapsack problem by backtracking all of the possible solutions. Basically 0 means that item is not in the backpack, 1 means that the item is in the backpack. Cost is the value of all items in the backpack, we are trying to achieve the lowest value possible while having items of every "class". Each time that a combination of all classes is found, I calculate the value of all items and if it's lower than globalBestValue, I save the value. I do this is verify().
Now I'm trying to optimize my recursive backtrack. My idea was to iterate over my array as it's being generated and return the generator if the "cost" of my generated numbers is already higher then my current best-value, therefore the combination currently being generated can't be the new best-value and can be skipped.
However with my optimization, my backtrack is not generating all the values and it actually skips the "best" value I'm trying to find. Could you tell me where the problem is?
private int globalBestValue = Integer.MAX_VALUE;
private int[] arr;
public KnapSack(int numberOfItems) {
arr = new int[numberOfItems];
}
private void generate(int fromIndex) {
int currentCost = 0; // my optimisation starts here
for (int i = 0; i < arr.length; i++) {
if (currentCost > globalBestValue) {
return;
}
if (arr[i] == 1) {
currentCost += allCosts.get(i);
}
} // ends here
if (fromIndex == arr.length) {
verify();
return;
}
for (int i = 0; i <= 1; i++) {
arr[fromIndex] = i;
generate(fromIndex + 1);
}
}
public void verify() {
// skipped the code verifying the arr if it's correct, it's long and not relevant
if (isCorrect == true && currentValue < globalBestValue) {
globalBestValue = currentValue;
}else{
return;
}
}
Pardon my bluntness, but your efforts at optimizing an inefficient algorithm can only be described as polishing the turd. You will not solve a knapsack problem of any decent size by brute force, and early return isn't enough. I have mentioned one approach to writing an efficient program on CodeReview SE; it requires a considerable effort, but you gotta do what you gotta do.
Having said that, I'd recommend you write the arr to console in order to troubleshoot the sequence. It looks like when you go back to the index i-1, the element at i remains set to 1, and you estimate the upper bound instead of the lower one. The following change might work: replace your code
for (int i = 0; i <= 1; i++) {
arr[fromIndex] = i;
generate(fromIndex + 1);
}
with
arr[fromIndex] = 1;
generate(fromIndex + 1);
arr[fromIndex] = 0;
generate(fromIndex + 1);
This turns it into a sort of greedy algorithm: instead of starting with 0000000, you effectively start with 1111111. And obviously, when you store the globalBestValue, you should store the actual data which gives it. But the main advice is: when your algorithm behaves weirdly, tracing is your friend.
I am new to programming and trying to learn by exploring. I was looking for a solution to find sum of maximum time repeating integer in an array with best space complexity. Suppose we have [1, 2, 3, 3] the result should be 6 with least space complexity, say O(n).
I came up with a solution but not sure about the complexity. Need some help to understand if below mentioned code has least complexity or it could be better(definitely!). Sorry if I made any mistake and thanks in advance.
public static int maxDuplicateSumSpaceBased(int[] a)
{
int maxRepCount = 1, tempCount;
int maxRepNum = a[0];
int temp = 0;
for (int i = 0; i < (a.length - 1); i++)
{
temp = a[i];
tempCount = 0;
for (int j = 1; j < a.length; j++)
{
if (temp == a[j])
tempCount++;
}
if (tempCount > maxRepCount)
{
maxRepNum = temp;
maxRepCount = tempCount;
}
}
return maxRepNum * maxRepCount;
}
Actually the space of the input is usually not counted in the O notation so your program has a spatial complexity of O(6)=O(c)=O(1). c is a constant. In fact you always use 6 variables. If the amount of space used is dependent on the input given the situation is different but it's not your case because regardless of the length of you input you use always 6 variables.
If you want to count the input as occupied space (sometimes it's done) your space complexity would be O(6+n)=O(n) assuming that n is the length of the input.
It's impossible to do better as you can easly prove:
You can't have less memory occupied than the input (or you must memorize all the input). Since the input is the only thing that's not a constant you have that the maximum space used is the one needed to store the input that is n.
The space complexity1 of your solution is O(1). You can't get better than that.
The time complexity of your solution is O(N^2). You can improve on that in a couple of ways:
If you can modify a, then you can sort it { time O(NlogN), spaceO(1) } then find / count the most frequent value { O(N) , O(1) }. Overall complexity is { O(NlogN), O(1)}.
If you cannot modify a, then copy it { O(N) / O(N) } and then proceed as above. Overall complexity is { O(NlogN), O(N) }.
If the range of the numbers (M) is less than the number of numbers, then you can use a bucket sort. Overall complexity is { O(N), O(M) }.
You can get better time complexity overall using a HashMap. The overall complexity of that will be { O(N) on average, O(N)} ... with significantly larger constants of proportionality. (Unfortunately, the worst case time complexity will be O(NlogN) or O(N^2) depending on the hash map implementation. It occurs when all of the keys collide. That is impossible for Integer keys and HashMap, but possible for Long keys.)
1 - I am referring to space in addition to the space occupied by the input array. Obviously, the space used for the input array cannot be optimized. It is a given.
I have understand your problem.. Now there could be a solution there are n integers and all integers k [1-n]. Then to find maxrepeatnumber takes O(n) time.
public static int maxDuplicateSumSpaceBased(int[] a)
{
int maxRepCount = 1, tempCount;
int k=a.length();
for (int i = 0; i <k; i++)
{
a[a[i]%k]+=k;
}
int maxRepnumber=0,temp=a[0];
for (int j = 1; j < k; j++)
{
if (temp < a[j])
{
temp=a[j];
maxRepnumber=j;
}
}
}
return maxRepNum;
}
Then you sum all that number and it take O(n)and O(1) space.
For example, if you were given {1,2} as the small array and {1,2,3,4,1,2,1,3} as the big one, then it would return 2.
This is probably horribly incorrect:
public static int timesOccur(int[] small, int big[]) {
int sum= 0;
for (int i=0; i<small.length; i++){
int currentSum = 0;
for (int j=0; j<big.length; j++){
if (small[i] == big[j]){
currentSum ++;
}
sum= currentSum ;
}
}
return sum;
}
As #AndyTurner mentioned, your task can be reduced to the set of well-known string matching algorithms.
As I can understand you want solution faster than O(n * m).
There are two main approaches. First involves preprocessing text (long array), second involves preprocessing search pattern (small array).
Preprocessing text. By this I mean creating suffix array or LCP from your longer array. Having this data structure constructed you can perform a binary search to find your your substring. The most efficient time you can achieve is O(n) to build LCP and O(m + log n) to perform the search. So overall time is O(n + m).
Preprocessing pattern. This means construction DFA from the pattern. Having DFA constructed it takes one traversal of the string (long array) to find all occurrences of substring (linear time). The hardest part here is to construct the DFA. Knuth-Morris-Pratt does this in O(m) time, so overall algorithm running time will be O(m + n). Actually KMP algorithm is most probably the best available solution for this task in terms of efficiency and implementation complexity. Check #JuanLopes's answer for concrete implementation.
Also you can consider optimized bruteforce, for example Boyer-Moore, it is good for practical cases, but it has O(n * m) running time in worst case.
UPD:
In case you don't need fast approaches, I corrected your code from description:
public static int timesOccur(int[] small, int big[]) {
int sum = 0;
for (int i = 0; i < big.length - small.length + 1; i++) {
int j = 0;
while (j < small.length && small[j] == big[i + j]) {
j++;
}
if (j == small.length) {
sum++;
}
}
return sum;
}
Pay attention on the inner while loop. It stops as soon as elements don't match. It's important optimization, as it makes running time almost linear for best cases.
upd2: inner loop explanation.
The purpose of inner loop is to find out if smaller array matches bigger array starting from position i. To perform that check index j is iterated from 0 to length of smaller array, comparing the element j of the smaller array with the corresponding element i + j of the bigger array. Loop proceeds when both conditions are true at the same time: j < small.length and corresponding elements of two arrays match.
So loop stops in two situations:
j < small.length is false. This means that j==small.length. Also it means that for all j=0..small.length-1 elements of the two arrays matched (otherwise loop would break earlier, see (2) below).
small[j] == big[i + j] is false. This means that match was not found. In this case loop will break before j reaches small.length
After the loop it's sufficient to check whether j==small.length to know which condition made loop to stop and hence know whether match was found or not for current position i.
This is a simple subarray matching problem. In Java you can use Collections.indexOfSublist, but you would have to box all the integers in your array. An option is to implement your own array matching algorithm. There are several options, most string searching algorithms can be adapted to this task.
Here is an optimized version based on the KMP algorithm. In the worst case it will be O(n + m), which is better than the trivial algorithm. But it has the downside of requiring extra space to compute the failure function (F).
public class Main {
public static class KMP {
private final int F[];
private final int[] needle;
public KMP(int[] needle) {
this.needle = needle;
this.F = new int[needle.length + 1];
F[0] = 0;
F[1] = 0;
int i = 1, j = 0;
while (i < needle.length) {
if (needle[i] == needle[j])
F[++i] = ++j;
else if (j == 0)
F[++i] = 0;
else
j = F[j];
}
}
public int countAt(int[] haystack) {
int count = 0;
int i = 0, j = 0;
int n = haystack.length, m = needle.length;
while (i - j <= n - m) {
while (j < m) {
if (needle[j] == haystack[i]) {
i++;
j++;
} else break;
}
if (j == m) count++;
else if (j == 0) i++;
j = F[j];
}
return count;
}
}
public static void main(String[] args) {
System.out.println(new KMP(new int[]{1, 2}).countAt(new int[]{1, 2, 3, 4, 1, 2, 1, 3}));
System.out.println(new KMP(new int[]{1, 1}).countAt(new int[]{1, 1, 1}));
}
}
Rather than posting a solution I'll provide some hints to get your moving.
It's worth breaking the problem down into smaller pieces, in general your algorithm should look like:
for each position in the big array
check if the small array matches that position
if it does, increment your counter
The smaller piece is then checking if the small array matches a given position
first check if there's enough room to fit the smaller array
if not then the arrays don't match
otherwise for each position in the smaller array
check if the values in the arrays match
if not then the arrays don't match
if you get to the end of the smaller array and they have all matched
then the arrays match
Though not thoroughly tested I believe this is a solution to your problem. I would highly recommend using Sprinters pseudocode to try and figure this out yourself before using this.
public static void main(String[] args)
{
int[] smallArray = {1,1};
int[] bigArray = {1,1,1};
int sum = 0;
for(int i = 0; i < bigArray.length; i++)
{
boolean flag = true;
if(bigArray[i] == smallArray[0])
{
for(int x = 0; x < smallArray.length; x++)
{
if(i + x >= bigArray.length)
flag = false;
else if(bigArray[i + x] != smallArray[x])
flag = false;
}
if(flag)
sum += 1;
}
}
System.out.println(sum);
}
}
I have an array with positive integers in random order. A number x
from the list is given ,we need to find any two numbers in the list
having sum equal to x.Running time must be less than n^2.
{edit}
What I did is that , I put all the numbers less than half of x in one array and greater than half of x in another array and all greater than x are discarded and then the idea is that the required two numbers must from the two arrays (not from a single array) and by iterating I can get the two numbers.
Now for the worst case I am little confuse is that approach is good? or if anyone guide me something more better than this also can we achieve log n or n *log n ?
Your solution is both wrong, and in O(n^2).
It is wrong since consider x=5 and arr=[1,2,3,5] - the two numbers needed are from one array, not from both.
What if arr=[3,3,6], x=6, you will place both 3s in one list (not greater than x/2 for example), and will fail to find 3+3=6.
Your algorithm runs in O(n^2), because assume exactly half of the elements are greater than x1, and half are smaller than x. Then, the number of combinations you have to check are (n/2*n/2) /2 = n^2/8
To solve it in O(nlogn), think what happens if you sort the data, given a number arr[i], can you find efficiently if there is a number x-arr[i] in the now sorted array?
You can even enhance the above to O(n) average case by placing the elements in a hash-set, and now, given an number y, can you find efficiently if x-y is also in the set?
EDIT:
Stroked out parts are not relevant anymore since OP editted the question, added a new cause of concern instead.
(1) than x/2 in the editted question.
Here is O(n) solution for finding the first pair of indices of array which sums to the expected target. The solution will stop when it finds the first 2 indices that sum to target, if you need all the pairs that add up to target then instead of using int[] result, you can use ArrayList or even a Map, process the complete array and return it with all the pairs of indices. There is an obvious assumption that the Map's hashcode function is really good and there are not much collisions so that map operations perform in O(1) time.
import java.util.*;
public class Solution {
public static void main(String[] args) {
int[] array = new int[] {1,2,4,7,12,67,12,5,9,1,10};
System.out.println(Arrays.toString(sum(array, 68)));
}
public static int[] sum(int[] array, int target) {
int[] result = new int[2];
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// n iterations
for (int index = 0; index < array.length; index++) {
// constant
if (map.containsKey(target - array[index])) {
result[1] = index;
// constant
result[0] = map.get(target - array[index]);
return result;
}
// constant
map.put(array[index], index);
}
return result;
}
}
Here you go,
Sort the array using merge sort (Time complexity: n logn). Take two pointers/counters, say i & j, one starts from index 0 and another from n-1 (assuming n size of array is n).
if array[i]+array[j]=sum
return;
else if (array[i]+array[j]<sum) i++;
else j--;
Do it until i>j.
Overall time complexity: n logn
/* Time Complexity = O(n)-since HashMap operations take O(1) time*/
public static ArrayList<Integer> twoSum(int[] arr , int target){
if (arr == null){
throw new IllegalArgumentException();
}
ArrayList<Integer> targetHolder = new ArrayList<>();
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0 ; i < arr.length ; i++){
if (map.containsKey(arr[i])){
int index = map.get(arr[i]);
targetHolder.add(index+1);
targetHolder.add(i+1);
}
else{
map.put(target-arr[i], i);
}
}
return targetHolder;
}
public static void main(String[] args) {
int[] A = {1,2,3,4,5,6};
System.out.println(twoSum(A, 6));
}
}
public void function(int[] array, int sum){
for(int i = 0; i < array.length/2; i ++){
for(int j = array.length-1;; j--){
if(array[i]+array[j] < sum) break;
if(array[i]+array[j] == sum) System.out.println(array[i]+" + "+array[j]+" = "+sum);
}
}
}
I have an array int[] a= {5,3,1,2} and I want to make a method that picks out the "k" smallest numbers and return an array with the k smallest integers in ascending order. But when I run this code I get the output: [1,3].
I know the code skips some numbers somehow, but I cant twist my brain to fix it.
Any ideas?
EDIT: Without sorting the original array.
public static int[] nrSmallest(int[] a, int k) {
if(k <1 || k>a.length)
throw new IllegalArgumentException("must be at least 1");
int[] values= Arrays.copyOf(a, k);
Arrays.sort(values);
int counter= 0;
for(int i= k; i < a.length; i++) {
if(a[i]< values[counter]) {
for(int j= k-1; j> counter; j--) {
values[j]= values[j-1];
}
values[counter]= a[i];
}
if(counter< k) counter++;
}
return values;
}
EDIT: Joop Eggen solved this for me. Scroll down to see answer. Thanks!
As already pointed out in the comments, simply return a part of the sorted array.
public static int[] nrSmallest(int[] a, int k) {
// check parameters..
// copy all so we don't sort a
int[] sorted = Arrays.copyOf(a, a.length);
Arrays.sort(sorted);
return Arrays.copyOf(sorted, Math.min(k, sorted.length));
}
If you can't modify the original array, this is typically done with some type of priority queue, often a binary heap.
The method that you use in your example is O(n^2), and uses O(k) extra space. Sorting the original array and selecting the top k items is O(n log n). If you copy the array and then sort it, it uses O(n) extra space.
Using a heap is O(n log k), and requires O(k) extra space.
There is an O(n) solution that involves manipulating the original array (or making a copy of the array and manipulating it). See Quickselect.
My own testing shows that Quickselect is faster in the general case, but Heap select is faster when the number of items to be selected (k) is less than 1% of the total items (n). See my blog post, When theory meets practice. That comes in quite handy when selecting, say, the top 100 items from a list of two million.
(Corrected) To keep your code:
for (int i= k; i < a.length; i++) {
if (a[i] < values[counter]) { // Found small value
// Insert sorted
for (int j = k-1; j >= 0; j--) {
if (j == 0 || a[i] > values[j-1]) { // Insert pos
// Move greater ones up.
for (int m = k - 1; m > j; m--) {
values[m] = values[m - 1];
}
values[j] = a[i]; // Store
break; // Done
}
}
}
}
int[] values= Arrays.copyOf(a, k); this line is wrong. you are copying only k elements. but you are suppose to copy all elements and then sort the array.
First sort the array and then return the sorted part of the array upto k.
public static int[] nrSmallest(int[] a, int k) {
if(k <1 || k>a.length)
throw new IllegalArgumentException("must be at least 1");
Arrays.sort(a);
return Arrays.copyOf(a,k);
}
You could use the "pivoting" idea of quicksort,
The pivot denotes the "rank" of that number in the array, so your end goal would be having a pivot at index "k", which will result in a subarray less than the Kth element, in other words first K smallest numbers (not exactly sorted).