quicksort algorithm in python initial condition - python

In the following implementation of the quicksort algorithm in Python:
def quicksort(listT):
greater=[]
lower=[]
pivot=[]
if len(listT)<=1:
return listT
else:
pivot=listT[0]
for i in listT:
if i<pivot:
lower.append(i)
elif i>pivot:
greater.append(i)
else:
pivot.append(i)
lower=quicksort(lower)
greater=quicksort(greater)
return lower+pivot+greater
I was wondering what exactly the first condition does in this implementation, for what I see when it divides each part of the list into a greater and lower part, according to the pivot, there would be a moment in which the list has a length lower than 1, but this returned list is not concatenated in any way. Could this condition be changed?

The len(listT)<=1 is needed to terminate the recursion. Quicksort works by dividing the problem into more easily solved subproblems. When the subproblem is an empty list or list of length one, it is already solved (no sorting needed) so the result can be returned directly.

If the initial condition is not stated, then the sort will fail at either
pivot=listT[0] # because the list may be empty and it will reference an invalid index, or
lower=quicksort(lower) # actually it will never end because the stack keeps building on this line.

Related

When finding all subsets of an array, result array returns empty subsets, any idea what's going on here?

I am currently trying to store all possible subsets of an array with recursion. When storing subset in my result array, I am getting an empty nested array. However, when I print subset within the base case, it appears. Anyone have an idea why subset is not appending to result.
Here's my code below:
def see_sequences(array):
subset = list()
result = list()
hel(array, subset, 0, result)
return result
def hel(arr, subset, i,result):
if i == len(arr):
result.append(subset)
else:
subset.append(None)
hel(arr, subset,i+1, result)
subset.pop()
subset.append(arr[i])
hel(arr,subset,i+1, result)
subset.pop()
This is result
[[], [], [], [], [], [], [], [], []]
In main, I am running see_sequences([1,2,3]).
Thanks for the help!
You might be interested to know why all of your lists are empty.
subset.append(None)
hel(arr, subset,i+1, result)
subset.pop()
For each append there is a matching pop. The pop occurs after the recursive call to hel(), where subset gets used like this:
if i == len(arr):
result.append(subset)
Each time, you are appending exactly the same list to result. The number of elements grows, but all the elements are exactly the same list. They are not copies of the same list, let alone copies of the current state of the list - they are all literally the same object.
When the recursion finishes and the recursive calls unwind, the pop method removes every appended element. At the end of the process the list is empty. When your print statement executes, you are printing the same empty list 9 times.
I would also like to point out that your algorithm is not a real recursion at all, even though your function is making calls to itself. You have actually written a simple for loop in a way that makes it look like recursion. You initialize a loop index here:
hel(array, subset, 0, result)
because you set the third argument to 0. You increment the loop index here:
hel(arr, subset,i+1, result)
when you add 1 to i. You terminate the loop here:
if i == len(arr):
result.append(subset)
by checking to see if you've reached the end of the list.
You pass all the variables you're working on down the stack of recursive calls. What you have done is to hide the basic loop structure inside of all that code.
I don't know why you're trying to use recursion on this particular problem. As far as I can see, recursion can't help you here. If you're doing this as an exercise to learn a programming technique, this seems a poor choice.
If you really need this strange algorithm for a project you're working on, try doing it without recursion (ask a different question if you need help).

What does array[:] exactly do in this backtracking algorithm, or rather in Python?

So, I was solving(or rather, looking at the solution haha) a question on Leetcode, and this was a solution to allow you to generate all possible permutations of an array with unique integers.
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
length = len(nums)
results = []
def backtrack(first_char_index):
if first_char_index == length:
# print(nums == nums[:])
results.append(nums)
for index in range(first_char_index, length):
nums[first_char_index], nums[index] = nums[index], nums[first_char_index]
backtrack(first_char_index + 1)
nums[first_char_index], nums[index] = nums[index], nums[first_char_index]
backtrack(0)
return results
So, I was testing out the solution, and I realized that this solution only works if inside the if condition inside of the backtrack function, I use results.append(nums[:]) instead of the above results.append(nums).
So I initially figured that this was probably because nums[:] should be used because we need to generate a new copy, but then I had that print statement added before results.append(nums), and found that all of the print statements gave me a True result.
I remember seeing a few solutions with this pattern of having nums[:] instead of nums, and would like to ask if anyone could shed light on what exactly does the extra [:] do? I know that it creates a new copy (i.e different object, but same value), but since it has the same value, why does result in a different result?
To illustrate this, the result with an input of [1, 2, 3] gives
[[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3],[1,2,3]]
when using nums, and
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
(the correct answer), when using nums[:].
Thanks in advance!
EDIT: For some reason, this question is being recognized to be the same as others that are regarding deep/shallow copy. However, I guess what I'm asking here is that since [:] results in a new, different object with the same value, and with the fact that the value of nums and nums[:] is the same (it prints out to be the altered value), shouldn't it be appending an array with the altered value, instead of the original untouched nums array?
nums and nums[:] have indeed the same value (that you check using ==), but the are different objects (that you can check using the 'is' keyword). Sequences are mutable, therefore you can change the values they contain without changing the object itself.
The [:] simply creates a copy of an existing sequence. This way you have a different object with all the values of the previous one
EDIT:
the reason is that, when you append nums to results, nums can still be changed even if it's inside results. Therefore the elements inside results will be changed everytime you change the original nums (in fact in the result all the values are identical). If you create a copy for each element you put in results, instead, the elements will all have different values.

"in list" vs. "manual searching" in list

