Python quicksort - one list - swaps - python

**I need to make a quicksort algorithm but so that it uses only one list and does swaps inside of it. I managed to make it "sort" or position the first element but now i don't know how to implement the recursion. The biggest problem I'm having is how to recursively work on a part of the list instead of making the new one. Here is my code:
-------------------------------------------------------------------------------**
New code, same problem.
Here is my code. It does the job but gets stuck in the loop.
def qsort(n,l,g):
if l is None:
l=0
if g is None:
g=len(n)
if g-l<=1:
return n
print g-l
p=n[l]
i=1+l
j=1+l
for x in n[l+1:g]:
if x<p:
n[i],n[j]=n[j],n[i]
i+=1
j+=1
n[l],n[i-1]=n[i-1],n[l]
ls=0
le=i-1
gs=i
ge=j
print n
qsort(n,ls,le)
qsort(n,gs,ge)
Can someone give me any suggestions, I'm lost. Can't find whats wrong or how to fix it.
Know its messy but cant do better atm :D

Write it like this:
def qsort(li, lo=None, hi=None):
if lo is None:
lo = 0
if hi is None:
hi = len(li) - 1
...
...
if hi - lo > 1:
qsort(## left half ##)
qsort(## right half ##)
lo and hi are the smallest and largest indexes you should look at in li, respectively.

Swap version of quicksort.
quicksort([10, 0, 9, 1, 8, 2, 7, 3, 6, 4, 5]) >> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
quicksort([0, 4, 2, 3, 6]) >> [0, 2, 3, 4, 6]
def quicksort(seq):
if len(seq) <= 1:
return seq
pivot_index = len(seq)-1
i = 0
while i < pivot_index:
if seq[i] > seq[pivot_index] and pivot_index-1 == i:
seq[i], seq[pivot_index] = seq[pivot_index], seq[i]
pivot_index -= 1
i = 0
elif seq[i] > seq[pivot_index] and pivot_index > 1:
seq[pivot_index-1], seq[pivot_index] = seq[pivot_index], seq[pivot_index-1]
seq[i], seq[pivot_index] = seq[pivot_index], seq[i]
pivot_index -= 1
i = 0
else:
i += 1
return quicksort(seq[:pivot_index]) + [seq[pivot_index]] + quicksort(seq[pivot_index+1:])

Related

How to compare each element in the list and check if it's bigger then the element on the right

hello I am struggling with this problem for school and can't get my code to do what it needs to solve this. The question is: Define an element of a list of items to be a dominator if every element to its right (not just the one
element that is immediately to its right) is strictly smaller than that element. It wants me to count how many denominators are in the list.
def extract_increasing(digits):
countDem = 0
#check and see if there is anything in the list
if not digits:
return 0
#compare the first element to the one on the right of it
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
return countDem
The code below should check if a number in the list is a dominator.
def is_dominator(lst, idx):
for i in range(idx + 1, len(lst)):
if lst[i] >= lst[idx]:
return False
return True
digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in digits:
print(is_dominator(digits, i))
The error in your code is that you're adding one for the counter every time the next value meets the condition.
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
countDem += 1
Every time digits[x] > digits[y] is met you add one to your counter. You should only add one once you checked that all values to the right meet the condition.
isDem = False
for x in range(len(digits)):
for y in range(x + 1, len(digits)):
if digits[x] > digits[y]:
isDem = True
else:
isDem = False
#Once you went through all the values to the right you can add one to the counter
if isDem ==True:
countDem += 1
Hope that helps!
You start in the last element, and save always the max_element in every iteration, then you know always if exist some number grater than the current number. This is a little more efficient because it runs through the array only once.
def dominator(li: list):
sol = 0
max_number = -math.inf
for i in range(len(li)-1, -1,-1):
if li[i] > max_number:
sol+=1
max_number = li[i]
return sol
Try list comprehension
lst = [0, 10, 2, 6, 7]
new_lst = [v for k,v in enumerate(lst) if all(v > x for x in lst[k+1:])]
# [10, 7]
Update
def extract_increasing(digits: list) -> int:
countDem = 0
for x, y in enumerate(digits):
if all(y > a for a in digits[x+1:]):
countDem += 1
return countDem
lst = [0, 10, 2, 6, 7]
extract_increasing(lst) # -> 2

Counting Shifts in Merge Sort Error with one array

I was trying to count the number of shifts that happen with merge sort when I ran into a problem. When I run the code with multiple arrays, for some reason one of the arrays states that 3 shifts happened when in reality its 4. I will greatly appreciate it if anyone can help me figure out what the problem is. Thanks
def mergeSort(arr):
count = x = y = 0
result =[]
arrayLength = len(arr)
if arrayLength <= 1:
return count
middle = arrayLength // 2
left = arr[:middle]
right = arr[middle:]
leftLength = len(left)
rightLength = len(right)
count += mergeSort(left)
count += mergeSort(right)
while x < leftLength and y < rightLength:
if left[x] <= right[y]:
result.append(left[x])
x += 1
else:
result.append(right[y])
y += 1
count += len(left[x:])-x
return count
arr = [1,20,6,4,5]
print(mergeSort(arr))
arr2 = [4,3,2,1]
print(mergeSort(arr2))
arr3=[1, 1, 1, 2, 2]
print(mergeSort(arr3))
arr4=[2, 1, 3, 1, 2]
print(mergeSort(arr4))
arr5 = [12,15,1,5,6,14,11]
print(mergeSort(arr5))
arr6=[3, 5, 7, 11, 9]
print(mergeSort(arr6))
result = mergeSort(arr)
print(result)
You have two bugs:
Your len(left[x:])-x subtracts x twice.
You're not actually sorting the given array but just building a result that you never use. The sorting is important for the upper call levels to count correctly.
Fixed and with better testing (Try it online!):
from itertools import combinations
def mergeSort(arr):
count = x = y = 0
arrayLength = len(arr)
if arrayLength <= 1:
return count
middle = arrayLength // 2
left = arr[:middle]
right = arr[middle:]
leftLength = len(left)
rightLength = len(right)
count += mergeSort(left)
count += mergeSort(right)
for write in range(arrayLength):
if y == rightLength or x < leftLength and left[x] <= right[y]:
arr[write] = left[x]
x += 1
else:
arr[write] = right[y]
y += 1
count += len(left) - x
return count
def naive(arr):
return sum(a > b for a, b in combinations(arr, 2))
def test(arr):
expect = naive(arr)
result = mergeSort(arr)
print(result == expect, expect, result, arr)
test([1, 20, 6, 4, 5])
test([4, 3, 2, 1])
test([1, 1, 1, 2, 2])
test([2, 1, 3, 1, 2])
test([12, 15, 1, 5, 6, 14, 11])
test([3, 5, 7, 11, 9])
I presume your question really isn't about counting the recursion and more about figuring out why your algorithm is not correct. When you're checking for while x < leftLength and y < rightLength: you are dropping items at the end of one of the lists. This should be an or not an and to make sure you are doing ALL items in both left and right lists. Something like this:
while x < leftLength or y < rightLength:
if x == leftLength:
result.append(right[y])
y += 1
continue
if y == rightLength:
result.append(left[x])
x += 1
continue
if left[x] <= right[y]:
result.append(left[x])
x += 1
else:
result.append(right[y])
y += 1
return result
and you can't return the counts like Frank said because you stop doing the merge sort, and writing back to arr doesn't work in python as it's not an in/out variable. You would have to have a global variable outside the class to do the counting.

Python remove N consecutive duplicates from the list

Here's the problem. The input is a list of integers. If more than three adjacent numbers appear next to each other they should be dropped and the operation goes again. Kind of similar to the Iphone game, where player needs to pop lines of three or more balls of the same colors. The output should be the count of the balls that will be removed.
The algorithm is as follows. Starting with a sample list of say [3,3,4,4,4,4,3,2].
First iteration should remove the 4,4,4,4 - so the list would become [3,3,3,2], and the intermediary output of removed numbers will be 4.
Second iteration should remove 3,3,3 - so the final list would be [2] and final count of removed numbers - 7.
The first implementation for three consecutive items came from another stackoverflow thread - Remove triplets of adjacent numbers from the list
Here's the working function implementation for exactly 3 consecutive numbers:
def balls(l):
values = l
while len(values) >= 3:
print(values) #for demonstrative purposes of this question
for x in range(0,len(values)-2):
if values[x] == values[x+1] and values[x] == values[x+2]:
values = values[:x] + values[x+3:]
break
else:
break
print(values) #for demonstrative purposes of this question
return len(l) - len(values)
balls([3, 3, 4, 4, 4, 3, 4])
Output:
[3, 3, 4, 4, 4, 3, 4]
[3, 3, 3, 4]
[4]
6
How could I update the implementation to include the more general solution of removing 3+ consecutive numbers. I am thinking about tracking the start and end index of the consecutive duplicates, then subsetting the list. However, not sure how to implement that. Here are the tests that should work.
if __name__ == "__main__":
test1 = [3, 3, 4, 4, 4, 3, 4]
print(balls(test1))
#Output should be 6
test2 = [5, 5, 5, 5, 5, 5, 5, 5]
print(balls(test2))
#Output should be 8
test3 = [5, 7, 8, 3]
print(balls(test3))
#Output should be 0
def remove_consecutive(l, length):
amount = len(l)
count = 1
start = 0
current = l[0]
i = 1
while i < len(l):
if l[i] == current:
count += 1
else:
if count >= length:
for i in range(count):
l.pop(start)
start = 0
i = 0
current = l[0]
else:
start = i
current = l[i]
count = 1
i+=1
if count >= length:
for i in range(count):
l.pop(start)
return amount - len(l)
Wuff, i got it. My brain is kinda stinky lately so it took so long.
Here is my code, it works well. But I think there may be better ways to achieve higher efficiency.
def remove_consecutive(lst):
len_init = len(lst)
contain_tuplets = True
while contain_tuplets:
for i in range(len(lst)-2):
indices_to_pop = []
if lst[i]==lst[i+1]==lst[i+2]:
indices_to_pop.extend([i, i+1, i+2])
for j in range(i+3,len(lst)):
if lst[j] == lst[i]:
indices_to_pop.append(j)
else:
break
[lst.pop(i) for _ in indices_to_pop]
contain_tuplets = True
break
else:
contain_tuplets = False
count_removed_numbers = len_init - len(lst)
return count_removed_numbers, lst
test case1:
lst = [3,3,4,4,4,4,3,2]
remove_consecutive(lst)
output
(7, [2])
test case 2:
lst = [2, 2, 1, 1, 1, 2, 1]
remove_consecutive(lst)
output:
(6, [1])
def remove_consecutive(l, length):
amount = 0
count = 1
current = l[0]
for i in range(1, len(l)):
if l[i] == current:
count += 1
if count > length:
amount += 1
elif count == length:
amount += length
else:
current = l[i]
count = 1
return amount

Finding Maximum non-negative Subarray in python

I've tried to find the sub-array(s) from a given which contain elements of maximum sum than any other sub array.
Below function has parameter as input a and the output needs to be returned. There can be more than one subarray as their maximum sum can be equal. The code did not seem to be working as expected.
def max_sum_subarray(a):
N, sub_sum, max_sum, subArrays = len(a), 0, 0, {}
p,q=0,0 #starting and ending indices of a max sub arr
for i in range(N):
q=i
sub_sum+=a[i]
if(a[i]<0):
q-=1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
sub_sum=0
p=i+1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
return(subArrays[p:q+1])
When I tried to run for input
a=[ 1, 2, 5, -7, 2, 5 ]
Expected output is [1, 2, 5] but it gave [2, 5] instead. Can anyone please post the solution in python?
It seems like you making this harder than necessary. You can just keep track of max array seen to far and the current one you're pushing into -- you don't really need to care about anything else. When you hit a negative (or the end of the array) decide if the current should be the new max:
def maxSub(a):
max_so_far = []
max_sum = 0
cur = []
for n in a:
if n >= 0:
cur.append(n)
else:
cur_sum = sum(cur)
if cur_sum > max_sum:
max_sum = cur_sum
max_so_far = cur
cur = []
return max([max_so_far, cur], key = sum)
a=[ 1, 2, 5, -7, 2, 5 ]
maxSub(a)
# [1, 2, 5]
Of course itertools.groupby makes this a one-liner:
from itertools import groupby
a=[ 1, 2, 5, -7, 2, 5 ]
max([list(g) for k,g in groupby(a, key=lambda x: x>0) if k == True], key=sum)
For the following conditions:
NOTE 1: If there is a tie, then compare with segment’s length and
return segment which has maximum length
NOTE 2: If there is still a tie, then return the segment with minimum
starting index
Here is my working code in python:
def check(max_arr,curr):
if sum(curr) > sum(max_arr):
max_arr = curr
elif sum(curr) == sum(max_arr):
if len(curr) > len(max_arr):
max_arr = curr
elif len(curr) == len(max_arr):
if max_arr and (curr[0] > max_arr[0]):
max_arr = curr
return max_arr
def maxset(A):
curr = []
max_arr = []
for i in A:
if i >= 0:
curr.append(i)
else:
max_arr = check(max_arr,curr)
curr = []
max_arr = check(max_arr,curr)
return max_arr

builtins.IndexError: list index out of range

I have this function here:
def swap_cards(deck, index_deck):
swap_deck = []
after_index = []
bandf_index = []
if index_deck >= len(deck):
for i in range(0, len(deck) + 1):
if deck[i] == index_deck:
after_index += [deck[0]]
else:
bandf_index += [deck[i]]
swap_deck += [deck[index_deck]] + bandf_index[1:index_deck - 1] + after_index
Every time I try to input an index that is greater than or equal to the deck length I get the error 'list index out of range'
Example:
swap_cards([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9)
I expect [9, 1, 2, 3, 4, 5, 6, 7, 8, 0] but am instead getting 'list index out of range'.
Switching the last and the first number, I don't know what I am doing wrong.
You have a problem with len(). Assuming your index_deck is 9 as in the example above:
if index_deck >= len(deck):
for i in range(0, len(deck) + 1):
This range will yield [0,1,2,3,4,5,6,7,8,9,10,11]
if deck[i] == index_deck:
after_index += [deck[0]]
Since array indexes are zero-based, the above line will generate the error. deck[10] and deck[11] don't exist.
else:
bandf_index += [deck[i]]
Fixing the above len() statement will fix this as well (again, you may try to access deck[10] if the if statement is false.
swap_deck += [deck[index_deck]] + bandf_index[1:index_deck - 1] + after_index
Final thing: you verify that if index_deck >= len(deck):. This will break the above line at deck[index_check] since here you will always access an index which does not exist.

Categories

Resources