Pointer in doubly linked list pointing at the wrong thing - python

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

Related

unable to access next node for Linked List while reversing a Linked List in python

I am a bit new to python and I have seen the correct solutions to the reversing the linkedlist problem but I wanted to know why my solution does not work. In particular, reverse function stays inside the while loop for the code below because of "new_list.head.next=prev" line
class Node:
def __init__(self, value):
self.value = value
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def append(self, value):
if self.head is None:
self.head = Node(value)
return
node = self.head
while node.next:
node = node.next
node.next = Node(value)
def __iter__(self):
node = self.head
while node:
yield node.value
node = node.next
def __repr__(self):
return str([v for v in self])
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
node = linked_list.head
new_list.head = node
while node.next:
prev = node
node = node.next
new_list.head = node
new_list.head.next = prev
return new_list
if __name__ == "__main__":
a = LinkedList()
b = [1,2,3,4,5]
for item in b:
a.append(item)
print a
c = reverse(a)
print c
If you tag your question with Python3 please make sure it runs in python 3.
The reason is because you are mixing up points and creating an infinite loop. Print the value and it may help you find the bug. I am going to use the values to point out the issue.
while node.next:
# First node that comes in value = 1
print(node.value) #
prev = node # prev = 1
node = node.next # node = 2
new_list.head = node # You are setting the head = 2 (i.e. head = node.next)
new_list.head.next = prev # You are setting next of head = 1 (i.e. head.next = node.next.next)
# however this also set node.next.next = 1
# So going into the next loop: node.value = 2 and node.next.value = 1
Because of this pointer confusion you are forever looping between your first and second node.
This is how your reverse can look:
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
node = linked_list.head
new_list.head = Node(node.value)
while node.next:
node = node.next
prev_head = new_list.head
new_list.head = Node(node.value)
new_list.head.next = prev_head
return new_list
With it I got desired output of print c: [5, 4, 3, 2, 1]
General advise: create new Node instead of assignment to node in initial list.
It's a little easier to reason about this (at least to me) if you think about two references:
• One to the remaining part of the original list you haven't seen
• One to the head of the new list
At each iteration move the remaining up and set the old remaining to the head of the new list. Order is important here — as you've seen it's easy to accidentally change next on two different variables that are pointing the same node if you're not careful:
def reverse(linked_list):
new_list = LinkedList()
if linked_list is None:
return new_list
remaining = linked_list.head
while remaining:
prev_head = new_list.head # the old head becomes the first link
new_list.head = remaining # new head becomese the first remaining
remaining = remaining.next # move remaing one up the chain
new_list.head.next = prev_head # point the new head to the previous
return new_list

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?

Trouble with forward linked list

I've implemented a backwards linked list successfully, but I'm trying to find out how to implement a forwards linked list, and I can't seem to figure out how to do it. The problem is in my insert method. I'm trying to keep track of the previous node, and point it to the newly created node, but I'm missing something.
class Node(object):
def __init__(self, data=None, next_node=None):
self.data = data
self.next_node = next_node
def set_next(self, new_next):
self.next_node = new_next
def get_data(self):
return self.data
def get_next(self):
return self.next_node
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def insert(self, data):
previous_node = self.head
current_node = self.head
new_node = Node(data)
if previous_node is not None:
previous_node.set_next(new_node)
previous_node = self.head
It's not clear what your insert method is supposed to do. If it is meant to insert at the beginning of the list (before head), then you should be setting new_node.set_next(previous_node) and self.head = new_node. If you meant to append to the end of the list, then you will need to scan through the list until you find the node with current_node.get_next() == None and do current_node.set_next(new_node).
Since this looks like homework, I don't want to straight out give you the answer. I will provide some pseudocode to get you started though
def insert(value):
let current = head
until current.next_node == None:
let current = current.next_node
let current.next_node = Node2(value)
Try this.
If the list is empty, set the head, and return. Else, loop "forward" through all the elements until you hit a None node. Once you do, set the appropriate next_node value.
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def empty(self):
return self.head is None
def insert(self, data):
new_node = Node(data) # Want to insert this
if self.empty(): # Is this list empty?
self.head = new_node # Yes? Then add the head
else: # No? There are elements in the list
current_node = self.head # Save a moving reference
while not current_node.get_next() is None: # While there are forward nodes
current_node = current_node.get_next() # Move the reference forward
current_node.set_next(new_node) # Set the last node, thus inserting at the "end"
Let's write out what this does:
def insert(self, data):
previous_node = self.head # set to head of list
current_node = self.head # not used elsewhere
new_node = Node(data) # make a new node with the given data.
if previous_node is not None: # if the list isn't empty
previous_node.set_next(new_node) # point the list head to the new node
previous_node = self.head # does nothing; this was already the list head
Bottom line: this creates a new node and makes it the "next" node of the list head. That's all this does; your list will never be more than 2 nodes long. In fact, your list can't ever get a first node, because you have no code to insert into an empty list.
To fix this, you need a clear, adaptable algorithm, such as:
Create a new node.
Link the existing list behind the new node:
Set new.next to the current head node.
Point the list head to the new node.
Note that this works even with an empty list.
If you want a list that adds to the end, then also maintain a self.tail attribute that points to the last node in the list. Then your insert (append) looks like this:
Create a new node.
Link the new node to the existing tail:
if tail exists:
Set tail.next to the new node.
else:
Set head to the new node
Set tail to the new node.
Is this clear enough to implement?

