Changing a singly linked list to a doubly linked list - python

I created a singly linked list function and my professor said for extra credit we can change it into a doubly linked list. I read a few things such as adding a prev_node function such as this.
class ListNode(object):
def __init__(self, item = None, prev = None, link = None):
'''creates a ListNode with the specified data value and link
post: creates a ListNode with the specified data value and link'''
self.item = item
self.prev = prev
self.link = link
However, I am confused on where to go from there. I know I need to add a tail as well as a head like I did here.
from DoublyListNode import ListNode
class LinkedList(object):
#--------------------------------------------------------------
def __init__(self, seq=()):
""" Pre: Creates a Linked List
Post: Creates a list containing the items in the seq=()"""
if seq == ():
# If there is no items to be put into the list, then it creates an empty one.
self.head = None
self.tail = None
else:
# Creates a node for the first item.
self.head = ListNode(seq[0], None)
# If there are remaining items, then they're added while keeping track of the last node.
last = self.head
for item in seq[1:]:
last.link = ListNode(item, None)
last = last.link
self.size = len(seq)
Could anyone tell me, ( NOT DO IT FOR ME), what I have to do in order to change my linkedlist into a doubly linkedlist? I know I have to reference now the tail as well as the head but I'm pretty stumped on how to do that.

I don't know Python, but here's a way to do it in Haskell:
import qualified Data.Vector as V
import Data.Vector (Vector, (!), fromList)
data DLL a = DLL {prevP :: Maybe (DLL a),
val :: a,
nextP :: Maybe (DLL a)}
list2DLL :: [a]->DLL a
list2DLL = vec2DLL . fromList
prev :: Int -> Maybe Int
prev i | i <= 0 = Nothing
prev i = Just (i-1)
next :: Int -> Int -> Maybe Int
next i lim | i < lim-1 = Just(i+1)
next _ _ = Nothing
vec2DLL :: Vector a -> DLL a
vec2DLL v = scaffold ! 0 where
scaffold = V.mapWithIndex go v
go i a = DLL (fmap (scaffold!) $ prev i)
a
(fmap (scaffold!) $ next i (length v))

I know I have to reference now the tail as well as the head but I'm pretty stumped on how to do that.
A great way to get started is to write out in English what it is you want to do:
I have a Linked List and I need to convert it to a doubly linked list. To do this, each node needs to keep track of a prev and next, and my list needs a head and tail node. When I create the list, head and tail should point at the same item. When I add a new item to my list, it should become the last node, its .prev element should be the previous .tail, and it should be the .next item for that item as well.
Once you have a description of what you want to do, then it should be relatively straightforward to translate that into code.

Related

understand head and pointers for a singly-linked list

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.

Finding lowest odd number in a Linked List in python

I am newly learning about Linked Lists and my assigment requires me to make a function that returns the lowest odd number...and I think I have done that in my get_min_odd function.
I believe my code is only checking the first and second element of the linked list so my question is how can I iterate through a linked list to find the lowest odd number? I would think a for loop but I don't understand what list I'm iterating through...which brings up another question, where are the elements in a linked list stored if not in a list, array, string, etc.?
class LinkedList:
def __init__(self):
self.head = None
#def.....
def add_all(self, a_list):
newNode = Node(a_list)
newNode.next = self.head
self.head = newNode
for i in a_list:
self.next = i
def add_all(self, a_list):
for i in range(len(a_list)):
self.add(a_list[i])
def get_min_odd(self):
lst = []
if self.head.data % 2 == 1:
lst.append(self.head.data)
elif self.head.next.data % 2 == 1:
lst.append(self.head.next.data)
else:
return 999
return min(lst)
my_list = LinkedList()
my_list.add_all([1, 4, -3, 6, 9, 2, 10001, 25, 19, 20])
print(my_list.get_min_odd())
A few issues:
You have two methods that are called add_all. This means the first one will be lost. You actually need an add method instead of that first add_all. And it should not take a list as value, but just a single numeric value. That method should not have to loop (Moreover, assigning i to the same next attribute over and over again makes no sense).
get_min_odd is indeed too much fixed on the first and second node. They might not even exist when the list is short or empty. To loop over a linked list, you need a name that first equals the head node, but then traverses along the next attributes to the next node, and then the next, ... until it reaches the end.
get_min_odd should not create a list with values. That defeats the purpose of a linked linked list. Instead that code should immediately update the minimum it has while traversing the linked list.
Here is the updated code for the two methods that needed correction:
def add(self, value): # name and arg name correction
newNode = Node(value)
newNode.next = self.head
self.head = newNode
and:
def get_min_odd(self):
result = None
node = self.head
while node: # as long as the end of the list is not encountered...
if node.data % 2 == 1:
# adjust the result to the minimum data encountered so far
result = node.data if result is None else min(result, node.data)
node = node.next # traverse to next node
return result

