I'm doing the leetcode question #217 : Contains Duplicate.
After checking the answer, I cannot understand some parts of the code. The question is as given below:
Given an integer array nums, return true if any value appears at least twice in the array, and return false if every element is distinct.
Example 1:
Input: nums = [1,2,3,1]
Output: true
Example 2:
Input: nums = [1,2,3,4]
Output: false
The answer is the following:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
nums.sort()
for i in range(0,len(nums)-1):
if nums[i] == nums[i+1]:
return True
return False
The following is my question:
In my understanding, looping through range(0,len(nums)-1) allows us to check and compare characters in num[0:-2], but what about the last two characters?
How can we compare those two characters if we have nums like [2,14,18,22,22].
And for the last two lines, why can't we directly use return False instead of using the if...else... structure?
How is the logic been written here?
Thanks for anyone who can clarify my puzzle!
You are comparing the last two elements. When i == len(nums)-2, you compare nums[i] with nums[i+1]. Those are the last two elements.
You don't need if/else. As soon as you find a duplicate, you immediately return from the function. So the only way to get to the end of the loop is if there are no duplicates.
Python Solution:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
return len(set(nums)) != len(nums)
set() method in python takes parameters like Any iterable sequence like list, tuple, or dictionary and returns an empty set if no element is passed. Non-repeating element iterable modified as passed as argument.
Here, we are checking if the length of the list after iterating equals the length before? If yes implies there is no number repeated.
range(start, end, step function work from start, start+1, start+2 ... end-1, where end is exclusive and start is inclusive.
ie for range(0,100,1) it wil go as 0,1,2,3,4,5......96,97,98,99.
if your code you are checking character ith and(i+1)th character. so in at last index ie (len(num)-2), you are checking len(num)-2 and len(num)-1 character which include the last elements.
now why return directly false. it is considering that all element are distinct if the program is reached till this statement. if any duplicate is found then it will be found in the for loop.
Related
I'm working on https://leetcode.com/problems/permutations/ and I'm trying to decide which approach for generating the permutations is more clear. The question is "Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order." I've got two different solutions below.
Solution 1
def permute(self, nums: List[int]) -> List[List[int]]:
results = []
N = len(nums)
def dfs(subset, permutation: List[int]):
if len(subset) == N:
results.append(subset.copy())
return
for i, num in enumerate(permutation):
subset.append(num)
dfs(subset, permutation[:i] + permutation[i+1:])
# backtracking
subset.pop()
dfs([], nums)
return results
Solution 2
def permute(self, nums: List[int]) -> List[List[int]]:
results = []
N = len(nums)
def dfs(subset, permutation: List[int]):
if len(subset) == N:
results.append(subset.copy())
return
for i, num in enumerate(permutation):
dfs(subset + [num], permutation[:i] + permutation[i+1:])
dfs([], nums)
return results
I believe in the first solution, when you append to a list in python (i.e append to the subset parameter), lists are pass by reference so each recursive call will share the same list. This is why we have to explicitly backtrack by popping from subset. However in the second solution when a list is passed to a recursive call with the syntax subset + [num], a copy of the list is passed to each recursive call so that's why we don't explicitly have to backtrack.
Can someone confirm if my assumptions are correct? Is one approach favored over another? I think the time and space complexities are identical for both approaches (O(N!) and O(N), respectively) where N = the number of elements in nums.
Yes you are right that the first permute passes the same object (subset) in each recursive call.
And this is possible in first permute because lists are mutable, if you had a string to permute upon then you have to pass a copy because they are immutable.
And in the second permute a copy of subset is created. You can test it with the statement print(id(subset)) at the beginning of dfs in each permute. You can observe that the statement prints same id in the first permute but not in the second permute.
To me even though both have same time complexity (depends on what you do at the base condition - its O(N.N!) and not O(N!) because you are appending a copy of list to the result list ), why do you want to create a copy of subset and place an entirely new object on stack when you can have the copy of object reference (not the object itself!) on the stack which consumes less memory. So I prefer first permute.
This is two sum problem from leetcode, I tried to solve, It got accepted. I am asking if this code is efficient enough in terms of memory and space complexity.
My code :
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
l = len(nums)
ans = []
for i in range(l):
compliment = target - nums[i];
# print(a.index(x))
if compliment in nums:
if nums.index(compliment)!=i:
# print(a.index(x))
ans.append(i)
ans.append(nums.index(compliment))
break;
return ans
Your code searches for compliment in nums, and then follows it up with an index function. Later on, you use this index again in nums.index(compliment). Essentially, you're searching through the array three times. A better way would be to search through the array and store the index if found, else -1. If the index is not -1, i.e., present in the array, you can append it to ans. This essentially skips the two lines (and that index function) and traverses the array once instead of thrice.
index_if_found = -1
for index in range(len(arr)):
if arr[index] == search_value:
index_if_found = index
break
You can now use this index index_if_found instead of using the index function.
EDIT: Thanks to Kelly for correcting me on the search algorithm.
Additionally, you have two append methods to the same array. A slightly faster approach would be to use the extend method and add them in the same operation. So instead of
ans.append(i)
ans.append(nums.index(compliment))
You'd have
ans.extend((i, nums.index(compliment)))
Why dose the order of the conditions inside a while loop create different results. For example
nums = []
k=3
while (nums[-1]<k and nums ):
print('*')
gives error list index out of range
But this one doesn't show any error
nums = []
k=3
while( nums and nums[-1]<k ):
print('*')
Yes it is expected behaviour, if an empty list is accessed by any index (-1 or any), it always throw exception reason being to access an empty list.
num[-1] represents the last element in the list. And num=[] is an empty list. Hence num[-1] will always throw exception in this case.
while nums and nums[-1]<k :
is safe condition as it first checks whether list contains elements or not, if it contains then only nums[-1] get executed.
Let's take a step back and look at this without using any loops. Assuming we have an empty list like below:
nums = []
What would you expect nums[0] to do? How about the below:
nums[-1]
In both cases above, an index error would be raised, because the list nums is empty (it contains no elements, so there are elements within nums to access).
Now let's make it a little more difficult. How about the following:
last_num = nums and nums[-1] or None
Actually, that should be same (roughly) as the below:
last_num = nums[-1] if nums else None
This above syntax is a common idiom that implements a "safety check", meaning it retrieves the last element if num is a non-empty list, or a default value of None otherwise.
Given an array of ints, return True if .. 1, 2, 3, .. appears in the array somewhere.
def array123(nums):
for i in nums:
if nums[i:i+3] == [1,2,3]:
return True
return False
Coding bat problem
my code is satisfying all the test cases except for nums=[1,2,3]
can someone tell me whats wrong with my code
It should be like this.
def array123(nums):
for i in range(len(nums)):
if nums[i:i+3] == [1,2,3]:
return True
return False
Try this. Hope this helps. :)
Your code is not completely right. You're slicing the list with the items in the list, not with indices. Luckily, this did not throw any error because the items in the list are within the bounds of the list's indices or rather slicing does not raise errors, when done correclty, irrespective of start and/or stop indices.
You can use range(len(...)) to generate the indices, and you may stop the search at len(nums) - len(sublist), so you don't check for slices less than the length of the sublist. This comes more handy as the length of the sublist gets larger.
def array123(nums, sublist):
j = len(sublist)
for i in range(len(nums)-j):
if nums[i:i+j] == sublist:
return True
return False
# Call function
array123(nums, [1,2,3])
Useful reference:
Explain Python's slice notation
You're getting wrong result because you're iterating on the value of elements not on the index of elements of elements. Try following code
def array123(nums):
for i in range(len(nums)-2):
if nums[i:i+3] == [1,2,3]:
return True
return False
And remember to give the end index of list (range(len(nums)-2)) because suppose if length of your array is 4 then (range(len(nums)-2)) will be
(range(2)) = [0,1]
So the loop will iterate for 0,1 as starting index
So if I have a list, let's say: [1,2,'hello'] and I want to add all the numbers in a list, (strings should be ignored). I want it to return the number 3 (1+2). And the function must be recursive.
This is what I have come up with:
import numbers
def isnumber(x):
return isinstance(x, numbers.Number)
def sum_first(list):
if not list:
return 0
elif isnumber(list[-1]):
return list[-1] + sum_first(list[:-1])
list=eval(input("Enter a list: "))
print(sum_first(list))
This only works if the last element in the list is a number. Let's assume it's a string, how could I change it?
You are only handling two cases:
the list is empty
the last element is a number
Your example list doesn't fit either case, so you end up returning None.
Add a third branch:
def sum_first(lst):
if not lst:
return 0
elif isnumber(lst[-1]):
return lst[-1] + sum_first(lst[:-1])
else:
return sum_first(lst[:-1])
Now you handle the case where the last element is not a number; you handle it by ignoring it; recursing with the list without that one element.
I renamed your list variable to lst to avoid masking the built-in type.
reduce can be seen as a form of recursive function. If you don't intend to write your own recursive function then may be you could do this
import operator as op
def silent_int(v):
try:
return int(v)
except ValueError:
return 0
xs = [1, 2, 'hello', '4']
print reduce(op.add, map(silent_int, xs))
Obviously the correct way of counting numbers should be using sum but with the recursion restriction you could use reduce or build your own trampoline!