I am having a problem with my Quick Sort code. I am new to coding and python(python 3.6), any help would be really appreciated. I have seen several implementations of Quick Sort online but I want to figure out what is really wrong with my code. I am pasting my code below:
def Partition(A):
q = A[0]
i = 1
for j in range(1, len(A)):
if A[j] < q:
A[j], A[i] = A[i], A[j]
i = i + 1
A[0], A[i - 1] = A[i - 1], A[0]
return i - 1
def QuickSort(A):
if len(A) <= 1:
return A
else:
q = Partition(A)
QuickSort(A[ : q])
QuickSort(A[q + 1 : ])
return A
A = [7, 5, 4, 1, 3, 6, 2, 8]
Sorted = []
Sorted = QuickSort(A)
print(Sorted)
For the input above I am getting the output [2, 5, 4, 1, 3, 6, 7, 8] instead of getting a sorted list in ascending order.
These try to sort copied parts of A:
QuickSort(A[ : q])
QuickSort(A[q + 1 : ])
They return something, but you ignore what they return, so it's lost. You should write their results back into A:
A[ : q] = QuickSort(A[ : q])
A[q + 1 : ] = QuickSort(A[q + 1 : ])
After this change, your result is the expected [1, 2, 3, 4, 5, 6, 7, 8].
Related
This question already has answers here:
How to insert multiple values by index into list at one time
(4 answers)
Closed 7 months ago.
I want to solve the following problem in one line. I looked at the itertools docs to find a specific function for this but no luck. Say
a = [1, 2, 3,4]
b = [5, 6, 7, 8]
I want to insert the elements of b into a, but each element into a specific index. So
insert_function(a, b, keys=[0,1, 2, 3])
should return
[5, 1, 6, 2, 7, 3, 8, 4]
One approach:
def insert_function(la, lb, keys=None):
ii = [i + k for i, k in enumerate(keys)]
i, j = 0, 0
ret = []
for r in range(len(la) + len(lb)):
if r in ii:
ret.append(lb[j])
j += 1
else:
ret.append(la[i])
i += 1
return ret
res = insert_function(a, b, keys=[0, 1, 2, 3])
print(res)
Output
[5, 1, 6, 2, 7, 3, 8, 4]
Or as an alternative use this one-liner list comprehension with O(n + m) time complexity:
def insert_function(la, lb, keys=None):
ii = set(i + k for i, k in enumerate(keys))
it_a, it_b = iter(a), iter(b)
return [next(it_a) if r not in ii else next(it_b) for r in range(len(la) + len(lb))]
res = insert_function(a, b, keys=[0, 1, 2, 3])
print(res)
The problem is that when you insert elements you shift the subsequent indices. That can be prevented by inserting from right to left:
def insert_function(a, b, keys):
# copy so original list is left intact
a = a.copy()
# sort the keys in reverse
for k,v in sorted(zip(keys, b), key=lambda x:x[0], reverse=True):
a.insert(k, v)
return a
# returns [5, 1, 6, 2, 7, 3, 8, 4]
print(insert_function(a, b, [0, 1, 2, 3]))
This should work!
def insert_function(a, b, keys):
assert len(b) == len(keys)
for i in range(len(b)):
a.insert(keys[i], b[i])
I currently have a list like this:
A = [1, 2, "NaN", 4, 5,"NaN"]
If an element is null, I would like to replace it with the average value of adjacent values
so A[3] would be replaced by 3 (2+4/2).
If there is only one adjacent value as shown by A[5], then return the one adjacent value itself.
Desired output:
A = [1, 2, 3, 4, 5, 5]
I'm sure there is a better way, but here is one way to do it. It handles the case where NaN appears at the end of the list.
Code:
A = [1, 2, "NaN", 4, 5,"NaN"]
for i in range(len(A)):
if A[i] == "NaN":
if i == 0: A[i] = A[1]
elif i == len(A) - 1: A[i] = A[i - 1]
else: A[i] = (A[i - 1] + A[i + 1]) // 2
print(A)
Output:
[1, 2, 3, 4, 5, 5]
Shift the items in the given array, by some number of times, as shown in the below examples;
array = [1, 2 ,3 , 4, 5, 6]
k1 = 2
k2 = -3
k3 = 20
test1:
cirShift(array, k1)
Result: [5, 6, 1, 2, 3, 4]
test2:
cirShift(array, k2)
Result: [4, 5, 6, 1, 2, 3]
test3:
cirShift(array, k3)
Result: [5, 6, 1, 2, 3, 4]
I have used the below to achieve the right-rotate a list by k positions;
def rightRotateByOne(A):
Fin= A[-1]
for i in reversed(range(len(A) - 1)):
A[i + 1] = A[i]
A[0] = Fin
def rightRotate(A, k):
if k < 0 or k >= len(A):
return
for i in range(k):
rightRotateByOne(A)
if __name__ == '__main__':
A = [1, 2, 3, 4, 5, 6, 7]
k = 3
rightRotate(A, k)
print(A)
As of now, able to obtain results for test1 but would like to achieve the test2 and test3
Even easier, split the array in half given the boundary, swap and glue back:
def cirShift(a, shift):
if not shift or not a:
return a
return a[-shift%len(a):] + a[:-shift%len(a)]
Courtesy of #KellyBundy, a short-circut one-liner:
def cirShift(a, shift):
return a and a[-shift%len(a):] + a[:-shift%len(a)]
I think this question may be an exercise in self learning ('how to do X using just python'), so my answer is auxiliary, but you can always use np.roll():
#test 1
import numpy as np
a = [1, 2 ,3, 4, 5, 6]
np.roll(a, 2)
gives output
[5, 6, 1, 2, 3, 4]
and
#test 2
np.roll(a, -2)
gives output
[3, 4, 5, 6, 1, 2]
Even if you give a number that is larger than the array size, it handles the overflow:
#test 3
np.roll(a, 10)
gives output
[3, 4, 5, 6, 1, 2]
Rolling also works in multiple dimension arrays and across specified axes, which is pretty neat.
def shift(l, shift_t):
r = [*l]
for n in range(abs(shift_t)):
if shift_t < 0:
r = r[1:] + [r[0]]
else:
r = [r[-1]] + r[:-1]
return r
The key is to take one item of the list and place it on the opposite side, which is essentially all that shifting is doing. If you shift negative, you put the first one at the end, and if you shift positive, you put the last one at the beginning.
I'm having a problem returning the sum. I keep getting zero.
I commented the print results for better understanding. Again, my code is returning 0, and not 11.
A = [1, 5, 2, 1, 4, 0]
def solution(A):
start = [i - j for i, j in enumerate(A)]
start.sort() #[-4, -1, 0, 0, 2, 5]
for i in range(0, len(A)):
pair = 0
end = i + A[i] #1, 6, 4, 4, 8, 5
count = bisect_right(start, end) #4, 6, 5, 5, 6, 6
count_1 = count-1 #3, 5, 4, 4, 5, 5
count_2 = count_1 - i #3, 4, 2, 1, 1, 0
pair += count_2 #??????? I need the above added together like this 3+4+2+1+1+0 since
that's the answer.
return pair
print(solution(A))
As you've written in the comments, the last count2 is zero.
You're adding zero to zero, the loop ends, and you return zero.
You need to start the counter outside the loop, or you can sum outside too, like so
counts = []
for i, x in enumerate(A):
counts.append(bisect_right(start, i + x) - 1 - i)
return sum(counts)
Which could be rewritten
return sum(bisect_right(start, i + x) - 1 - i for for i, x in enumerate(A))
You are given an array of integers a. A new array b is generated by rearranging the elements of a in the following way:
b = [a[0], a[len(a)-1], a[1], a[len(a)-2, ...]
my code only loops for one time, and I am just stuck from here. What I have tried is below
def alternatingSort(a):
length = len(a)
b = []
for i in range(length):
if i % 2:
b.append(a[length-i])
else:
b.append(a[i])
return b
if my input is [1, 3, 5, 6, 4, 2], my output should be [1,2,3,4,5,6]
But i get [1, 2, 5, 6, 4, 3].
Your logic is not correct. Here, is the working solution with minimum changes:
def alternatingSort(a):
length = len(a)
b = []
for i in range(length):
if i % 2:
b.append(a[length - (i // 2) - 1]) # Updated.
else:
b.append(a[i // 2]) # Updated.
return b
a = [1, 3, 5, 6, 4, 2]
print(alternatingSort(a))
a = [1, 3 ,2]
print(alternatingSort(a))