Sorted Doubly Linked List Python - python

I'm having trouble understanding and implementing a Doubly Linked List. I can grasp most of the concepts of a Linked List. Here is my code so far (in Python)
*This is a purely academic exercise. I would normally use list and dict.
class DoublyNode(object):
"""A node of the SortedDoublyLL object.
DoublyNode(item, next=None, previous=None) -> a new DoublyNode with data as
its data, and next and previous as its neighbors."""
def __init__(self, data, next = None, previous = None):
"""Make a new DoublyNode from item, pointing to next and previous."""
self.data = data
self.next = next
self.previous = previous
class SortedDoublyLL(object):
"""A Sorted Doubly Linked List.
SortedDoublyLL() -> new SortedDoublyLL list that is empty
SortedDoublyLL(sequence) -> a SortedDoublyLL initialized from sequence's
items.
"""
def __init__(self, sequence = []):
"""Make a new SortedDoublyLL from the elements of sequence."""
if len(sequence) == 0:
self.head = None
self.tail = None
else:
cur_node = None
prev_node = None
sequence.sort()
sequence.reverse()
for element in sequence:
prev_node = cur_node
cur_node = DoublyNode(element, cur_node, prev_node)
self.head = cur_node
self.tail = DoublyNode(sequence[0])

Change your loop to
for element in sequence:
prev_node = cur_node
cur_node = DoublyNode(element, None, prev_node)
prev_node.next = cur_node
Because the line prev_node = cur_node precedes the call DoublyNode(element, cur_node, prev_node), you end up setting both the previous and next elements to the previous element so you are ending up with a linked list that just has two links to the previous element. So you might as well just pass None as the next parameter1 and then initialize it manually on the next pass of the loop. This has the advantage of leaving it as None on the last element of the list.
1 Using the name next as a parameter in the constructor will shadow the builtin function next which advances an iterator. You could use the name next_ which is the canonical thing to do. Using next as an attribute isn't a problem because that qualifies the name so that no shadowing will occur. It will mess up in some syntax highlighters though.

Related

Linked list- current vs. current.next when appending

Node:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def add() part one:
def add(self, newData):
if self.head is None:
self.head = Node(newData)
return
current = self.head
def add() part 2
Why does this work?
while current.next:
current = current.next
current.next = Node(newData)
And this doesn't?
while current:
current = current.next
current = Node(newData)
Aren't "current.next" in first one and "current" in second one same?
The only difference between the two loop codes is the last line, where you either assign to current.next or to current. Those do fundamentally different things.
When you assign to current.next, you're modifying an attribute of the Node object referenced by current. That change happens in place, so you'll see the change through other references (like the chain of nodes earlier in the list). That's what you want to happen.
In the modified version, when you assign to current, you're only rebinding the variable name in the local namespace. You're not changing any node! You could compare it to:
a = 1
b = a
b = 2
print(a) # still prints 1
Here b is current in the second version of your code. a is the next attribute of the last node of the list (which was called current.next in the working version of the code). Rebinding the local name b doesn't change the other reference to the same value.

Can't add node in circular linked list if nodes are made without the function

