Issue with iterating through Linked List in Python - python-3.x

I am trying to implement a Linked List from scratch in Python 3.7 and after many attempts, I can't seem to get the print_values() method to print all node values in the order that is expected (sequential). At this point I am not sure if the problem is with the append() method or the print_values() method.
class Node:
def __init__(self, node_value):
self.node_value = node_value
self.nextNode = None
class SinglyLinkedList:
# methods that should be available: get_size, insert_at, append, remove,
# update_node_value
def __init__(self):
self.head_node = None
self.tail_node = None
self.size = 0
def get_list_size(self):
"""This returns the value for the size variable which get incremented
every time a new node is added.
This implementation is better because it has a running time of O(1)
as opposed to iterating through the
whole list which has a running time of O(n)"""
return self.size
def append(self, value):
new_node = Node(value)
if self.head_node is None:
self.head_node = new_node
self.size += 1
else:
while self.head_node.nextNode is not None:
self.head_node = self.head_node.nextNode
self.head_node.nextNode = new_node
self.size += 1
def print_values(self):
current_node = self.head_node
list_values = []
while current_node.nextNode is not None:
list_values.append(current_node.node_value)
if current_node.nextNode.nextNode is None:
list_values.append(current_node.nextNode.node_value)
current_node = current_node.nextNode
if list_values is not None:
print("Linked list: " + str(list_values))
else:
print("Linked List is currently empty.")
# Helper code below.
new_ll = SinglyLinkedList()
new_ll.append("alpha")
print(new_ll.get_list_size())
new_ll.append("beta")
print(new_ll.get_list_size())
new_ll.append("gamma")
print(new_ll.get_list_size())
new_ll.append("delta")
print(new_ll.get_list_size())
new_ll.append("epsilon")
print(new_ll.get_list_size())
new_ll.append("zeta")
print(new_ll.get_list_size())
new_ll.print_values()
And all I am getting in the output is this:
1
2
3
4
5
6
Linked list: ['epsilon', 'zeta']

Typically a singly linked list only keeps track of the head. (Not the tail as well). So self.tail_node = None is not normally used.
When working with a linkedlist or a tree it will make your life a lot easier to work with recursion instead of using loops. Loops work fine if you just want to go over the list but if you want to change it then I would recommend a recursive solution.
That being said the issue isn't with your print it is with your append.
You can NEVER move the head node. You must always make a pointer so this caused the issue:
self.head_node = self.head_node.nextNode
Fix:
def append(self, value):
new_node = Node(value)
if self.head_node is None:
self.head_node = new_node
self.size += 1
else:
temp_head = self.head_node
while temp_head.nextNode is not None:
temp_head = temp_head.nextNode
temp_head.nextNode = new_node
self.size += 1
Recursive Solution:
def append(self, value):
new_node = Node(value)
self.size += 1
self.head_node = self.__recursive_append(self.head_node, new_node)
def __recursive_append(self, node, new_node):
if not node:
node = new_node
elif not node.nextNode:
node.nextNode = new_node
else:
node.nextNode = self.__recursive_append(node.nextNode, new_node)
return node
That being said I didn't realize this till after I redid your print so here is a cleaner print method using a python generator that may help you.
Generators is something you can use with python that you can't normally use with other programming languages and it makes something like turning a linked list into a list of values really easy to do:
def print_values(self, reverse=False):
values = [val for val in self.__list_generator()]
if values:
print("Linked list: " + str(values))
else:
print("Linked List is currently empty.")
def __list_generator(self):
'''
A Generator remembers its state.
When `yield` is hit it will return like a `return` statement would
however the next time the method is called it will
start at the yield statment instead of starting at the beginning
of the method.
'''
cur_node = self.head_node
while cur_node != None:
yield cur_node.node_value # return the current node value
# Next time this method is called
# Go to the next node
cur_node = cur_node.nextNode
Disclaimer:
The generator is good but I only did it that way to match how you did it (i.e. getting a list from the linkedlist). If a list is not important but you just want to output each element in the linkedlist then I would just do it this way:
def print_values(self, reverse=False):
cur_node = self.head_node
if cur_node:
print('Linked list: ', end='')
while cur_node:
print("'{}' ".format(cur_node.node_value), end='')
cur_node = cur_node.nextNode
else:
print("Linked List is currently empty.")