How to delete a given node from a linked list using Python

I am trying to learn linked list in Using python,
Could someone please guide me how to delete a particular give node from a linked list?
#!/usr/bin/python
class Node(object):
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
def print_list(node):
while node:
print node,
node = node.next
print
def delete_node(node, node_to_remove):
if first_node == None:
return
pass
# way of creating linked list
def create_linked_list1(n):
linked_list = Node(1)
head = linked_list
for i in range(1, n):
head.next = Node(i)
head = head.next
return linked_list
node1 = create_linked_list1(10)
print_list(node1)
The obvious solution is this:
def delete_by_index(node, index):
for _ in range(index):
prev_node, node = node, node.next
prev_node.next = node.next
However, this won't be able to delete the first node. In order to do that, you need to make it return the new list head, which will normally be the old list head, but will instead be the old second node in the case where you deleted the head. So:
def delete_by_index(node, index):
if not index:
return node.next
head = node
for _ in range(index):
prev_node, node = node, node.next
prev_node.next = node.next
return head
It should be obvious how to simplify this, and you should do so.
Another option is to factor out the "searching" and "deleting" parts. Write an nth function (this should be easy), then you can do this:
def delete_by_index(node, index):
if not index:
return node.next
prev_node = nth(node, index-1)
prev_node.next = prev_node.next.next
return node
If you know about recursive functions, you might also want to figure out how to write either delete_by_index or nth recursively (it's one of the easiest recursive functions to write).
You may also want to trap the error caused by deleting, say, the 15th node of a 10-node list and make it something nicer. Figure out what error you would get in that case (or, if you can't figure it out, just run it and see), then try/except that, and raise an IndexError instead.
While you're at it, you may want to add a delete_by_data(node, data) function and maybe a delete_by_identity(node, child_node) for further practice.
Assume the following singly linked list with a pointer, head, to the first node
head ⇢ n0 → n1 → … → n i - 1 → n i → n i + 1 → … → n N-1 → None
def delete(i):
if i == 0: # there is no prev node, increment head
head = head.next
else:
prev = get_node(i-1) # find prev node, node[i-1]
prev.next = prev.next.next # remove node i
Because this is a singly linked list get_node(i-1) will have to start at head and increment i-1 times to find the node.
NOTE: If this was a doubly linked list, given node[i] you can find node[i-1] using node[i].prev.
v-> w-> x->y> z and if we want to delete x so that new Linked List is v -> w-> y-> z and we have access to only x
position of the next node to x i.e. position of the Node Y
next_node = x.next
swapping the Y to X and then deleting Y `x.data = next_node.data
x.next = next_data.next
Deletion of a node using python in single linked list.
def delete(self,data):
if self.head.data==data:
temp=self.head.next
del self.head
self.head=temp
else:
p=self.head
while p.next.data!=data:
p=p.next
temp=p.next.next
del p.next
p.next=temp
Delete by value:
def delete(head, val):
if head is None:
return None
prev = head
temp = head.next
while temp is not None:
if temp.val == val:
prev.next = temp.next
return val
else:
prev = temp
temp = temp.next
return None

Categories