Min swaps to sort unordered consecutive integers - python

You are given an unordered array consisting of consecutive integers [1, 2, 3, ..., n] without any duplicates. You are allowed to swap any two elements. You need to find the minimum number of swaps required to sort the array in ascending order. The code what i have got ,timed out for a few test cases. Is there any way that we can optimise the code?
My code is as follows:
def minimumSwaps(arr):
lst=[ele+1 for ele in range(len(arr))]
cnt=0
for i in range(len(arr)):
if(lst[i]!=arr[i]):
k=arr.index(lst[i])
arr[i],arr[k]=arr[k],arr[i]
cnt=cnt+1
return cnt

You don't really need to generate a reference list lst because you should know that n in arr should be in the index n-1 to be ascending. Also, doing k=arr.index(lst[i]) requires O(n) time to search, and which is complete unnecessary. Here's my solution:
def minimumSwaps(arr):
total_swaps = 0
start = 0
while start < len(arr):
if arr[start] == start + 1:
start += 1
continue
arr[arr[start] - 1], arr[start] = arr[start], arr[arr[start] - 1]
total_swaps += 1
return total_swaps
If I guess it right, it's a question on Hackerrank, and this was my solution by the time passed the tests. :P
This algorithm starts at position one and swaps the items under the cursor directly into their correct positions. When the correct item is swapped into this position it increments the cursor to the next item and repeats the process.
This is more efficient than stepping through the array sequentially, swapping the correct item into each position, because you don't have to work out exactly where the correct item is located within the tail of the list.

I used the logic given in link here: https://www.youtube.com/watch?v=Def9kMtZCNA
def minimumSwaps(arr):
# element usually at element value-1 index value
# eg, 1 at index 0, 2 at index 1.....
counter=0
for i in range(len(arr)):
if arr[i] != i+1:
while arr[i] != i+1:
temp=arr[arr[i]-1]
arr[arr[i]-1]=arr[i]
arr[i]=temp
counter+=1
else:
continue
return counter

Related

Remove Duplicates from Sorted Array in O(1) Space

Question: https://leetcode.com/problems/remove-duplicates-from-sorted-array/description/
Basically, duplicates in a sorted array needs to be removed without creating a new one. I have tested my solution, and it falters when there are 3, 5, 7 (and so on) number of duplicates of the same number. What techniques can I try?
Code:
def removeDuplicates(nums):
i = 0 # moving pointer
end = len(nums)-1 # points to the end of the array
# iterates over the array
while i != end:
# if current number is equal to the next
if (nums[i] == nums[i+1]):
# pop it off
nums.pop(i)
# decrement the length of array
end -= 1
i += 1 # increments the moving pointer
return i + 1
nums = [0,0,1,1,1,2,2,3,3,4]
print(removeDuplicates(nums))
The way to remove duplicates from a sorted list in O(1) space and O(n) time is to pack the unique elements at the beginning of the list, and then truncate the list:
def removeDuplicates(nums):
#new length
newlen = 0
for val in nums:
if newlen == 0 or nums[newlen-1] != val:
# no matter what, we've already retrieved nums[newlen],
# so this write doesn't interfere with the rest of the loop
nums[newlen] = val
newlen += 1
# delete the unused space at the end of the list, all at once
if newlen < len(nums):
del nums[newlen:]
return newlen
nums = [0,0,1,1,1,2,2,3,3,4]
print(removeDuplicates(nums))
print(nums)
Your original code fails, because you remove elements from the start of the array as you're iterating through the end. The iteration remembers its position in the array in i, and when you remove an element, the elements after it are shifted into lower positions.
You could fix that by putting the i += 1 into an else, but the resulting implementation would still take O(N^2) time, which isn't really acceptable.

Writing a Conditional For Loop to Sum a Subset of a List and Return Binary Value