I agree with Error - Syntactical Remorse's answer in that the problem is in append and the body of the while loop...here is the example in pseudo code:
append 0:
head = alpha
append 1:
//skip the while loop
head = alpha
next = beta
append 2:
//one time through the while loop because next = beta
head = beta
//beta beta just got assigned to head, and beta doesn't have next yet...
next = gamma
append 3:
//head is beta, next is gamma...the linked list can only store 2 nodes
head = gamma //head gets next from itself
//then has no next
next = delta
...etc.

Related

Insert new value Into SortedLinked List using Python?

I implement the following code, it works but it added the new values at the end of linked list Like, [1,2,3,6], value 4
the results are [1,2,3,6,4] which is wrong
the correct results is [1,2,3,4,6]
# Singly-linked lists are already defined with this interface:
class ListNode(object):
def __init__(self, x):
self.value = x
self.next = None
def insertValueIntoSortedLinkedList(head, valuetoInsert):
currentNode = head
while currentNode is not None:
if currentNode.next is None:
currentNode.next = ListNode(valuetoInsert)
return head
currentNode = currentNode.next
My Question how to modifie the insertValueIntoSortedLinkedList function
Thank you
I made some changes to insertValueIntoSortedLinkedList(). Basically, the error is that you don't compare if the value you're going to insert is smaller than current value (so you always insert the new value to end).
# Singly-linked lists are already defined with this interface:
class ListNode(object):
def __init__(self, x):
self.value = x
self.next = None
def insertValueIntoSortedLinkedList(head, valuetoInsert):
currentNode = head
while True:
# is current value greater than value we are going to insert?
if currentNode.value > valuetoInsert:
# yes, create new node with old value
next_node = ListNode(currentNode.value)
next_node.next = currentNode.next
# replace current value with new value (which is lower than old value)
currentNode.value = valuetoInsert
# set next node to new node we created previously (with old value)
currentNode.next = next_node
# we are done, return
return
# we are at the end, so break from while-loop
if currentNode.next is None:
break
currentNode = currentNode.next
# the valuetoInsert is greater than all values so far, so insert it to the end
currentNode.next = ListNode(valuetoInsert)
def print_list(head):
""" Helper method to print the list """
currentNode = head
while currentNode is not None:
print(currentNode.value)
currentNode = currentNode.next
l = ListNode(1)
insertValueIntoSortedLinkedList(l, 2)
insertValueIntoSortedLinkedList(l, 3)
insertValueIntoSortedLinkedList(l, 6)
insertValueIntoSortedLinkedList(l, 4) # this inserts value before 6
print_list(l)
Prints:
1
2
3
4
6

How to fix TypeError in Python when comparing values of two nodes of a Linked List

I am trying to compare the value of 2 nodes of 2 separate linked lists (the values will always be int), but it keeps throwing TypeError despite my best efforts to get around it.
Python version 3.7.4 on VS Code for Mac 1.37.0
This is code I wrote to merge two Linked Lists with values as int (which should be pre-sorted before being passed as arguments to the 'merge' method). The output needs to be a single sorted Linked List. I understand TypeError gets thrown if 2 values are being operated on while they are of incompatible type (e.g. str + int or int < object etc.). To get around it, I tried forced type-casting, but that then creates AttributionError, since object at one point is 'None' and it doesn't get typecast to int.
######## Definition of 'Node', 'LinkedList' #######
class Node:
def __init__(self, value):
self.value = value
self.next = None
def __repr__(self):
return str(self.value)
class LinkedList:
def __init__(self, head):
self.head = head
def append(self, value):
if self.head is None:
self.head = Node(value)
return
node = self.head
while node.next is not None:
node = node.next
node.next = Node(value)
#### Where I need help #####
def merge(list1, list2):
"""
Merge and sort two linked lists
Args:
list1, list2: two linked lists that need to be merged. They need to be pre-sorted before being passed as a argument.
Returns:
linked-list: Merged and sorted linked-list, a combination of list1 and list2
"""
merged = LinkedList(None) # create an empty linked list
if list1 is None:
return list2
if list2 is None:
return list1
list1_elt = list1.head # start with heads of the two lists
list2_elt = list2.head
while list1_elt is not None or list2_elt is not None:
# val1 = int(str(list1_elt.value))
# val2 = int(str(list2_elt.value))
# condition = val1 < val2
# print("List1 value: {} and List2 value: {}".format(list1_elt.value, list2_elt.value))
if list1_elt is None:
# print("List2 value: {}".format(list2_elt.value))
merged.append(list2_elt)
list2_elt = list2_elt.next
elif list2_elt is None:
# print("List1 value: {}".format(list1_elt.value))
merged.append(list1_elt)
list1_elt = list1_elt.next
elif list1_elt.value <= list2_elt.value:
# elif val1 <= val2:
# elif condition:
# print("List1 value: {}".format(list1_elt.value))
merged.append(list1_elt)
list1_elt = list1_elt.next
else:
# print("List2 value: {}".format(list2_elt.value))
merged.append(list2_elt)
list2_elt = list2_elt.next
return merged
##### TEST CODE TO CHECK #######
# First Test scenario
linked_list = LinkedList(Node(1))
linked_list.append(Node(3))
linked_list.append(Node(5))
second_linked_list = LinkedList(Node(2))
second_linked_list.append(4)
merged = merge(linked_list, second_linked_list)
node = merged.head
while node is not None:
#This should print 1 2 3 4 5
print(node.value)
node = node.next
In the merge method definition, I keep getting an error at the part:
elif list1_elt.value <= list2_elt.value:
I've tried to store the values in two separate variables with forced typecasting, but I get other errors. I am at my wit's end to understand what I am doing wrong here. This is the error I get:
File "filename.py", line 58, in merge
elif list1_elt.value <= list2_elt.value:
TypeError: '<=' not supported between instances of 'Node' and 'int'
You are setting value with a Node rather than int, at
self.head = Node(value)
node.next = Node(value)
Either you can send int to append function as :
linked_list.append(5)
or directly assign the Node
self.head = value
node.next = value
I would suggest the first method as you should send the value which you want to insert into the list and your append function is also expecting a value.