I need to add a node at the start of the circular linked list
I can add the node if the list is empty and I add every node using the function but if the list already has nodes then the function doesn't work
class Node:
def __init__(self,data):
self.data = data
self.ref = None
class CircularLinkedList:
def __init__(self):
self.head = None
self.tail = None
def atStart(self,newData):
newNode = Node(newData)
if self.head is None:
self.head = newNode
self.tail = newNode
self.tail.ref = newNode
if self.tail:
newNode.ref = self.tail.ref
self.tail.ref = newNode
def printList(self):
if self.head is None:
print("Circular linked list is empty")
n = self.head
while n:
print(n.data)
n = n.ref
if n == self.head:
break
cll = CircularLinkedList()
cll.head = Node("Monday")
e2 = Node("Tuesday")
e3 = Node("Wednesday")
cll.head.ref = e2
e2.ref = e3
e3.ref = cll.head
cll.atStart("Sunday")
cll.atStart("Monday")
cll.printList()
I need to change the head node using the atStart function and print the whole list but it prints the three nodes I have made (cll.head,e2,e3) or just the newNode using the atStart if list is empty
I tried debugging and seems like whenever the list is not empty it only takes the newNode data and doesnt set the ref of it head node and just prints the old three nodes
There are a few issues:
In atStart the if self.tail: condition will always be true. This is problematic, as only one of the two alternatives should be executed, not both. Yet when the first block executes (the case where the list was empty), self.tail gets a value, so this second if block will also execute (the case where the list is not empty). This should not be an if, but just a plain else: -- you want to cover all remaining cases (where the list was not empty).
In the case the list was not empty, the atStart method does not make the new node the head of the list, so the list becomes inconsistent.
The main program should not tinker with the head or ref attributes -- this should be left to the class code only. This leads to all kinds of problems. In this case the problem is that cll.tail remains None, and so the list is inconsistent and you cannot rely on the correct execution of the atStart method, because that assumes that tail was set correctly. You could add cll.tail = e3 to fix that particular issue, but again, this is bad practice. The logic for attaching nodes to eachother should be performed within the class code only.
Not a problem, but it is overkill for a circular list to have two node references (head and tail): in a non-empty list the head always follows the tail node, so there really is no need to remember what the head is when you have the tail. In short: just keep track of the tail.
Here is the adapted code:
class Node:
def __init__(self,data):
self.data = data
self.ref = None
class CircularLinkedList:
def __init__(self):
# No need for a head reference, as it is always coming after the tail
self.tail = None
def atStart(self,newData):
newNode = Node(newData)
if self.tail is None:
self.tail = newNode
else: # Must be ELSE to avoid executing both blocks
newNode.ref = self.tail.ref
self.tail.ref = newNode # new node always becomes the head
def printList(self):
if self.tail is None:
print("Circular linked list is empty")
n = self.tail.ref
while n:
print(n.data)
if n == self.tail:
break
n = n.ref
The main program corrected:
cll = CircularLinkedList()
# Don't alter the atttributes of the linked list or its nodes! Only use methods:
cll.atStart("Wednesday")
cll.atStart("Tuesday")
cll.atStart("Monday")
cll.printList()
print("----")
cll.atStart("Sunday")
cll.atStart("Monday")
cll.printList()
From www.geeksforgeeks.org:
The circular linked list is a linked list where all nodes are
connected to form a circle. In a circular linked list, the first node
and the last node are connected to each other which forms a circle.
There is no NULL at the end.
def atStart(self,newData):
if self.head is None:
# Initialize head and tail
newNode = Node(newData)
self.head = newNode
self.tail = newNode
else:
# Copy the old head node
old_head = self.head
# Make a new head node
self.head = Node(newData)
# Update the reference of the new head node to point the old_head
self.head.ref = old_head
# Now update the tail ref to point to the head (circular)
self.tail.ref = self.head
Explanation:
In this code if head and tail are empty, for the the first call of atStart method the head and tail will be the same, that's self.head is self.tail which evaluates to True. It's possible to implement this code a better way, where for example __init__ or atStart methods should except two values instead of one to initialize the head and tail together. Because in this way, the data of the head will reside in the tail uselessly even after adding a new head. If you call this:
cll.head.data == cll.tail.data
Now it's interesting what you will get. The first call to atStart method will evaluate to True, the second call may not evaluate to true. That's, whatever the first cll.head.data held will be there in cll.tail.data always. Effectively we're wasting cll.tail.data.
A better approach would be
def atStart(self, *newData):
if self.head is None:
# Initialize head and tail (each with its own data)
self.head = Node(newData[0])
self.tail = Node(newData[1])
# tail references head (circular)
self.tail.ref = self.head
#rest of code from above....
The second call to the atStart method will update the head of the circular list to point to the new head node (created to add the last value) and the tail reference will be updated to point back to the new head. Note that the ref attribute of the new head will be updated to reference the old head, it's a very important step or you will end up losing your old data.

Pointer in doubly linked list pointing at the wrong thing

I'm trying to convert a string into a doubly linked list. I want to be able to traverse it, find a match, and then insert another linked list at a certain position. However, I'm having trouble with my pointers. I have 'current.prev', 'current', and 'current.next' pointers that should all be pointing at the nodes in that order. Instead, my .prev and current pointers always point to the same thing. I need to be able to point at the different parts of the linked list, otherwise I won't be able to traverse back to a certain spot so I can insert a new linked list.
this is the code that creates doubly linked lists for me:
#-----------------------doubly-linked-lists-----------------------#
class NodeD:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLL:
def __init__(self):
self.head = None
def append(self, new_data):
new_node = NodeD(new_data)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
return
def printList(self, node):
while node:
print(node.data, end=""),
last = node
node = node.next
this is the code that makes a string into a linked list:
#making-linked-lists-from-strings#
def dnaToLL(dnainput):
dnaLL = DoublyLL()
head = NodeD(dnainput[0])
dnaLL.head = head
current = dnaLL.head
for i in range(1, len(dnainput)):
current.prev = current
current = current.next
current.next = NodeD(dnainput[i])
return dnaLL
I've been using this to test where the pointers are pointing:
dnaLL = dnaToLL(DNA)
dnaLL = dnaLL.head
print(dnaLL.prev.data +dnaLL.data+dnaLL.next.data)
but with this input:
S1 = "neho"
DNA = "imfinehowru"
I get this output:
iim
any idea where I've gone wrong? I am assuming it's where I've tried to make strings into doubly linked lists, but any other order that I put the pointers in gives me errors.
Thank you for your help!
The problem is that whe you do current.prev = current, you're saying "the previous node is the current node" - not the actual previous node.
Keep in mind that the first node in the list should have .prev = None. You'd only want to set .prev for future nodes. This modification should fix your dnaToLL() function:
def dnaToLL(dnainput):
dnaLL = DoublyLL()
head = NodeD(dnainput[0])
dnaLL.head = head
current = dnaLL.head
for i in range(1, len(dnainput)):
# note how the order is changed
current.next = NodeD(dnainput[i])
current.next.prev = current
current = current.next
return dnaLL