How to interact head of linkedlist outside the LinkedList class in Python?

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

How does the referencing work in python and how to create a sperate copy of linkedlist to compare with original linkedlist

class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
regularListHead = ListNode(-1)
regularListHead.next = head
reverseListHead = head
reverseListPrev = None
reverseListCurrent = reverseListHead
while reverseListCurrent != None:
reverseListNext = reverseListCurrent.next
reverseListCurrent.next = reverseListPrev
reverseListPrev = reverseListCurrent
reverseListCurrent = reverseListNext
reverseListHead = reverseListPrev
a = regularListHead
In my code, I am trying to convert the original list to reversed list and compare both for checking palindrome but when I do the operations to reverse the reverselist then original list is also updated. I am new to python can anyone let me know why this is happening and what could be done to achieve what I want
You only create one new node. All the other nodes are the nodes of your original list, and you update their next properties so the list is reversed.
So if you want to have both the original and the reversed list, you'll have to have two lists, each having their own nodes:
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None:
return
# Create reversed list
revHead = ListNode(head.val)
current = head.next
while current is not None:
revHead = ListNode(current.val, revHead)
current = current.next
# Compare original with the reversed one
while head is not None:
if head.val != revHead.val:
return False
head = head.next
revHead = revHead.next
return True
This is however not optimal: the comparison loop should only need to perform 𝑛/2 comparisons, not 𝑛.
You may even ask yourself why you would create a linked list for the reversed version: as it is only needed for the palindrome check, you might as well create a standard list, and then do the palindrome check with just that list, which is a piece of cake.
But more importantly, creating a new list (whether a linked list or normal "array" list) requires O(n) extra space. If you are interested in an algorithm for doing this without creating new nodes, then try to implement this:
Count the number of nodes in the list
Use that to identify the first node of the second half of the list. If the number of nodes is odd, let this be the node after the center node.
Apply an in-place list reversal algorithm on that second half. Now you have two shorter lists.
Compare the values in those two lists to see whether they are equal (ignore the center node if there was one). Remember the outcome (false or true)
Optionally repeat step 3 so the reversal is rolled back, and the list is back in its original state. (This is "nice" towards the caller of your function)
Return the result that was found in step 4.

Rotate a linked list k times (Python)

While practicing for my final in Python programming I ran into this question "def rotaten" of rotating k times. The problem says that k can range from 0 to any positive integer number (even greater than list size, if k < 0 raise ValueError
and it must execute in O( (n-k)%n ) where n is the length of the list. It also has the following warnings:
WARNING: DO NOT call .rotate() k times !!!!
WARNING: DO NOT try to convert whole linked list to a python list
WARNING: DO NOT swap node data or create nodes
The problem is that I'm not understanding the solution given. Is there an easier way to solve this problem? Thank you in advance
class Node:
def __init__(self,initdata):
self._data = initdata
self._next = None
def get_data(self):
return self._data
def get_next(self):
return self._next
def set_data(self,newdata):
self._data = newdata
def set_next(self,newnext):
self._next = newnext
class LinkedList:
def rotaten(self, k):
if k < 0:
raise ValueError
if self._size > 1:
m = k % self._size
if m > 0 and m < self._size:
current = self._head
for i in range(self._size - m - 1):
current = current.get_next()
chain_b = current.get_next()
old_head = self._head
old_last = self._last
self._last = current
self._last.set_next(None)
self._head = chain_b
old_last.set_next(old_head)
The easiest ways are forbidden
warning forbids implementing the singular rotation, then calling it repeatedly
warning forbids using native python structure - python has list built-in in basic collections list(), which can be then transformed to a deque, which can then be rotated by popping from the end and inserting the same node to the beginning.
warning is preventing you from making your life easier by creating some other nodes or worse - copying data. (copying data from one node to another would remove any advantage you had from storing the data into the lists in the first place)
The example solution is basically this:
Take the current first node(head of the list) and the tail node which is stored in the List structure. (the bookkeeping of the example list consists of holding the head and tail, the rest is done in the nodes themselves)
Find the k-th node - so that you rotate the whole list at once. This is where the list needs to be cut.
Add the last node as the new head, doing all the necessary reference-linking and unlinking. (Which is I guess the point of this question - to test if you understand the references. In C these would be pointers, python has the references implicitly.)
So as far as the linked lists go, this is the most straightforward solution with the requested O((n-k)%n) complexity.
Good luck with your pointers :-).

Categories

Resources