Recursive Quicksort for a LinkedList - java

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)

Related

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

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.

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.

Why use recursion if the same task can be accomplished with loop control structures?

As I start learning recursion different questions are crossing my mind. Recursion uses more memory for the stack and it's usually slower due to maintaining the stack.
What is the benefit of using recursion if I still can use for loops? We describe actions to be repeated over and over until a condition is true, and we can use both recursion or for loops.
Why would I choose recursive version while I have faster control structures as an option?
Recursion uses more memory for the stack and it usually slower due to maintaining the stack
That statement is far from being universally true. It applies in situations when you do not need to save the state for more than a fixed number of levels, but that does not cover many important tasks that can be solved recursively. For example, if you want to implement a depth-first search on a graph, you need to make your own data structure to store the state that would otherwise go on the stack.
What is the benefit of using Recursion if I still can use for loop?
You get more clarity when you apply a recursive algorithm to a task that is best understood through recursion, such as processing recursively-defined structures. In cases like that, a loop by itself is no longer sufficient: you need a data structure to go along with your loop.
Why would I choose recursive version while I have faster control structure?
You wouldn't necessarily choose recursion when you could implement the same algorithm with faster control structures that are easy to understand. However, there are situations when you may want to code a recursion to improve readability, even though you know that you can code the algorithm using a loop, with no additional data structures. Modern compilers can detect situations like that, and "rewrite" your recursive code behind the scene to make it use iterations. This lets you have the best of both worlds - a recursive program that matches reader's expectations, and an iterative implementation that does not waste space on the stack.
Unfortunately, demonstrating examples of situations when recursion gives you clear advantages requires knowledge of advanced topics, so many educators take shortcuts by demonstrating recursion using wrong examples, such as factorials and Fibonacci numbers. One relatively simple example is implementing a parser for an expression with parentheses. You can do it in many different ways, with or without recursion, but the recursive way of parsing expressions gives you an amazingly concise solution that is easy to understand.
The great example of when the recursive solution is better than the itterative one is the tower of Hanoi. Consider the following two solutions -
Recursive (from this question):
public class Hanoi {
public static void main(String[] args) {
playHanoi (2,"A","B","C");
}
//move n disks from position "from" to "to" via "other"
private static void playHanoi(int n, String from , String other, String to) {
if (n == 0)
return;
if (n > 0)
playHanoi(n-1, from, to, other);
System.out.printf("Move one disk from pole %s to pole %s \n ", from, to);
playHanoi(n-1, other, from, to);
}
}
Iterative (copied from RIT):
import java.io.*;
import java.lang.*;
public class HanoiIterative{
// -------------------------------------------------------------------------
// All integers needed for program calculations.
public static int n;
public static int numMoves;
public static int second = 0;
public static int third;
public static int pos2;
public static int pos3;
public static int j;
public static int i;
public static void main(String args[]) {
try{
if( args.length == 1 ){
System.out.println();
n = Integer.parseInt(args[0]); //Sets n to commandline int
int[] locations = new int[ n + 2 ]; //Sets location size
for ( j=0; j < n; j++ ){ //For loop - Initially all
locations[j] = 0; //discs are on tower 1
}
locations[ n + 1 ] = 2; //Final disk destination
numMoves = 1;
for ( i = 1; i <= n; i++){ //Calculates minimum steps
numMoves *= 2; //based on disc size then
} //subtracts one. ( standard
numMoves -= 1; //algorithm 2^n - 1 )
//Begins iterative solution. Bound by min number of steps.
for ( i = 1; i <= numMoves; i++ ){
if ( i%2 == 1 ){ //Determines odd or even.
second = locations[1];
locations[1] = ( locations[1] + 1 ) % 3;
System.out.print("Move disc 1 to ");
System.out.println((char)('A'+locations[1]));
}
else { //If number is even.
third = 3 - second - locations[1];
pos2 = n + 1;
for ( j = n + 1; j >=2; j-- ) //Iterative vs Recursive.
if ( locations[j] == second )
pos2 = j;
pos3 = n + 1;
for ( j = n + 1; j >= 2; j-- ) //Iterative vs Recursive.
if ( locations[j] == third )
pos3 = j;
System.out.print("Move disc "); //Assumes something is moving.
//Iterative set. Much slower here than Recursive.
if ( pos2 < pos3 ){
System.out.print( pos2 );
System.out.print(" to ");
System.out.println((char)('A' + third));
locations[pos2] = third;
}
//Iterative set. Much slower here than Recursive.
else {
System.out.print( pos3 );
System.out.print(" to ");
System.out.println((char)('A' + second));
locations[ pos3 ] = second;
}
}
}
}
} //Protects Program Integrity.
catch( Exception e ){
System.err.println("YOU SUCK. ENTER A VALID INT VALUE FOR #");
System.err.println("FORMAT : java HanoiIterative #");
} //Protects Program Integrity.
finally{
System.out.println();
System.out.println("CREATED BY: KEVIN SEITER");
System.out.println();
}
}
}//HanoiIterative
//--------------------------------------------------------------------------------
Im guessing you didnt really read that iterative one. I didnt either. Its much more complicated. You change change some stuff here and there, but ultimately its always going to be complicated and there is no way around it. While any recursive algorithm CAN be converted to iterative form, it is sometimes much more complicated code wise, and sometimes even significantly less efficient.
How would you search a directory full of sub directories that are themselves full of sub directories and so on (like JB Nizet stated, tree nodes) or calculate a Fibonacci sequence with less ease than using recursion?
All algorithms can be translated from recursive to iterative. Worst case scenario you can explicitly use a stack to keep track of your data (as opposed to the call stack). So if efficiency is really paramount and you know recursion is slowing you down significantly, it's always possible to fall back on the iterative version. Note that some languages have compilers that convert tail recursive methods to their iterative counterparts, e.g., Scala.
The advantage of recursive methods is that most of the time they are much easier to write and understand because they are so intuitive. It is good practice to understand and write recursive programs since many algorithms can be naturally expressed that way. Recursion is just a tool to write expressive and correct programs. And again, once you know your recursive code is correct, it's easier to convert it to its iterative counterpart.
Recursion is usually more elegant and intuitive than other methods. But it's not the universal solution to everything.
Take the Fibonacci sequence for example. You can find the nth term by recursively using the definition of fibonacci number (and the base case with n == 1). But you'll find yourself calculating the mth term ( m < n - 2 ) more than once .
Use instead an array [1, 1] and append the next ith term as the sum of a[i-1] + a[i-2]. You'll find a linear algorithm a lot faster than the other.
BUT you'll love recursion.
It's elegant and often powerful.
Imagine you want to traverse a tree to print something in order. It could be something like:
public void print (){
if( this == null )
return;
left.print();
System.out.println(value);
right.print();
}
To make this with a while loop you need to do your own backtracking stack because it has calls which are not in tail position (though one call is). It won't be as easy to understand as this though and IMO technically it would still be recursion since goto+stack is recursion.
If your trees are not too deep you won't blow the stack and the program works. There is no need to do premature optimizations. I even would have increased the JVM's stack before changing this to do it's own stack.
Now in a future version of the runtime even JVM can get tail call optimization just like proper runtimes should have. Then all recursions in tail positions won't grow the stack and then it's no difference from other control structures so you choose which has the most clear syntax.
My understanding is that standard iterative loops are more applicable when your data set is small, has minimal edge cases, and for which the logical conditions are simple for determining how many times to iterate one or more dedicated functions.
More importantly, recursive functions are more useful when applied to more complex nested data structures, in which you may not be able to intuitively or accurately estimate how many times you need to loop, because the amount of times you need dedicated functions to reiterate is based on a handful of conditions, some of which may not be mutually exclusive, and for which you care to specifically deliberate the order of the call stack and an intuitive path to a base case, for ease of readability and debugging***.
Recursion is recommended for prototype programming for non programmers or junior programmers. For more serious programming you should avoid recursion as much as you can. Please read
NASA coding standard

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.