How yield works in Inorder traversal with recursive generator?

I am learning about recursive generators and found this program on web.
I undertand the recursive version of In-Order traversal but i am having trouble understanding recursive generator.
Specifically i am not able to understand
why 'yield x' is written in for loop?
Why 'yield x' is not collected in final list?
I have tried to dubug the generator and added the watch but i found that recursive call execute multiple time 'yield x' and it's not collected in final result.
class Tree:
def __init__(self, label, left=None, right=None):
self.label = label
self.left = left
self.right = right
def __repr__(self, level=0, indent=" "):
s = level*indent + repr(self.label)
if self.left:
s = s + "\\n" + self.left.__repr__(level+1, indent)
if self.right:
s = s + "\\n" + self.right.__repr__(level+1, indent)
return s
def __iter__(self):
return inorder(self)
def tree(list):
n = len(list)
if n == 0:
return []
i = n // 2
return Tree(list[i], tree(list[:i]), tree(list[i + 1:]))
# Recursive Generator
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Role of yield x inside for loop.
Maybe what you don't understand is how the generator works? The generator is different from the iterator, it is not directly calculating the worthy collection, it is dynamically getting all the values. If the result of inorder(t.left) is not traversed by a for loop, then yield inorder(t.left) will return the entire generator created for t.left. Because your entire inorder function is a generator.
Unfortunately, I have not been able to find a specific description document. This is a description based on my experience. Others are welcome to make corrections to my opinion. If someone can provide a specific official description, welcome to add
Not sure exactly what you're looking for, but perhaps it is something along these lines:
class Tree:
def __init__(self, label, left=None, right=None):
self.label = label
self.left = left
self.right = right
def __repr__(self, level=0, indent=" "):
s = level*indent + repr(self.label)
if self.left:
s = s + "\n" + self.left.__repr__(level+1, indent)
if self.right:
s = s + "\n" + self.right.__repr__(level+1, indent)
return s
def __iter__(self):
return inorder(self)
def tree(list):
n = len(list)
if n == 0:
return []
i = n // 2
return Tree(list[i], tree(list[:i]), tree(list[i + 1:]))
def inorder(t):
if t:
for x in t.left:
yield x
yield t.label
for x in t.right:
yield x
t = tree("ABCDEFG")
[print(i.label) for i in t]
which outputs:
A
B
C
D
E
F
G
With that code you could instead:
[print('----------\n', i) for i in t]
which outputs each hierarchical node in the tree, from A to G.
Edit: If you're asking how generator works, perhaps this example could be enlightening:
>>> def list2gen(lst):
... for elem in lst:
... yield str(elem) + '_'
...
>>> print(list2gen([1,2,'7',-4]))
<generator object list2gen at 0x000001B0DEF8B0C0>
>>> print(list(list2gen([1,2,'7',-4])))
['1_', '2_', '7_', '-4_']
If your debugger breaks multiple times at a yield, but those elements never materialize in the resulting generator, you'd have to attribute that to bugs in the debugger. I've not used one for Python in more than a decade; they used to be notoriously bug-infested. In Python the paradigm is "test is king," and "avoid manual debugging," but I disagree with that. (The only reason I don't is lack of great IDE's and debuggers.)

