Time and Space Complexity for Leet Code Problem 39: Combination Sum - java

I was practicing backtracking problems and started off with the Combination Sum problem in Leetcode 39. I worked on my own solution after watching a few YouTube videos to understand the problem better since my solution was failing even though I felt that the logic was right. (Basically I wasn't handling the case to remove redundant solutions, i.e. I had a solution that would get the Permutation of the solution but not the Combination!!
I wrote my code in java, I honestly didn't find many java solutions to this. Python solutions seemed pretty simple though. Maybe it's not the best space and time. I'll try to optimize it and post the update here.
I'm struggling to calculate the time and space complexity for this problem. I'm pretty weak at calculating the time and space usually and with this problem particularly, it's really confusing considering each element can be used multiple times and hence will be part of multiple solutions with multiple counts. I'm trying to calculate space since I use recursion and I'm using the tree method to check the stack size. But if anyone has a good approach to calculate time and space to such problems will be really helpful.
My code below. Any inputs to optimize it will be helpful!
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> mainList = new ArrayList<>();
List<Integer> currList = new ArrayList<>();
compute(0, currList, target, candidates, 0, mainList);
return mainList;
}
void compute(int currSum, List<Integer> currList, int target, int[] candidates, int pos, List<List<Integer>> mainList){
if(currSum > target || pos>(candidates.length-1)){
return;
}
if(currSum == target){
mainList.add(new ArrayList<>(currList));
return;
}
currList.add(candidates[pos]);
currSum = currSum+candidates[pos];
compute(currSum, currList, target, candidates, pos, mainList);
currSum = currSum-(currList.get(currList.size()-1));
currList.remove(currList.size()-1);
compute(currSum, currList, target, candidates, pos+1, mainList);
}
}
I think one way is to sort the candidates array and if sum>target for position i we need not call the function for position i + 1.

Related

Resizing an Array with a Dedicated method for upsize and downsizing