Python linked list remove duplicate

I am trying to write a code to remove duplicates from a sorted linked list "head". My code below always returns the last duplicate if the list ends with a duplicate. for e.g. [1,2,2,3,3] will return [1,2,3,3]. I can't figure out why. Does anyone have an idea?
class Solution(object):
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return head
l1=newhead=ListNode(head.val)
head=head.next
while head:
if head.val!=l1.val:
l1.next=head
l1=l1.next
head=head.next
return newhead
You should keep track of the leading node of each new value and keep fetching the next node until you get a node with a different value, at which point you assign that node as the next node for the leading node:
class Solution(object):
def deleteDuplicates(self, head):
node = head
while node:
lead = node
while node.next and node.next.val == lead.val:
node = node.next
node = lead.next = node.next
return head
Problem Solution
Create a class Node with instance variables data and next.
Create a class LinkedList with instance variables head and last_node.
The variable head points to the first element in the linked list while last_node points to the last.
Define methods append, get_prev_node, remove and display.
The method append takes a data item as argument and appends a node with that data item to the list.
The method get_prev_node takes a reference node as argument and returns the previous node. It returns None when the reference node is the first node.
The method remove takes a node as argument and removes it from the list.
The method display traverses the list from the first node and prints the data of each node.
Define a function remove_duplicates which takes a linked list as argument and removes duplicates from it.
The function remove_duplicates uses two nested loops to remove duplicate nodes.
Create an instance of LinkedList, remove duplicate nodes and display the list.
Program/Source Code
Here is the source code of a Python program to remove duplicates from a linked list.
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
self.last_node = None
def append(self, data):
if self.last_node is None:
self.head = Node(data)
self.last_node = self.head
else:
self.last_node.next = Node(data)
self.last_node = self.last_node.next
def get_prev_node(self, ref_node):
current = self.head
while (current and current.next != ref_node):
current = current.next
return current
def remove(self, node):
prev_node = self.get_prev_node(node)
if prev_node is None:
self.head = self.head.next
else:
prev_node.next = node.next
def display(self):
current = self.head
while current:
print(current.data, end = ' ')
current = current.next
def remove_duplicates(llist):
current1 = llist.head
while current1:
data = current1.data
current2 = current1.next
while current2:
if current2.data == data:
llist.remove(current2)
current2 = current2.next
current1 = current1.next
a_llist = LinkedList()
data_list = input('Please enter the elements in the linked list: ').split()
for data in data_list:
a_llist.append(int(data))
remove_duplicates(a_llist)
print('The list with duplicates removed: ')
a_llist.display()
Program Explanation
An instance of LinkedList is created.
The user is prompted to enter the data items for the list.
The function remove_duplicates is called to remove duplicates from the list.
The linked list is displayed.

Python - Linked List node comparision requires additional dereferencing

I am using Python 3.6.3. I am trying to write a simple Linked List in Python. Here is my code:
class Node(object):
"""Represents a node within a linked list"""
def __init__(self, data, next=None):
self.stuff = data
self.next = next
def __str__(self):
return str(self.stuff)
class LinkedList(object):
def __init__(self):
self.head = None
self.size=0
def append(self, data):
if not self.head:
self.head = Node(data)
return
else:
n=self.head
while n.next:
n = n.next
new_node = Node(data)
n.next = new_node
return
def insertAfter(self, data, newNode):
if not self.head:
return
else:
n=self.head
while n and n.stuff != data:
n = n.next
if not n:
return
else:
newNode.next = n.next
n.next = newNode
return
def printlist(self):
if not self.head:
print("List is empty")
return
else:
n = self.head
while n:
print(str(n))
n = n.next
return
ll = LinkedList()
ll.append(Node("1"))
ll.append(Node("2"))
ll.append(Node("3"))
ll.insertAfter("2", Node("2.5"))
ll.printlist()
I expect it to print:
1
2
2.5
3
But, instead it prints:
1
2
3
After debugging, I realized that changing this line in the insertAfter method:
while n and n.stuff != data:
to:
while n and n.stuff.stuff != data:
prints the expected output.
I don't understand why it is doing this.
Please help.
Thank you
The problem is you are not adding 1, 2, 3 to the LinkedList and letting it create a Node to wrap each. You are adding a Node whose stuff variable is 1, 2, and 3. When you call the append method to add each Node, they get wrapped in another Node by the append method. Therefore you need to call Node.stuff.stuff to access the actual element that is stored.
Look at what your append method is actually doing. It accepts some parameter data and then creates a Node with either the line self.head = Node(data) or the line new_node = Node(data), depending on if the LinkedList already has a head Node or not.
Change ll.append(Node("1")) to just ll.append("1"). Alternatively, change your append method to assume it is being passed a Node object. The first solution is much more preferable as the Node class has little use outside the context of the LinkedList class and the LinkedList doesn't work unless it is populated with Nodes anyways; it seems like extra work to make the user of the LinkedList class have to create a Node every single time.
EDIT: Also what is the reason you are passing in numbers as strings? You don't need to put 1, 2, 2.5, and 3 in quotes unless you specifically want them to be strings- but if so, why?

Categories