Singly linked list infinite loop when sharing elements between lists in Python

I implemented a linked list using Python 3.6, the linked list itself works well, but the problem is when I try to create the following example:
3 -> 1 -> 5 -> 9 ->
7 -> 2
4 -> 6 ->
It means I have 2 linked lists and in a certain point they share the same elements (7,2), the code of my linked list is the following:
class Element:
def __init__(self,value):
self.next = None
self.value = value
class LinkedList:
def __init__(self,head=None):
self.head = head
def append(self,new_element):
current = self.head
if current:
while current.next:
current = current.next
current.next = new_element
else:
self.head = new_element
def print_linked(self):
current = self.head
while current:
print(current.value, end=" ")
current = current.next
e1 = Element(3)
e2 = Element(1)
e3 = Element(5)
e4 = Element(9)
e1p = Element(4)
e2p = Element(6)
e1s = Element(7)
e2s = Element(2)
# Start setting up a LinkedList
ll = LinkedList(e1)
ll.append(e2)
ll.append(e3)
ll.append(e4)
ll.append(e1s)
ll.append(e2s)
l2 = LinkedList(e1p)
l2.append(e2p)
l2.append(e1s)
l2.append(e2s)
When I try to print any of the linked list, the program always enters in an infinite loop at the last element and only happens when I try to share the same element.
3 1 5 9 7 2 2 2 2 2 2 2 [...]
Am I missing something?. Help is appreciated. Thanks
Lets go over this:
ll.append(e2)
ll.append(e3)
ll.append(e4)
ll.append(e1s)
ll.append(e2s)
After this code run the internal state for the last item (e2s) is it points to nowhere.
But then:
l2.append(e2p)
l2.append(e1s)
l2.append(e2s)
This makes the last item point to itself (l2.append(e2s) appends without regard to cycles). You iterate the entire list and append the item even if it is already there.
Because the state is internal to the nodes (Element) you probably have two options:
Don't share state (make a copy)
Check for exitence of node and do not allow it to repeat in a list
You can raise an error in case of duplicate items:
def append(self,new_element):
current = self.head
if current is new_element:
raise ValueError('can not duplicate node %s on list' % new_element)
if current:
while current.next:
current = current.next
current.next = new_element
else:
self.head = new_element

trying to look for duplicates in an ordered list

So my programs function is to add the provided numbers to an ordered list, then search for the duplicates. I have a function that finds the duplicates of a number I asked and I know is a duplicate. Im trying to get the duplic function to read through the list and append the duplicates to a new list called seen.
class Node:
def __init__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setNext(self,newnext):
self.next = newnext
class OrderedList:
def __init__(self):
self.head = None
def add(self, item):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.getData() > item:
stop = True
else:
previous = current
current = current.getNext()
temp = Node(item)
if previous == None:
temp.setNext(self.head)
self.head = temp
else:
temp.setNext (current)
previous.setNext(temp)
def prntlist(self):
prnt = self.head
while prnt != None:
print(prnt.data, end=" ")
prnt = prnt.next
print()
def duplic (self):
currnt = self.head
seen = set()
uniq = []
for i in range(int(currnt.data)):
if i not in seen:
uniq.append(i)
seen.add(i)
print (seen)
def count(self, item):# function to count the value
count = 0
ptr = self.head
while ptr != None:
if (ptr.data == item):
count += 1
ptr = ptr.next
return count
mylist = OrderedList()
mylist.add(23)
mylist.add(23)
mylist.add(10)
mylist.add(14)
mylist.add(5)
mylist.add(31)
mylist.add(35)
mylist.add(37)
mylist.add(26)
mylist.add(23)
mylist.add(29)
mylist.add(18)
mylist.add(2)
mylist.add(25)
mylist.prntlist()
print('Count of 23 in list: ', mylist.count(23))
print('Duplicates in list: ', mylist.duplic())
I would like it to print:
mylist
Count of 23 in list: 3
Duplicates in list: {23,23}
You may need to be more clear in the question..
From what i understand, you could write the function this way:
def duplic (self):
currnt = self.head
seen = set()
for i in currnt.data:
if currnt.data.count(i)>1:
seen.add(i)
print (seen)
The uniq seems to be useless. And, a set object cannot have two same elements, so you can't have {23,23}. (you can write this, but it will result in {23} in the end)

Resources