I want to focus on a moving window of k in the lyst below, starting on the left and stopping one element short of the right edge. I want to take the sum of the 3 (k) items in the window. If the sum is >= k/2, append the value of 1 to the list "pred", otherwise append 0 to the list.
Here is my code so far:
lyst=[1,0,1,0,1]
k=3
pred=[]
for x in range(0, len(lyst)-k):
sumList=sum(lyst)
if sumList >= k/2:
pred.append(1)
else:
pred.append(0)
print(pred)
I know my sumList item is the issue here. I just need to adjust that so it generally sums k items to create 2 (that's len(lyst)-k) new values to pred. Each value will either be 0 or 1 depending on the condition.
Output should be:
pred=[1, 0]
Output I'm getting now:
pred=[1,1]
The critical part is taking only the required slice of lyst for your sum. Your start position is the left end for k elements of lyst:
for start in range(0, len(lyst)-k):
sumList = sum(lyst[start:start+k])
This will move a k-element window through lyst: [1, 0, 1], then [0, 1, 0]. These are greater than k/2 (yielding a 1) and less (for a 0).
Output:
[1, 0]
ADVANCED IMPLEMENTATION
You can build a list comprehension from the inside out, using the code in your (now repaired) loop.
lyst=[1,1,0,0,1]
k=3
pred = [int(sum(lyst[start:start+k]) >= k/2)
for start in range(0, len(lyst)-k)]
First you have an error in your code:
for x in range(0, len(lyst)-k) will iterate over the len(lyst)-k first items not the last ones.
You can read about range function to see how it works. Said that you can use slices in order to take the sum of the elements you want.
You code can be changed as:
for x in range(len(lyst) - 1, len(lyst) - k, -1):
sumList=sum(lyst[x-k:x])

i want to find out the index of the elements in an array of duplicate elements

a=[2, 1, 3, 5, 3, 2]
def firstDuplicate(a):
for i in range(0,len(a)):
for j in range(i+1,len(a)):
while a[i]==a[j]:
num=[j]
break
print(num)
print(firstDuplicate(a))
The output should be coming as 4 and 5 but it's coming as 4 only
You can find the indices of all duplicates in an array in O(n) time and O(1) extra space with something like the following:
def get_duplicate_indices(arr):
inds = []
for i, val in enumerate(arr):
val = abs(val)
if arr[val] >= 0:
arr[val] = -arr[val]
else:
inds.append(i)
return inds
get_duplicate_indices(a)
[4, 5]
Note that this will modify the array in place! If you want to keep your input array un-modified, replace the first few lines in the above with:
def get_duplicate_indices(a):
arr = a.copy() # so we don't modify in place. Drawback is it's not O(n) extra space
inds = []
for i, val in enumerate(a):
# ...
Essentially this uses the sign of each element in the array as an indicator of whether a number has been seen before. If we come across a negative value, it means the number we reached has been seen before, so we append the number's index to our list of already-seen indices.
Note that this can run into trouble if the values in the array are larger than the length of the array, but in this case we just extend the working array to be the same length as whatever the maximum value is in the input. Easy peasy.
There are some things wrong with your code. The following will collect the indexes of every first duplicate:
def firstDuplicate(a):
num = [] # list to collect indexes of first dupes
for i in range(len(a)-1): # second to last is enough here
for j in range(i+1, len(a)):
if a[i]==a[j]: # while-loop made little sense
num.append(j) # grow list and do not override it
break # stop inner loop after first duplicate
print(num)
There are of course more performant algorithms to achieve this that are not quadratic.

Optimizing Python Function to Submit on Codewars

I have a solution for this problem on codewars.com that works when I run it in Sublime, but when I try to submit, I get this error:
Process was terminated. It took longer than 12000ms to complete
Why did my code time out?
Our servers are configured to only allow a certain amount of time for your code to execute. In rare cases the server may be taking on too much work and simply wasn't able to run your code efficiently enough. Most of the time though this issue is caused by inefficient algorithms. If you see this error multiple times you should try to optimize your code further.
The goal of the function is to find the next biggest number after a given number that you can make by rearranging the digits of a given number. For example, if I was given 216, I would need to return 261.
This is the code I have now:
import itertools
def next_bigger(n):
# takes a number like 472 and puts it in a list like so: [4, 7, 2]
num_arr = [int(x) for x in str(n)]
perms = []
total = ''
# x would be a permutation of num_arr, like [7, 2, 4]
for x in itertools.permutations(num_arr):
for y in x:
total += str(y)
perms.append(int(total))
total = ''
# bigger is all permutations that are bigger than n,
# so bigger[0] is the next biggest number.
# if there are no bigger permutations, the function returns -1
bigger = sorted([x for x in perms if x > n])
return bigger[0] if bigger else -1
I'm new to coding in Python, so is there some mistake I am making which causes my code to be extremely inefficient? Any suggestions are welcome.
Thanks for all the help you guys gave me. I ended up finding a solution from here using the Next Lexicographical Permutation Algorithm
This is my tidied up version of the solution provided here:
def next_bigger(n):
# https://www.nayuki.io/res/next-lexicographical-permutation-algorithm/nextperm.py
# https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
# Find non-increasing suffix
arr = [int(x) for x in str(n)]
i = len(arr) - 1
while i > 0 and arr[i - 1] >= arr[i]:
i -= 1
if i <= 0:
return -1
# Find successor to pivot
j = len(arr) - 1
while arr[j] <= arr[i - 1]:
j -= 1
arr[i - 1], arr[j] = arr[j], arr[i - 1]
# Reverse suffix
arr[i : ] = arr[len(arr) - 1 : i - 1 : -1]
return int(''.join(str(x) for x in arr))
Why are you getting TLE (time limit exceeded)?
Because your algorithm has wrong complexity. How much permutations you will find for list with 3 elements? Only 6. But what if we use list with 23 elements? 25852016738884976640000.
This is too much for time limit.
So, if you want to have solve this problem you have to find solution without permutations. Please rethink how the numbers are written. The number 271 is bigger then 216 because the number on the second position has bigger value 7>1.
So, your solution has to find two numbers and swap them position. The number on the left have to smaller then the second one.
For example - for 111115444474444 you should find 5 and 7.
Then you swap them - and now you should sort sublist on right from the first position.
For example after swapped the values (111117444454444) you have to sort (444454444) -> (444444445). Now merge all, and you have solution.
import functools
def next_bigger(a):
a = map(int, str(a))
tmp = list(reversed(a))
for i, item_a in enumerate(reversed(a)):
for j in (range(i)):
if item_a < tmp[j]:
#you find index of number to swap
tmp[i]=tmp[j]
print(list(reversed(tmp[i:])))
tmp[j]=item_a
fin = list(reversed(tmp[i:])) + sorted(tmp[:i])
return functools.reduce(lambda x,y: x*10+y, fin)
return -1
A simple backtracking approach is to consider the digits one at a time. Starting from the most significant digit, pick the smallest number you have left that doesn't prevent the new number from exceeding the input. This will always start by reproducing the input, then will have to backtrack to the next-to-last digit (because there aren't any other choices for the last digit). For inputs like 897654321, the backtracking will immediately cascade to the beginning because there are no larger digits left to try in any of the intermediate slots.
You should sorting the num_arr in desc order and creating a number by combining the result.
Since OP required, next largest, OP needs to check starting from right, which right digit is larger then its very left digit and rotate their position.
Here is the final code:
def next_bigger(n):
num_arr = [int(x) for x in str(n)]
i = 0
i = len(num_arr) - 1
while(i > 0):
if num_arr[i] > num_arr[i-1]:
a = num_arr[i]
num_arr[i] = num_arr[i-1]
num_arr[i-1] = a
break
else:
i = i-1
newbig = "".join(str(e) for e in num_arr)
return int(newbig)
Now I edit to calculate next bigger element.
def perms(s):
if(len(s)==1):
return [s]
result=[]
for i,v in enumerate(s):
result += [v+p for p in perms(s[:i]+s[i+1:])]
return result
a=input()
b=perms(str(a))
if len(b)!=1:
for i in range(0,len(b)):
if b[i]==a:
print (b[i+1])
break
else:
print ("-1")

Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
My solution:
def findDuplicate(nums):
slow = fast = finder = 0
while fast is not None:
slow = nums[slow]
fast = nums[nums[fast]]
if fast is slow:
return slow
return False
nums = [1,2,2,3,4]
print findDuplicate(nums)
My above solution works and gives me o/p 2 but it doesn't work for every input for example it doesn't work for [11,15,17,17,14] or [3,1,2,6,2,3] and gives me error IndexError: list index out of range. I am not able to find patterns and am not able to track down the exact problem. Also tried to change my while condition:
while fast is not None and nums[nums[fast]] is not None:
your help will be greatly appreciated! Thank you.
Since the numbers are between 1 and n and you have been told there is only one duplicate, you can use difference between the sum of the numbers in the array and the sum of numbers from 1 to n to get the duplicate.
def findDuplicate(l):
n = len(l) - 1 # Get n as length of list - 1
return sum(l) - (n * (n + 1) / 2) # n*(n+1)/2 is the sum of integers from 1 to n
So the duplicate is the sum of the list - n*(n+1)/2
Of course, this doesn't generalize to finding duplicates for any list. For that case, you need to use #Jalepeno112 's answer.
The fact that the first one works is a fluke. Let's look at what it does on the first pass.
nums = [1,2,2,3,4]
# slow starts as index 0. So now, you've reassigned slow to be nums[0] which is 1.
# so slow equals 1
slow = nums[slow]
# now you are saying that fast equals nums[nums[0]].
# nums[0] is 1. nums[1] is 2
# so fast = 2
fast = nums[nums[fast]]
On the next pass, slow will be nums[1] which is 2. fast will be nums[nums[2]] which is nums[2] which is 2. At this point slow and fast are equal.
In your second example, you are getting an IndexError because of fast = nums[nums[fast]] If the value at nums[fast] is not a valid index, then this code will fail. Specifically in the second example, nums[0] is 11. nums doesn't have an element at index 11, so you get an error.
What you really want to be doing is performing a nested for loop on the array:
# range(0,len(nums)-1) will give a list of numbers from [0, to the length of nums-1)
# range(1, len(nums)) does the same,
# except it will start at 1 more than i is currently at (the next element in the array).
# So it's range is recomputed on each outer loop to be [i+1, length of nums)
for i in range(0,len(nums)-1):
for j in range(i+1,len(nums)):
# if we find a matching element, return it
if nums[i] == nums[j]:
return nums[i]
# if we don't find anything return False
return False
There are likely other more Pythonic ways to achieve this, but that wasn't your original question.
first you must ensure all numbers in list satisfy your constrains.
to find duplicated numbers in a list Use Counter in collections it will return each number and number of occurrence example :
>>> from collections import Counter
>>> l=Counter([11,15,17,17,14])
>>> l
Counter({17: 2, 11: 1, 14: 1, 15: 1})
to get the most common one use :
>>> l.most_common(n=1)
[(17, 2)]
where n is the number most common numbers you want to get
def duplicates(num_list):
if type(num_list) is not list:
print('No list provided')
return
if len(num_list) is 0 or len(num_list) is 1:
print('No duplicates')
return
for index,numA in enumerate(num_list):
num_len = len(num_list)
for indexB in range(index+1, num_len):
if numA == num_list[indexB]:
print('Duplicate Number:'+str(numA))
return
duplicates([11,15,17,17,14])
duplicates([3,1,2,6,2,3])
duplicates([])
duplicates([5])
l=[]
n= int(input("the number of digit is :"))
l=[0 for k in range(n)]
for j in range(0,n):
l[j]=int(input("the component is"))
print(l)
b=0; c=0
for i in range(n):
if l[i]== l[n-1-i]:
b=1;c=i
if b==1:
print("duplicate found! it is",l[c])
elif b==0:
print("no duplicate")
The answer is unfinished. It tries to convert the array to a linked list. So far it found where the slow pointer and fast pointer met, but this is the halfway solution. To get the solution, we need to initialize another pointer from the beginning of the linked list and walk towards each other. When they meet, that point is the where cycle is detected, in our question it is where the single point is:
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
slow,fast=0,0
while True:
slow=nums[slow]
fast=nums[nums[fast]]
if slow==fast:
break
slow2=0
while True:
slow2=nums[slow2]
slow=nums[slow]
if slow==slow2:
return slow2

Categories