There is this question, 189 Rotate array on Leetcode. enter link description here Its statement is "Given an array, rotate the array to the right by k steps, where k is non-negative."
To understand it better, here's an example.
enter image description here
So, My code for this is
for _ in range(k):
j = nums[-1]
nums.remove(nums[-1])
nums.insert(0, j)
It cannot pass some of the test cases in it.
In the discussion panel, I found a code claiming it got submitted successfully that went like
for _ in range(k):
nums.insert(0, nums.pop(-1))
I would like to know, what is the difference between these two and why my code isn't able to pass some of the test cases.
If you do this on python shell [].remove.__doc__, you'll see the purpose of list.remove is to:
Remove first occurrence of value. Raises ValueError if the value is
not present.
In your code nums.remove(nums[-1]) does not remove the last item, but first occurrence of the value of your last item.
E.g.
If you have a list with values nums = [2, 4, 8, 3, 4] and if you do nums.remove(nums[-1]) the content of nums becomes [2, 8, 3, 4] not [2, 4, 8, 3] that you're expecting.
Just use slicing:
>>> def rotate(l, n):
... return l[-n:] + l[:-n]
...
>>> lst = [1, 2, 3, 4, 5, 6, 7]
>>> rotate(lst, 1)
[7, 1, 2, 3, 4, 5, 6]
>>> rotate(lst, 2)
[6, 7, 1, 2, 3, 4, 5]
>>> rotate(lst, 3)
[5, 6, 7, 1, 2, 3, 4]
In your code j = nums[-1] and you are trying to insert(0, nums[-1]).
In
for _ in range(k):
nums.insert(0, nums.pop(-1))
inserting other number - (0, nums.pop(-1))
Answer given by cesarv is good and simple, but on large arrays numpy will definitely perform better. Consider:
np.roll(lst, n)
The remove() method takes a single element as an argument and removes it's first occurence from the list.
The pop() method takes a single argument (index). The argument passed to the method is optional. If not passed, the default index -1 is passed as an argument (index of the last item).
If the test case has the same item before the last index then test case will fail.
Hence to correct your code replace remove with pop
for _ in range(k):
poppedElement = nums.pop()
nums.insert(0, poppedElement)
or to make it even concise -
for _ in range(k):
nums.insert(0, nums.pop())
Related
I wanted to create a list of lists
def func(m,n,l,t):
a=[i for i in range(m)]
b=[]
b.append(a)
a=swap(a)
b.append(a)
for i in b:
print(i)
def swap(l):
i,n=0,len(l)
while(i<n):
l[i],l[i+1]=l[i+1],l[i]
i+=2
return l
i created list a as my basis and append each modification i want to in the b list.
The problem is after i append the first list and modify it, the first one i inserted also changes the same as the second one i inserted.
the output of this code is this
[1, 0, 3, 2, 5, 4, 7, 6]
[1, 0, 3, 2, 5, 4, 7, 6]
what i want is when i performm the swap i dont want the first list to change
the output should look like this
[0, 1, 2, 3, 4, 5, 6, 7]
[1, 0, 3, 2, 5, 4, 7, 6]
if you could help, please thank you.
In place of append() method, you can try out extend() method which does the same as of append. but extend() method is mainly used for iterate items, as of in your case.
and also if you want to use only append, then take a copy of your variable first and append it, as you are changing in the first place, it is taking effect in the list too. So you can follow append into a list like, a.copy(). Hopes it helps
Please try out and share the feedback.
Thank you
Their are multiple code error you have done, for example:
you don't have to iterate over range object in order to get list, you could just...
a = list(range(m))
and also You don't have to run a while loop in order to iterate over two steps you could...
for i in range(0, len(l), 2):
see range for reference
also you didn't use any of n, l, t parameters of the func function.
THE ANSWER
when you pass the a variable to the swap function you are actually passing the class Object itself, so the swap function can change the value of the a variable (list class).
All you need is passing a copy of the a variable not the variable itself
a = swap(a.copy())
FINAL CODE
def swap(l):
for i in range(0, len(l), 2):
l[i],l[i+1]=l[i+1],l[i]
return l
def func(m):
a = list(range(m))
b = [a]
a = swap(a.copy())
b.append(a)
for i in b:
print(i)
func(8) # [0, 1, 2, 3, 4, 5, 6, 7], [1, 0, 3, 2, 5, 4, 7, 6]
I'm just wondering what would happen if a variable is the for loop (or while loop) statement. Will that variable only be evaluated the first time that statement is executed?
For example:
arr = [2, 3, 4]
for i in range(len(arr)):
arr.append(5)
Yes. Try it:
>>> arr = [2, 3, 4]
>>>
>>> for i in range(len(arr)):
... arr.append(5)
...
>>> arr
[2, 3, 4, 5, 5, 5]
As you can see, the loop has 3 iterations, because that's the value of len(arr) at the start of the loop.
This is the exact reason that it's generally discouraged to modify a list (or any other iterable) as you're iterating over it; the results may not always be intuitive since the iterator is set up at the start of the loop, and modifying the list may cause it to behave in unexpected ways (skipping over items, etc).
Will that variable only be evaluated the first time that statement is
executed?
Yes, if I understand your question correctly, the expression in the header of the for loop is only evaluated once. You can verify this by creating your own function that prints a message then returns the length of the array.
def my_fun(my_list):
print("Inside my function.")
return len(my_list)
arr = [2, 3, 4]
for i in range(my_fun(arr)):
arr.append(5)
print(arr)
Output:
Inside my function.
[2, 3, 4, 5, 5, 5]
As you can see, the message is only printed one time, so range(my_fun(arr)) must only be evaluated one time.
The code here is equivalent to :
arr = [2, 3, 4]
r = range(len(arr))
for i in r:
arr.append(5)
print(arr)
Here the call to the range function generates a new object of type range (https://docs.python.org/3/library/stdtypes.html#typesseq-range). The iteration through the for loop is therefore on the object r, and not on the object arr. The modification in the loop of arr has therefore no consequences on the loop itself, since this one is on the object r.
On the other hand, if we try the following code :
arr = [2, 3, 4]
for i in arr:
arr.append(5)
if len(arr) > 15:
break
print(arr)
We get the following result:
[2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
Here the for loop is applied directly on arr, and so its modification does impact the iteration performed by the for.
Trying to 'shuffle' a list with even number of items. Splitting the list, L in half and alternating taking an element from each.
I've tried pop, but that approach was unable to get me to a one-liner. (while loop) and I know there is likely some more succinct way to move through it.
The shuffle from random isn't exactly what I need, either – because that randomizes the entire order instead of alternating between the split list.
If a one-liner isn't possible, is that because it's more readable in a while loop?
def shuffle(L):
'''
I apologize in advance for how wet the following code is...
Example:
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
'''
return [L[:(len(L)//2)][0], L[(len(L)//2):][0], L[:(len(L)//2)][1], L[(len(L)//2):][1], L[:(len(L)//2)][2], L[(len(L)//2):][2]]
other attempt:
def shuffle(L):
x, L_first, L_next, L = len(L), L[:(len(L)//2)], L[(len(L)//2):], []
while len(L) != x:
L.extend([L_first.pop(0), L_next.pop(0)])
return L
Use slice assignment with a step:
def shuffle(l):
result = [None] * len(l)
# Put the first half of l in the even indices of result
result[::2] = l[:len(l)//2]
# Put the second half of l in the odd indices of result
result[1::2] = l[len(l)//2:]
return result
If I understand correctly, you could also opt for itertools.chain.from_iterable after zipping to get the alternating effect.
from itertools import chain
def shuff(l):
return list(chain.from_iterable(zip(l[:len(l)//2], l[len(l)//2:])))
Demo
>>> shuff(list(range(1, 7))
[1, 4, 2, 5, 3, 6]
One possibility (requires an external library but the recipe can also be found in the itertools-recipes section) is:
from iteration_utilities import roundrobin
def shuffle(L):
return list(roundrobin(L[:len(L)//2], L[len(L)//2:]))
This is probably slower than list assignment but it also works for arbitary amounts of iterables without problems and it doesn't require odd-sized-input handling:
>>> shuffle([1, 2, 3, 4, 5, 6, 7])
[1, 4, 2, 5, 3, 6, 7]
>>> shuffle([1, 2, 3, 4, 5, 6])
[1, 4, 2, 5, 3, 6]
I did some timings and #user2357112 definetly has the fastest solution but my solution is at least on the second place (note that this graph is in log-log, that means the difference in absolute terms may seem smaller than it really is!):
Disclaimer: I'm the author of that iteration_utilities library.
list comprehension with index calculation using modulo and floor division
[ L[(i + (i % 2)*len(L))//2] for i in range(len(L)) ] # for case of even len(L)
still one line for the general case
[ L[i//2 + (i % 2)*len(L)//2] for i in range(2*(len(L)//2)) ] + [L[-1]]*(len(L) % 2)
the index calc (i + (i % 2)*len(L))//2
can be parsed as adding
i//2 which gives 0, 0, 1, 1, 2, 2 ...
and
(i % 2)*len(L)//2 where (i % 2) alternates 0, 1 for even/odd i
0, len(L)//2, 0, len(L)//2, 0, len(L)//2 ...
sum:
0, len(L)//2, 1, 1 + len(L)//2, 2, 2 + len(L)//2 ...
Found two solutions. First one is very unpythonic (using python 2.7)
a = [1, 2, 3, 4, 5, 6] # intial array
Method One (using string magic):
[int(p) for p in ' '.join([str(x) + ' ' + str(y) for x, y in zip(a[:len(a) / 2], a[len(a) / 2:])]).split(' ')]
Method Two:
[i for Tuple in zip(a[:len(a) / 2], a[len(a) / 2:]) for i in Tuple]
Why is this not working? Actual result is [] for any entry.
def non_unique(ints):
"""
Return a list consisting of only the non-unique elements from the list lst.
You are given a non-empty list of integers (ints). You should return a
list consisting of only the non-unique elements in this list. To do so
you will need to remove all unique elements (elements which are
contained in a given list only once). When solving this task, do not
change the order of the list.
>>> non_unique([1, 2, 3, 1, 3])
[1, 3, 1, 3]
>>> non_unique([1, 2, 3, 4, 5])
[]
>>> non_unique([5, 5, 5, 5, 5])
[5, 5, 5, 5, 5]
>>> non_unique([10, 9, 10, 10, 9, 8])
[10, 9, 10, 10, 9]
"""
new_list = []
for x in ints:
for a in ints:
if ints.index(x) != ints.index(a):
if x == a:
new_list.append(a)
return new_list
Working code (not from me):
result = []
for c in ints:
if ints.count(c) > 1:
result.append(c)
return result
list.index will return the first index that contains the input parameter, so if x==a is true, then ints.index(x) will always equal ints.index(a). If you want to keep your same code structure, I'd recommend keeping track of the indicies within the loop using enumerate as in:
for x_ind, x in enumerate(ints):
for a_ind, a in enumerate(ints):
if x_ind != a_ind:
if x == a:
new_list.append(a)
Although, for what it's worth, I think your example of working code is a better way of accomplishing the same task.
Although the example of working code is correct, if suffers from quadratic complexity which makes it slow for larger lists. I'd prefer s.th. like this:
from nltk.probability import FreqDist
def non_unique(ints):
fd = FreqDist(ints)
return [x for x in ints if fd[x] > 1]
It precomputes a frequency distribution in the first step, and then selects all non-unique elements. Both steps have a O(n) performance characteristic.
this is the function for which is the unittest written for:
def swap_k(L, k):
""" (list, int) -> NoneType
Precondtion: 0 <= k <= len(L) // 2
Swap the first k items of L with the last k items of L.
>>> nums = [1, 2, 3, 4, 5, 6]
>>> swap_k(nums, 2)
>>> nums
[5, 6, 3, 4, 1, 2]
"""
this is the unittest code:
def test_swap_k_list_length_6_swap_2(self):
"""Test swap_k with list of length 6 and number of items to swap 2.
Also allow for the fact that there are potentially four alternate
valid outcomes.
"""
list_original = [1, 2, 3, 4, 5, 6]
list_outcome_1 = [5, 6, 3, 4, 1, 2]
list_outcome_2 = [5, 6, 3, 4, 2, 1]
list_outcome_3 = [6, 5, 3, 4, 1, 2]
list_outcome_4 = [6, 5, 3, 4, 2, 1]
valid_outcomes = [list_outcome_1, list_outcome_2, list_outcome_3, list_outcome_4]
k = 2
a1.swap_k(list_original,k)
self.assertIn(list_original, valid_outcomes)
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
the unittest code passes and I don't understand why since I think the only valid outcome would be list_outcome_1 judging by the docstring of swap_k...
First of all, the test can pass even if "valid_outcomes" contains more than what's valid. (In your opinion, list_outcome_1). It just means it sometimes won't fail when it should.
Second, I think the test is correct: the doc doesn't say that the first "k" items will be placed last in their original order, nor does it guarantee the same for the last "k" items. So any order of [1,2] could appear at the end of the list, and any order of [5,6] could appear at the beginning.
In general, if something is not guaranteed then I prefer not to assume it, even if it seems logical (a list is ordered, after all, so it's almost natural to assume that).
"Fixing" the unittest would also mean fixing the doc to guarantee order.
self.assertEqual(list_original, list_outcome_1)
and
self.assertIn(list_original, valid_outcomes)
both satisfies the test. Here you are testing whether the true outcome is in list of outcome which is true, so the test is valid.
However as per docstring
self.assertEqual(list_original, list_outcome_1)
would have been better as it checks the equality.