I'm fairly new to coding and am struggling with an assignment for my class. The program takes a user input for the size of an Array and prompts the user to enter each value 1 at a time. The array size starts at 3 and if the array needs to be bigger when the array has filled a new array that's 2x size is created and all info is copied into it. I was able to figure out this part but I just can't see what I'm doing wrong in the downsizing part. After the info is copied I have to remove the trailing zeroes. I think I have the downsize method right but I don't know if I'm calling it right
import java.util.Scanner;
public class Lab6 {
public static void main(String args[]) {
int[] myarray = new int[3];
int count = 0;
int limit, limitcount = 1;
Scanner kbd = new Scanner(System.in);
System.out.print("How many values would you like to enter? ");
limit = kbd.nextInt();
while (limitcount <= limit) {
System.out.println("Enter an integer value ");
int input = kbd.nextInt();
limitcount++;
if (count < myarray.length) {
myarray[count] = input;
}
else {
myarray = upsize(myarray);
myarray[count] = input;
}
count++;
}
myarray = downsize(myarray, count)
printArray(myarray);
System.out.println("The amount of values in the arrays that we care about is: " + count);
}
static int[] upsize(int[] array) {
int[] bigger = new int[array.length * 2];
for (int i =0;i<array.length; i++) {
bigger[i] = array[i];
}
return bigger;
}
static void printArray( int[] array ) {
for ( int number : array ) {
System.out.print( number + " ");
}
System.out.println();
}
static int[] downsize(int[] array,int count) {
int[] smaller = new int[count];
for (int i =0; i<count; i++) {
smaller[i] = array[i];
}
return array;
}
}
Giving you a full response rather than a comment since you're new here and I don't want to discourage you with brevity which could be misunderstood.
Not sure what happened to your code when you pasted it in here, you've provided everything but the format is weird (the 'code' bit is missing out a few lines at the top and bottom). Might be one to double-check before posting. After posting, I see that someone else has already edited your code to fix this one.
You're missing a semi-colon. I'm not a fan of handing out answers, so I'll leave you to find it :) If you're running your code in an IDE, it should already be flagging that one up for you. If you're not, why on earth not??? IntelliJ is free, easy to get going with, and incredibly helpful. There are others out there as well which different folk prefer :) An IDE will help you spot all sorts of useful things quickly.
I have now run your code, and you do have a problem! It's in your final method, downsize(). Look very, very carefully at the return statement ;) Your questions suggests you aren't actually sure whether or not this method is right, which makes me wonder: have you actually run this code with different inputs to see what results you get? Please do that.
Style-wise: blank lines between methods would make the code easier to look at, by providing a visual gap between components. Please be consistent with putting your opening { on the same line as the method signature, and with having spaces between items, e.g. for (int i = 0; i < count; i++) rather than for (int i =0; i<count; i++). The compiler couldn't care less, but it is easier for humans to look at and just makes it look like you did care. Always a good thing!
I think it is awesome that you are separating some of the work into smaller methods. Seriously. For extra brownie points, think about how you could move that while() block into its own method, e.g. private int[] getUserData(int numberOfItems, Scanner scanner). Your code is great without this, but the more you learn to write tiny units, the more favours you will be doing your future self.
Has your class looked at unit testing yet? Trust me, if not, when you get to this you will realise just how important point 5 can be. Unit tests will also help a lot with issues such as the one in point 3 above.
Overall, it looks pretty good to me. Keep going!!!
Simple mistake in your downsize method. If you have an IDE like Eclipse, Intellij, etc. you would have seen it flagged right away.
return array; // should return smaller
I have a few suggestions since you mentioned being new to coding.
The "limitcount" variable can be removed and substituted with "count" at every instance. I'll leave it to you to figure that out.
Try using more descriptive and understandable variable names. Other people will read your code (like now) and appreciate it.
Try to use consistent spacing/indentation throughout your code.
Your upsize method can be simplified using a System.arraycopy() call which generally performs better and avoids the need for writing out a for loop. You can rewrite downsize in a similar manner.
static int[] upsize(int[] array) {
int[] bigger = new int[array.length * 2];
System.arraycopy(array, 0, bigger, 0, array.length);
return bigger;
}
Edit: All good points by sunrise above - especially that you've done well given your experience. You should set up an IDE when you have the time, they're simple to use and invaluable. When you do so you should learn to step through a debugger to explore the state of your program over time. In this case you would have noticed that the myarray variable was never reassigned after the downsize() call, quickly leading you to a solution (if you had missed the warning about an unused "smaller" array).

I'm working on Euler 12 , the code i have seems to workes properly but too slow , very very slow. How can i modify it to run faster?

Like i sad , i am working on Euler problem 12 https://projecteuler.net/problem=12 , i believe that this program will give the correct answer but is too slow , i tried to wait it out but even after 9min it still cant finish it. How can i modify it to run faster ?
package highlydivisibletriangularnumber_ep12;
public class HighlyDivisibleTriangularNumber_EP12 {
public static void findTriangular(int triangularNum){
triangularValue = triangularNum * (triangularNum + 1)/2;
}
static long triangularValue = 0l;
public static void main(String[] args) {
long n = 1l;
int counter = 0;
int i = 1;
while(true){
findTriangular(i);
while(n<=triangularValue){
if(triangularValue%n==0){
counter++;
}
n++;
}
if(counter>500){
break;
}else{
counter = 0;
}
n=1;
i++;
}
System.out.println(triangularValue);
}
}
Just two simple tricks:
When x%n == 0, then also x%m == 0 with m = x/n. This way you need to consider only n <= Math.ceil(sqrt(x)), which is a huge speed up. With each divisor smaller than the square root, you get another one for free. Beware of the case of equality. The speed gain is huge.
As your x is a product of two numbers i and i+1, you can generate all its divisors as product of the divisors of i and i+1. What makes it more complicated is the fact that in general, the same product can be created using different factors. Can it happen here? Do you need to generate products or can you just count them? Again, the speed gain is huge.
You could use prime factorization, but I'm sure, these tricks alone are sufficient.
It appears to me that your algorithm is a bit too brute-force, and due to this, will consume an enormous amount of cpu time regardless of how you might rearrange it.
What is needed is an algorithm that implements a formula that calculates at least part of the solution, instead of brute-forcing the whole thing.
If you get stuck, you can use your favorite search engine to find a number of solutions, with varying degrees of efficiency.

Is it better to use arrays or a queue when merging two sorted arrays?

I'm working on a programming practice site that asked to implement a method that merges two sorted arrays. This is my solution:
public static int[] merge(int[] arrLeft, int[] arrRight){
int[] merged = new int[arrRight.length + arrLeft.length];
Queue<Integer> leftQueue = new LinkedList<>();
Queue<Integer> rightQueue = new LinkedList<>();
for(int i = 0; i < arrLeft.length ; i ++){
leftQueue.add(arrLeft[i]);
}
for(int i = 0; i < arrRight.length; i ++){
rightQueue.add(arrRight[i]);
}
int index = 0;
while (!leftQueue.isEmpty() || !rightQueue.isEmpty()){
int largerLeft = leftQueue.isEmpty() ? Integer.MAX_VALUE : leftQueue.peek();
int largerRight = rightQueue.isEmpty() ? Integer.MAX_VALUE : rightQueue.peek();
if(largerLeft > largerRight){
merged[index] = largerRight;
rightQueue.poll();
} else{
merged[index] = largerLeft;
leftQueue.poll();
}
index ++;
}
return merged;
}
But this is the official solution:
public static int[] merge(int[] arrLeft, int[] arrRight){
// Grab the lengths of the left and right arrays
int lenLeft = arrLeft.length;
int lenRight = arrRight.length;
// Create a new output array with the size = sum of the lengths of left and right
// arrays
int[] arrMerged = new int[lenLeft+lenRight];
// Maintain 3 indices, one for the left array, one for the right and one for
// the merged array
int indLeft = 0, indRight = 0, indMerged = 0;
// While neither array is empty, run a while loop to merge
// the smaller of the two elements, starting at the leftmost position of
// both arrays
while(indLeft < lenLeft && indRight < lenRight){
if(arrLeft[indLeft] < arrRight[indRight])
arrMerged[indMerged++] = arrLeft[indLeft++];
else
arrMerged[indMerged++] = arrRight[indRight++];
}
// Another while loop for when the left array still has elements left
while(indLeft < lenLeft){
arrMerged[indMerged++] = arrLeft[indLeft++];
}
// Another while loop for when the right array still has elements left
while(indRight < lenRight){
arrMerged[indMerged++] = arrRight[indRight++];
}
return arrMerged;
}
Apparently, all the other solutions by users on the site did not make use of a queue as well. I'm wondering if using a Queue is less efficient? Could I be penalized for using a queue in an interview for example?
As the question already states that the left and right input arrays are sorted, this gives you a hint that you should be able to solve the problem without requiring a data structure other than an array for the output.
In a real interview, it is likely that the interviewer will ask you to talk through your thought process while you are coding the solution. They may state that they want the solution implemented with certain constraints. It is very important to make sure that the problem is well defined before you start your coding. Ask as many questions as you can think of to constrain the problem as much as possible before starting.
When you are done implementing your solution, you could mention the time and space complexity of your implementation and suggest an alternative, more efficient solution.
For example, when describing your implementation you could talk about the following:
There is overhead when creating the queues
The big O notation / time and space complexity of your solution
You are unnecessarily iterating over every element of the left and right input array to create the queues before you do any merging
etc...
These types of interview questions are common when applying for positions at companies like Google, Microsoft, Amazon, and some tech startups. To prepare for such questions, I recommend you work through problems in books such as Cracking the Coding Interview. The book covers how to approach such problems, and the interview process for these kinds of companies.
Sorry to say but your solution with queues is horrible.
You are copying all elements to auxiliary dynamic data structures (which can be highly costly because of memory allocations), then back to the destination array.
A big "disadvantage" of merging is that it requires twice the storage space as it cannot be done in-place (or at least no the straightforward way). But you are spoiling things to a much larger extent by adding extra copies and overhead, unnecessarily.
The true solution is to copy directly from source to destination, leading to simpler and much more efficient code.
Also note that using a sentinel value (Integer.MAX_VALUE) when one of the queues is exhausted is a false good idea because it adds extra comparisons when you know the outcome in advance. It is much better to split in three loops as in the reference code.
Lastly, your solution can fail when the data happens to contain Integer.MAX_VALUE.

Recursive Quicksort for a LinkedList

I've been trying to implement a Recursive Quicksort into my algorithm which makes use of LinkedList. However when I run the method, it seems to go on forever even for a small list (of 10 elements), I've been waiting for the method to stop for about 10 minutes.
This is the code in question
public static void QuickSort(LinkedList<Contacto> Lista, int ini, int fin){
Contacto pivote, aux;
int i, j, comp;
if (ini<fin){
pivote = Lista.get(ini);
i=ini+1;
j=fin;
while (i<j){
while(i<fin && (comp=Lista.get(i).get_name().compareTo(pivote.get_name()))<=0 )
i++;
while((comp=Lista.get(i).get_name().compareTo(pivote.get_name()))>0 )
j--;
if(i<j){
aux = Lista.get(i);
Lista.set(i, Lista.get(j));
Lista.set(j, aux);
}
}
aux=Lista.get(j);
Lista.set(j,pivote);
Lista.set(ini,aux);
QuickSort(Lista,ini,j-1);
QuickSort(Lista,j+1,fin);
}
}
Thanks for your help!
As is noted in the comments, the fact that it's taking ten minutes to sort a list of ten items is due to a bug somewhere, and I recommend you insert some breakpoints/println() statements to get a sense for how your method is proceeding (one at the top of each of your conditionals should be sufficient to show you where it's getting hung up).
That said, inefficiency with very short lists is a known problem with the quicksort algorithm - and, if you think about how the algorithm works, you can see why it's an inherent one. (I can expound on this, but you'll be better off if you figure out why yourself).
A common solution to this issue is having a cutoff: When the size of the list becomes smaller than the cutoff size, switch to a simple insertion sort to finish the job. There is a good discussion about this here, and a very good example implementation here.
Note the first few lines of the quicksort() method in that example:
private static void quicksort(Comparable [] a, int left, int right) {
final int CUTOFF = 3;
if (right-left+1 < CUTOFF) { // if less than three elements remain:
insertionSort(a,left,right);
else { //quicksort...
(note: that first link comes from the booksite for Algorithms, 4th edition, written by some folks at Princeton. I can't recommend that site highly enough as you're reasoning through the fundamental algorithms)

Stackoverflow error while implementing insertion sort using recursion

public static void insertionSortRecursion(String a[], int first, int last) {
if (first < last) {
//sort all but last
insertionSortRecursion(a, first, last - 1);
insertInOrder(a[last], a, first, last -1);
}
}
private static void insertInOrder(String element, String[] a, int first, int last) {
// System.out.println(last - 1);
// System.out.println(element);
// System.out.println(a[last]);
if (element.compareTo(a[last]) >= 0) {
a[last + 1] = element;
} else if(first < last) {
a[last + 1] = a[last];
insertInOrder(element, a, first, last - 1);
} else {
a[last + 1] = a[last];
a[last] = element;
}
}
Hey Guys,
I am trying to implement insertion sort using recursion it's working fine on small number of words but I am getting stackoverflow after implementing it because the size of file I am sorting have a lot of words around 10,000. Please suggest what should I do to remove the error.
These are the methods I am using for insertion sort using recursion and I am calling them in my constructor.
Assuming your algorithm is correct, leave this function as it is. Don't try to fix it. In general to get rid of stack overflow (while keeping recursion) there are two solutions :
Use tail call optimization
Increase the stack size
But let sit back and assume this code is going to be anything else than a programming exercise. Any other person who have to read will think :
Why does he use insertion sort ?
Why does he re-implement insertion-sort?
Did it have to be recursion?? my lord!!
Why did he waste his time to find a tail-call insertion algorithm? Or
Did he just increased the stack size for the only sake of running his method?
Good. Now we have 1000,000 items to sort and the program keep crashing.
Conclusion, they will erase your code at once and use Collections.sort().
As I said, if you are doing a programming exercise then good, your recursive insertion
sort work till some point. move on.
Java is not able to handle a recursion depth that deep (10000) by default. Consider the below oversimplified example still throws a StackOverflowError.
static void test(int i)
{
if (i == 0) return;
test(i-1);
}
public static void main(String[] args)
{
test(10000);
}
You must specify command-line parameters to achieve this (-Xss and possibly -Xmx to allocate more memory).
I ran your algorithm successfully for an array of size 100000 using -Xmx1000m -Xss10000000 (though it took a while).
I assume there a reason you're using recursion and not a simple double for loop.

Categories