I have got stack with a linked list. When I call list.push() it works fine, but it does nothing when I do the same in funtion init(). What could be the problem?
class Node:
def __init__(self, data):
self.data = data
self.nextNode = None
def __str__(self):
return str(self.data)
class linkedList:
def __init__(self):
self.head = None
self.end = None
def isEmpty(self):
return self.head == None
def push(self, item):
new_node = Node(item)
if not self.head:
self.head = self.end = new_node
else:
new_node.nextNode = self.head
self.head = new_node
#doesnt work
def init():
list = linkedList()
list.push("a")
init()
print(list.isEmpty()) #True
#works
list = linkedList()
list.push("a")
print(list.isEmpty()) #False
Outside a function, you cannot access a local object you create inside function. Returning it makes it available:
def make_list():
my_list = linkedList()
my_list.push("a")
return my_list
my_list = make_list()
print(my_list.isEmpty())
Don't use the name list for your objects as it shadows the builtin list.
You need to RETURN the object you make within the function, because that object 'dies' when function ends, so there is no object there for you to look into. That's why you get empty true.
here is how you make it work:
def make():
aList = linkedList()
aList.push("a")
return aList
then you would store whatever is return from that object:
some_list = make() # storing the linkedlist that make() returns into
# variable called 'some_list'
print(some_list.isEmpty()) # True
Related
Intuitively, using __iter__ and __next__ seems likely to be significantly for efficient, but I curious of the technical specifics of why, and how one maybe go about benchmarking to measure and compare the space and runtime of these?
A common case I can think of is a Linked List,
The following is an implementation illustration the use of __iter__ and __next__:
I have tried various benchmarking tools without much success being able to generate data to more closely analyze they're internals; specifically, how exactly the magic methods function, and if they're truly as significantly more efficient as I surmise they are.
class Node(object):
def __init__(self, value: int, next_node):
self.value = value
self.next = next_node
def __repr__(self):
return str(self.value)
class LinkedList(object):
def __init__(self, head=None):
self.head = None
self._current = None
def __repr__(self):
return '<{} {}>'.format(type(self).__name__, self.head)
def __iter__(self):
self._current = self.head
return self
def __next__(self):
if self._current is None:
raise StopIteration
current, self._current = self._current, self._current.next
return current
#property
def is_empty(self):
return self.head is None
def prepend_node(self, valuetoadd: int):
self.head = IntNode(valuetoadd, self.head)
Compared to the following:
class ListNode:
"""
A node in a singly-linked list.
"""
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __repr__(self):
return repr(self.data)
class SinglyLinkedList:
def __init__(self):
"""
Create a new singly-linked list.
Takes O(1) time.
"""
self.head = None
def prepend(self, data):
"""
Insert a new element at the beginning of the list.
Takes O(1) time.
"""
self.head = ListNode(data=data, next=self.head)
def find(self, key):
"""
Search for the first element with `data` matching
`key`. Return the element or `None` if not found.
Takes O(n) time.
"""
curr = self.head
while curr and curr.data != key:
curr = curr.next
return curr # Will be None if not found
def remove(self, key):
"""
Remove the first occurrence of `key` in the list.
Takes O(n) time.
"""
# Find the element and keep a
# reference to the element preceding it
curr = self.head
prev = None
while curr and curr.data != key:
prev = curr
curr = curr.next
# Unlink it from the list
if prev is None:
self.head = curr.next
elif curr:
prev.next = curr.next
curr.next = None
Here is my code. I created a linked list manually to check if my method works or not. But the output I get is nothing. There is no output
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
node1=Node(2)
node2=Node(4)
node3=Node(5)
node1.next=node2
node2.next=node3
a=node1
class MyList():
def __init__(self):
self.head=Node()
def isEmpty(self,a):
return self.head.next is None
hello=MyList()
print(hello.isEmpty(a))
In case you want to add data to LinkedList, you need to set the head of the list manually.
This code is probably what you want:
class Node:
def __init__(self, data=None):
self.data = data
self.next = None
node1=Node(2)
node2=Node(4)
node3=Node(5)
node1.next=node2
node2.next=node3
class MyList():
def __init__(self):
self.head=Node()
def isEmpty(self):
return self.head.data is None
hello=MyList()
print(hello.isEmpty())
new_hello = MyList()
new_hello.head=node1
print(new_hello.isEmpty())
Output
True
False
You never set the head node to point to another node in the way you're currently doing it (your head and "a" aren't actually the same node here). Pass in another variable as a node object to change this.
a = node1
class MyList():
def __init__(self, head):
self.head = head
def isEmpty(self):
return self.head is None # a linked list with a head is technically not empty
new_hello = MyList(a)
print (new_hello.isEmpty())
personally I would add an add_node(self, value) method and keep track of the end node as well, instead of doing it the way you are
I have a LinkedList class that has close to 200 lines of code. I would like to make a new class LLCircular(LinkedList) by always making sure that any myLL.tail.next is myLL.head. I believe I would need to update append(), push(), remove(), etc. accordingly. Is there a way I can do this to keep the original LinkedList class intact? Maybe a decorator or some dunder method?
For brevity's sake, if reading the code, my push() method is just the inverse of append(). I also have a pop() and remove() method, which would need to be updated, if I just rewrite those methods. As I am trying to avoid that approach, I am not posting that part of the code.
class LinkedListNode:
def __init__(self, value, nextNode=None, prevNode=None):
self.value = value
self.next = nextNode
self.prev = prevNode
def __str__(self):
return str(self.value)
class LinkedList:
def __init__(self, values=None):
self.head = None
self.tail = None
if values is not None:
self.append(values)
def __str__(self):
values = [str(x) for x in self]
return ' -> '.join(values)
def append(self, value=None):
if value is None:
raise ValueError('ERROR: LinkedList.py: append() `value` PARAMETER MISSING')
if isinstance(value, list):
for v in value:
self.append(v)
return
elif self.head is None:
self.head = LinkedListNode(value)
self.tail = self.head
else:
''' We have existing nodes '''
''' Head.next is same '''
''' Tail is new node '''
self.tail.next = LinkedListNode(value, None, self.tail)
self.tail = self.tail.next
if self.head.next is None:
self.head.next = self.tail.prev
return self.tail
'''class LLCircular(LinkedList):'''
''' ??? '''
Test Code:
foo = LinkedList([1,2,3])
foo.tail.next = foo.head #My LL is now circular
cur = foo.head
i = 0
while cur:
print(cur)
cur = cur.next
i +=1
if i>9:
break
What you want is to call your LinkedList base class functions using the super keyword, and then add the slight modifications to the LLCircular class function, i.e:
class LLCircular(LinkedList):
def append(self, value=None):
super(LLCircular, self).append(value)
# In addition to having called the LinkedList append, now you want
# to make sure the tail is pointing at the head
self.tail.next = self.head
self.head.prev = self.tail
If it is "circular" it won't need a tail or head, would it?
Nor "append" makes sense - insert_after and insert_before methods should be enough - also, any node is a reference to the complete circular list, no need for different objects:
class Circular:
def __init__(self, value=None):
self.value = value
self.next = self
self.previous = self
def insert_after(self, value):
node = Circular(value)
node.next = self.next
node.previous = self
self.next.previous = node
self.next = node
def insert_before(self, value):
node = Circular(value)
node.next = self
node.previous = self.previous
self.previous.next = node
self.previous = node
def del_next(self):
self.next = self.next.next
self.next.previous = self
def __iter__(self):
cursor = self.next
yield self
while cursor != self:
yield cursor
cursor = cursor.next
def __len__(self):
return sum(1 for _ in self)
class Node(object):
def __init__(self, data = None, next = None):
self.data = data
self.next_node = next
def get_data(self):
return self.data
def get_next(self):
return self.next_node
def set_next(self, new_next):
self.next_node = new_next
class LinkedList(object):
def __init__(self, head = None):
self.head = head
def insert(self, data):
new_node = Node(data, None)
if self.head is None:
self.head = new_node
else:
new_node.next = self.head
self.head = new_node
def print_list(self):
temp = self.head
while temp!=None:
print(temp.data)
if temp.next != None:
temp = temp.next
s = LinkedList()
s.insert(3)
s.insert(4)
s.insert(5)
s.insert(6)
s.print_list()
I always get this Node object has no attribute next in the console. It prints the linkedlist but how do I get rid of that warning. What extra condition should I put?
In LinkedList, you keep accessing the node’s next node using the property name next but in the Node type, you actually defined the next pointer to be called next_node.
So either change the Node definition so the pointer is just called next, or change the usages in LinkedList to refer to node.next_node instead of just node.next
The problematic part is in the function remove. Even after del is called, the relevant node is not removed from the Linked List. Did I misunderstand something about del?
class Node:
def __init__(self, val):
self.val = val
self.next = None
def add(self, val):
if not (self.next):
self.next = Node(val)
else:
self.next.add(val)
def remove(self, val):
if self.val == val:
if self.next:
self.val = self.next.val
self.next = self.next.next
else:
del self # this doesn't remove the node from linked list
else:
if self.next:
self.next.remove(val)
else:
print "no such val found %d" % val
def __str__(self):
output = []
while self is not None:
output.append(str(self.val))
self = self.next
return " -> ".join(output)
head = Node(1)
head.add(2)
print head
head.remove(3)
head.add(3)
head.add(4)
print head
head.remove(3)
head.remove(4)
print head
The statement del self only removes the name self from the local scope (and decrements the reference count). It has no effect on the other references to it nor to those objects.
To remove the node from the linked list, you must update the node(s) that refer to it. Since you have a singly linked list, you must traverse the list from the beginning to find the node whose node.next == self, then change it to node.next = self.next to remove self from the sequence of links.