Maybe this is a rather basic question, but I have seem this quite a lot on leetcode.
For example, one provides me a head of a singly linked list, than I define a pointer variable say:
curr = head
After I revert the head:
def revert(head):
prev, curr = None, head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev
Now I would obtain a singly linked list, in which case the order is reversed and prev is the head of it.
When the loop completes, of course the curr pointer points to None, I am wondering why the head gets modified to only include the last element.
And a more generic question is that, how come does the head gets updated automatically once the pointer is changed? Is this something that is achieved by the leetcode backend or something that is aware from a data structure perspective.
how come does the head gets updated automatically once the pointer is changed?
The short answer is that it doesn't get updated automatically.
In your example, the variable head points to a mutable object, meaning that modifications to it happen in place even when done from a function scope.
A regular python list is also mutable, to demonstrate let's take a regular python list and make some modifications to the contents.
def change_last_index(example):
lst = example # lst and example both point to the same mutable object
lst[-1] = 153
example = [1, 2, 3]
change_last_index(example)
print(example) # [1, 2, 153]
It's the same concept with the links/nodes of the linked list.
Lets create an example Node class/data structure that works with your function.
class Node:
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return f"{self.val} -> {self.next}"
And to demonstrate it's mutability we can create a function that creates a new node and assigns it to it's next attribute.
def set_next_node(node, value):
node1 = node # node1, node, and head all point to same object
node1.next = Node(value) # create new node and assign to head.next
head = Node(0)
print(head) # output: 0 -> None
set_next_node(head, 1)
# the function will create a new node with value of 1 set it to `head.next`
print(head) # output: 0 -> 1 -> None
In your revert function on the first line...
prev, curr = None, head
... the curr variable now points to the same object that the head points. So on the very first iteration of the while loop, head.next gets changed to None when the line curr.next = prev gets executed.
Then on the second iteration the very same line assigns head to the next attribute of a different node because prev gets reasigned to the head object when the line prev = curr gets executed.
Here is an example you can run using your revert function and the example Node class from above. I suggest running it and all the above code in an IDE/debugger so you can track the changes step by step, and run your own experiments, like making the linked list longer or changing the node values to strings.
head = Node(0)
head.next = Node(1)
head.next.next = Node(2)
print(head) # 0 -> 1 -> 2 -> None
def revert(head):
prev, curr = None, head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev
result = revert(head)
print(head) # 0 -> None
print(result) # 2 -> 1 -> 0 -> None
Hopefully you can see that nothing is happening automatically. All the modifications to head and all other elements of the linked list are direct results of the steps taken in your function.
Related
I'm trying to write a simple function that reverses a linked list in Python:
def reverse(head):
prev = None
nxt = head.next
head.next = prev
while nxt:
prev = head
head = nxt
head.next = prev
nxt = nxt.next
return head
The logic seemed fine when I thought through it.:
First move prev to head, then shuffle head forward so it points to the same node as nxt, before setting head.next to where it was. Finally moving nxt forward by one. Iterate until nxt reaches the end of the list.
However when I tried to reverse a linked list 0->1->2->3->4->5 with the following code:
def traverse(head):
while head:
print(head.val)
head = head.next
it repeatedly printed 0 and 1 endlessly.
As Michael pointed out, your code makes first and the second element in the linked list to point to each other.
The fix is pretty simple, here is what I did:
def reverse(head):
nxt = head.next
head.next = None
while nxt:
temp1 = nxt.next
temp2 = head
head = nxt
nxt.next = temp2
nxt = temp1
This code initially initially does the same thing as yours. The while loop however, uses 2 temporary variables temp1 and temp2 to hold the addresses of the variables surrounding nxt (on either sides). This prevents the conflict that happened in your code.
Hope this helps!
Cheers!
In the while-loop:
head = nxt # head and nxt now refer to the same node
head.next = prev # head.next now points to previous node but nxt.next does as well
nxt = nxt.next # this is same as: nxt = head.next therefore same as: nxt = prev
I am trying to solve the following problem on algoexpert:
Shift Linked List
Write a function that takes in the head of a Singly Linked List and an integer
k, shifts the list in place (i.e., doesn't create a brand new
list) by k positions, and returns its new head.
Shifting a Linked List means moving its nodes forward or backward and wrapping
them around the list where appropriate. For example, shifting a Linked List
forward by one position would make its tail become the new head of the linked
list.
Whether nodes are moved forward or backward is determined by whether
k is positive or negative.
Each LinkedList node has an integer value as well as
a next node pointing to the next node in the list or to
None / null if it's the tail of the list.
You can assume that the input Linked List will always have at least one node;
in other words, the head will never be None / null.
Sample Input
head = 0 -> 1 -> 2 -> 3 -> 4 -> 5 // the head node with value 0
k = 2
Sample Output
4 -> 5 -> 0 -> 1 -> 2 -> 3 // the new head node with value 4
The outline that the problem gives for the code is the following:
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
def shiftLinkedList(head, k):
#Write your code here.
pass
I suppose my background on linked list is very limited because from every resource I've read on linked lists, the general outline for it requires the node class and to have all the methods for rotating or shifting inside the LinkedList class.
I assume the head parameter for the function is going to be an integer that denotes the position of the list but how do I refer the head back to the original list? I already have the code written in my Thonny editor but I wrote the function inside the LinkedList class and simply called for it after making my list.
For example:
class Node:
def __init__self(data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def push(self, newhead):
newnode = Node(new_data)
newnode.next = self.head
self.head = newnode
list1 = LinkedList()
list1.head = Node(1)
e2 = 2
e3 = 3
list1.head.next = e2
e2.next = e3
Only once I've established my linked list can I create a method inside the class to shift or rotate it. Or am I wrong?
I tried creating a function the way the algo wanted, but I am still stuck. I think what I am really confused about is whether the argument head is an integer or a LinkedList?
Here is my complete attempt:
class Node:
def __init__(self, data):
self.data = data #assign data
self.next = None #initialize next as null
class LinkedList:
#function to initalize the linked list object
def __init__(self):
self.head = None
def printList(self):
temp = self.head
while(temp):
print(temp.data)
temp = temp.next
def moveToFront(self):
tmp = self.head
sec_last = None
if not tmp or not tmp.next:
return
while tmp and tmp.next:
sec_last = tmp
tmp = tmp.next
sec_last.next = None
tmp.next = self.head
self.head = tmp
def shiftList(head, k):
if not head:
return
tmp = head
length = 1
while(temp.next != None):
tmp = tmp.next
length += 1
if(k>length):
k = k%length
k = length - k
if(k==0 or k==length):
return head
current = head
cmt = 1
while(cmt < k and current != None):
current = current.next
cmt += 1
if(current==None):
return head
kthnode = current
tmp.next = head
head = kthnode.next
kthnode.next = None
return head
I assume the head parameter for the function is going to be an integer that denotes the position of the list
No, the head of a list is a LinkedList instance. So head.value is the value of the first node in the list, and head.next is a reference to the second node in the list.
In your attempt you have changed the definition of LinkedList from the original, and created another class, called Node, which really is what LinkedList is supposed to be (except that you call a property data instead of value). It is understandable that you did this, as you wanted to have a kind of container class for the whole linked list, and reserve a separate class Node for what concerns a single node in that list. But this code challenge does not work with such a container class.
It just works with one class that represents a node, but through its next member the whole list is inferred from it. Moreover, they want the function to return a reference to the node that has become the head of the list once the shifting has completed. This is a different way of working than with a container class, in which you would mutate the head member, and would not return anything: the caller would just access the list through the modified head member.
So, what you intended to do is not inherently wrong, it just is not the data structure that this code challenge is working with. You should go with it.
what I am really confused about is whether the argument 'head' is an integer or a linkedlist
It is a node, but the list is inferred from it. It can be a bit confusing that they call their class LinkedList and not Node, but it is a matter of how you look at it.
So here is how you could solve it:
First find out what the last node is in the list. For this you will have to start from the head (you have nothing else) and step through the list until you bump into its end. Also maintain a counter so that at the end you know how many nodes there are in the list.
Then create a link from that tail node to the head node, so that the list now becomes circular: it has no end anymore
Find out which node comes k steps before the tail node in this cyclic list. As it is not possible to walk backwards in a list, you should actually walk size-k steps forward, and so you need the size of the list which you determined in the first step.
The node after that found node should become the head node.
Make that node itself the new tail node: break the link it has with the node after it (the new head).
Return the new head reference
One more thing should be done to protect this code against huge values for k. Let's say the list has 6 elements, and k is 600, then of course it makes no sense to run over the cyclic list 600 times, as we can predict that you'll end where you started! 600 is a multiple of 6... So to make this efficient, you need to reduce k to a number that is less than the size of the list. The formula for knowing how many steps forward you need to take is -k % size.
Here is the implementation of this idea:
def shiftLinkedList(head, k):
if not head:
return # Nothing to do
# Determine size of the list, and its tail node
size = 1
tail = head
while tail.next:
size += 1
tail = tail.next
# Temporarily make the list cyclic
tail.next = head
# Find out where to cut the cycle
for _ in range(-k % size):
tail = tail.next
head = tail.next
# Cut the cycle
tail.next = None
return head
When reversing a singly linked-list, I am running into some trouble with variables changing.
def reverseList(self, head: ListNode) -> ListNode:
current = head
prev = None
while head:
current.next = prev # why does this line set head equal to [1] on the first iteration?
prev = current
head = head.next
current = head
return prev
The only time I change head is when assigning head = head.next. Changing current.next shouldn't affect head. So why does the head variable change on the commented line?
I tried the following code to reverse a linked list and is getting an infinite loop as an error. Can you pls tell me what's wrong in this approach.
def reverse(self):
temp = curr = self.head #curr refers to the next node
prev = None
while temp:
curr = temp.next #curr goes to the next node of temp
curr.next = temp #curr node points to its previous node temp
prev = temp #prev moves to the next node
temp = curr
#self.head.next = None
self.head = prev
There is a logic error in your method.
At the end of the first pass of the while loop:
curr (2nd element in list)
curr.next (1st element in list)
temp = curr = (2nd element in list)
In the second pass of the while loop. You expect to reach the 3rd element using temp.next. This is wrong because:
temp.next = curr.next = (1st element in list)
Leaving you to loop infinitely between the first and second element with no exit condition.
I will leave you to figure out the proper solution for this.
(Hint: temp should be assigned to the ??? element in the 1st pass)
Can I check
how do we remove a last node from a single linked list?
is it the same as how we remove the first node?
remove first Node
def deleteAtHead(self):
temp = self.head
self.head = self.head.next
delete temp
remove last Node
def deleteAtTail(self):
prev = None
temp = self.tail
self.tail= self.tail.prev
delete temp
You have to crawl back to the tail starting at the head.
The tail is the first node with no next: next is None.
Keeping track of the next-to-last (prev), you set its next to None.
def deleteAtTail(self): # remove_last would likely be a better name
""" removes the last element of the singly linked list
"""
temp = self.head
while(temp.next is not None):
prev = temp
temp = temp.next
prev.next = None
Setting prev.next to None removes the tail node (it will be garbage collected if there are no other references to it)
You cannot do remove operation in single-linked list with O(n) = c complexity because you don't have reference for previous list node. You will have to traverse to the end of the list remembering current and previous nodes and then you will have to trim previous node from the reference to it's next, which is the last node you are trying to remove. This will always have O(n) = n complexity for single-linked lists.
Because this is a singly linked list you will have to iterate through the list to get to the node just before tail. I think your function would simply need to be
def deleteAtTail(self):
temp = self.head
while(temp.next != None):
if temp.next == tail: #finds the node just before tail
break
temp = temp.next
temp.next = None
self.tail = temp
We set self.tail = temp because temp is equal to the node just before the end of the list. So we delete temp (by removing its last reference) and then set tail equal to node we earlier identified as the second to last node in the list as it will in fact be the new end of the list.
EDIT:
To address the concerns of some of the comments on this answer. It is good practice to maintain a head, tail and current pointer when dealing with a singly or doubly linked list. Knowing where tail is located is extremely valuable for making insertions into your list something that is computationally reasonable.
This means that your insertion method can go from this:
def insert(value):
temp = self.head
while(temp.next != None):
temp = temp.next
temp.next = Node()
temp.next.value = value
which has a time complexity O(n) to this:
def insert(value):
self.tail.next = Node()
self.tail.next.value = value
self.tail = self.tail.next
which is has a substantially more favorable time complexity of O(1).