My code in Python reads a next element in list and checks whether it already appeared in the list before. If yes, then it moves a left bound of the list behind the previous appearance (the rest of the code does not matter):
while k<len(lst):
if lst[k] in lst[a:k]: #a is a left bound
i = lst.index(lst[k],a) #could be done more effeciently with exception handling
a = i+1
k += 1
I tried to rewrite this without using high-level tricks (in/index):
while k<len(lst):
for i in range(a,k+1):
if lst[i] == lst[k]:
break
if i != k: #different indices, same values
a = i+1
k += 1
This appears to be cca 3.5 times slower than the code #1. But I do not think the code #2 does something highly inefficient, if I understand the "in" command correctly.
go through all elements in list
compare to the searched element
if they are equal, stop and return True
if at end of the list, return False
(and the function index probably works in the same way, you only have to remember also the index).
My guess is that the Python interpreter interprets the "in" as a low-level version of the for-cycle in code #2. But in the code #2, it has to interpret my comparison every time I increase the value of i, which makes the code run slowly overall. Am I right about this?
By the way, the list is an ordered list of non-repeating numbers (does not have to be, so no suggestions of binary search), which results in a worst-case complexity for this algorithm, n^2/2.

Trouble with top down recursive algorithm

I am trying to make word chains, but cant get around recursive searching.
I want to return a list of the words reuired to get to the target word
get_words_quicker returns a list of words that can be made by just changing one letter.
def dig(InWord, OutWord, Depth):
if Depth == 0:
return False
else:
d = Depth - 1;
wordC = 0;
wordS = [];
for q in get_words_quicker(InWord):
wordC+=1
if(OutWord == q):
return q
wordS.append(q)
for i in range(0,wordC):
return dig(wordS[i],OutWord,d)
Any help/questions would be much appreciated.
ANALYSIS
There is nowhere in your code that you form a list to return. The one place where you make a list is appending to wordS, but you never return this list, and your recursive call passes only one element (a single word) from that list.
As jasonharper already pointed out, your final loop can iterate once and return whatever the recursion gives it, or it can fall off the end and return None (rather than "nothing").
You have two other returns in the code: one returns False, and the other will return q, but only if q has the same value as OutWord.
Since there is no code where you use the result or alter the return value in any way, the only possibilities for your code's return value are None, False, and OutWord.
REPAIR
I'm afraid that I'm not sure how to fix this routine your way, since you haven't really described how you intended this code to carry out the high-level tasks you describe. The abbreviated variable names hide their purposes. wordC is a counter whose only function is to hold the length of the list returned from get_words_quicker -- which could be done much more easily.
If you can clean up the code, improve the data flow and/or documentation to something that shows only one or two disruptions in logic, perhaps we can fix the remaining problems. As it stands, I hesitate to try -- you'd have my solution, not yours.

alternative to recursion based merge sort logic

here is a merge sort logic in python : (this is the first part, ignore the function merge()) The point in question is converting the recursive logic to a while loop.
Code courtesy: Rosettacode Merge Sort
def merge_sort(m):
if len(m) <= 1:
return m
middle = len(m) / 2
left = m[:middle]
right = m[middle:]
left = merge_sort(left)
right = merge_sort(right)
return list(merge(left, right))
Is it possible to make it a sort of dynamically in the while loop while each left and right array breaks into two, a sort of pointer keeps increasing based on the number of left and right arrays and breaking them until only single length sized list remains?
because every time the next split comes while going on both left- and right- side the array keeps breaking down till only single length list remains, so the number of left sided (left-left,left-right) and right sided (right-left,right-right) breaks will increase till it reaches a list of size 1 for all.
One possible implementation might be this:
def merge_sort(m):
l = [[x] for x in m] # split each element to its own list
while len(l) > 1: # while there's merging to be done
for x in range(len(l) >> 1): # take the first len/2 lists
l[x] = merge(l[x], l.pop()) # and merge with the last len/2 lists
return l[0] if len(l) else []
Stack frames in the recursive version are used to store progressively smaller lists that need to be merged. You correctly identified that at the bottom of the stack, there's a one-element list for each element in whatever you're sorting. So, by starting from a series of one-element lists, we can iteratively build up larger, merged lists until we have a single, sorted list.
Reposted from alternative to recursion based merge sort logic at the request of a reader:
One way to eliminate recursion is to use a queue to manage the outstanding work. For example, using the built-in collections.deque:
from collections import deque
from heapq import merge
def merge_sorted(iterable):
"""Return a list consisting of the sorted elements of 'iterable'."""
queue = deque([i] for i in iterable)
if not queue:
return []
while len(queue) > 1:
queue.append(list(merge(queue.popleft(), queue.popleft())))
return queue[0]
It's said, that every recursive function can be written in a non-recursive manner, so the short answer is: yes, it's possible. The only solution I can think of is to use the stack-based approach. When recursive function invokes itself, it puts some context (its arguments and return address) on the inner stack, which isn't available for you. Basically, what you need to do in order to eliminate recursion is to write your own stack and every time when you would make a recursive call, put the arguments onto this stack.
For more information you can read this article, or refer to the section named 'Eliminating Recursion' in Robert Lafore's "Data Structures and Algorithms in Java" (although all the examples in this book are given in Java, it's pretty easy to grasp the main idea).
Going with Dan's solution above and taking the advice on pop, still I tried eliminating while and other not so pythonic approach. Here is a solution that I have suggested:
PS: l = len
My doubt on Dans solution is what if L.pop() and L[x] are same and a conflict is created, as in the case of an odd range after iterating over half of the length of L?
def merge_sort(m):
L = [[x] for x in m] # split each element to its own list
for x in xrange(l(L)):
if x > 0:
L[x] = merge(L[x-1], L[x])
return L[-1]
This can go on for all academic discussions but I got my answer to an alternative to recursive method.

Categories