What are the correct Big-O values in this program?

I am trying to test what I know about BigO not very confident not totally illiterate either but please guide.
This is not a home work , I am not a student any where but interested in understanding this and various other related concepts.
//What is bigO of this Application ?
public class SimpleBigOTest {
// What is BigO of this method -> I am certain it is O(n) but just checking
private void useItirativeApprachToPrintNto0(int n) {
for (int i = 0; i < n; i++) {
System.out.println("useItirativeApprachToPrintNto0: " + i);
}
}
// What is BigO of this method -> I am reasonabily certain it is O(n)
private void useRecurrsiveApprachToPrintNto0(int n) {
if (n != 0) {
System.out.println("useRecurrsiveApprachToPrintNto0: " + n);
useRecurrsiveApprachToPrintNto0(n - 1);
}
}
// What is BigO of this method -> I think now it is O(n^2)
private void mutltipleLinearItirationsDependentOnValueOfN(int n) {
int localCounter = n + n;
for (int i = 0; i < localCounter; i++) {
System.out.println("mutltipleLinearItirationsDependentOnValueOfN: "
+ i);
}
for (int i = 0; i < n; i++) {
System.out.println("mutltipleLinearItirationsDependentOnValueOfN: "
+ i);
}
}
// What is BigO of this method -> I think this is again O(n)
private void mutltipleLinearItirationsNotDependentOnValueOfN(int n, int j) {
int localCounter = j;
for (int i = 0; i < localCounter; i++) {
System.out
.println("mutltipleLinearItirationsNotDependentOnValueOfN: "
+ i);
}
for (int i = 0; i < n; i++) {
System.out
.println("mutltipleLinearItirationsNotDependentOnValueOfN: "
+ i);
}
}
// What is bigO of this main -> I would say O(n^2) because
// mutltipleLinearItirationsDependentOnValueOfN has biggest BigO of O(n^2)
// if I am correct
public static void main(String[] args) {
SimpleBigOTest test = new SimpleBigOTest();
int n = 1000;
int j = 1234;
test.useItirativeApprachToPrintNto0(n);
test.useRecurrsiveApprachToPrintNto0(n);
test.mutltipleLinearItirationsDependentOnValueOfN(n);
test.mutltipleLinearItirationsNotDependentOnValueOfN(n, j);
}
}
As a side question why do all the books on Algorithms speak so highly of Recursion where as in my practical experience I have always used iteration. Using Recursion we can run out memory quickly and nightmare to debug.
Your answers to the first two are correct.
Your answer to the third function is incorrect; this is also O(N). The reason is that the first loop iterates 2N times, and the second loop iterates N times. This is a total of 3N iterations, and 3N = O(N) because big-O ignores constant factors.
Your answer to the fourth function is also incorrect; this is O(N + J). It is possible to have a function's runtime dependent on multiple parameters, and that is the case here. Greatly increasing N or J will cause the runtime to depend on that parameter more than the other. Many important algorithms like Dijkstra's algorithm, the KMP string matching algorithm, etc. have runtimes that depend on multiple parameters. Some algorithms have runtimes that depend on the value they produce (these are sometimes called output-sensitive algorithms). It's good to keep this in mind when analyzing or designing algorithms.
Finally, the complexity of main is O(1) because you are calling all four functions with fixed values for the arguments. Since the program always does exactly the same amount of work (some constant). If you allow n and j to vary with the command-line arguments, then the runtime would be O(n + j), but since they're fixed the complexity is O(1).
As a final note, I'd suggest not dismissing recursion so quickly. Recursion is an extremely useful technique for designing algorithms, and many recursive algorithms (quicksort, mergesort, etc.) use little stack space and are quite practical. Thinking recursively often helps you design iterative algorithms by allowing you to think about the structure of the problem in a different way. Plus, many major data structures (linked lists, trees, tries, etc.) are defined recursively, and understanding their recursive structure will help you write algorithms that operate over them. Trust me, it's a good skill to have! :-)
Hope this helps!
Regarding the complexity scores #templatetypedef has already provided the correct once.
Now for your question regarding recursion vs loops.
Many problems in real world have recursive behavior and they can be best designed using this property. For example Tower of Hanoi, recursion provides a very simple solution whereas if you make use of some iterative approach then it can become quite complex :(
Lastly recursion does have some additional parameters overheads. If you need to have extremely optimized behavior then you have to decide amongst them.
Finally, remember that programmer time is more expensive than CPU time. Before you micro-optimize your code, it really is a good idea to measure to see if it really will be an issue.

Categories