I am a rookie python programmer. I see the leetcode's definition of a linked list below. I got 2 questions for this concept, any help would be appreciated. Thanks in advance
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
Q1 Just wonder what is the type of the "self.next", I know in C++, it should be a pointer that represents the address of the next node. But python does not have that type, so I am confused what type "next" is.
Q2 Some tell me next is just a name. If that is the case, I run the code below,
head =ListNode(1)
print sys.getsizeof(head)
head.next = ListNode(2)
print sys.getsizeof(head)
first the head.next is 'None', and then it is assigned to another ListNode type,
but I get the same size of head before and after this change, which I think the size of head should be larger since one of its member (next) is changed from None type to ListNode type. I am just confused about this, thank you so much!
PS. In my understanding, if I keep adding new nodes to the linklist, the head will be larger and larger since there are more and more 'nested' member 'next', just point out where I get wrong, thanks.
Question 1:
Python variables are dynamically typed. (i.e. a variable could hold an int, and then hold a list, and then any other arbitrary object, etc).
In your case, Head.next starts by referencing, None a NoneType object.
After you assign it a different value (ListNode(2)), the Head.next now references the newly created ListNode object.
Question 2:
Why doesn't the size change.
I'm not an expert on how python's sys.getsizeof works, but from what I can gather, is that List.next in both cases is a reference variable (i.e. a variable that references some other object). The size doesn't change because sys.getsizeof finds the size of the object's variables. Where Head.next is just a reference to some other object in both cases.
See, How do I determine the size of an object in Python?, for more complete answers on how sys.getsizeof works.
My interpretation of a linked list.
class LinkedList(object):
class Node(object):
def __init__(self, val=None, next=None, previous=None):
self.val = val
self.next = next
self.last = previous
def __init__(self):
self.length = 0
self.start = None
self.end = None
def append(self, value):
self.length += 1
if not self.start:
self.start = self.Node(value)
else:
if not self.end:
self.end = self.Node(value)
self.end.previous = self.start
self.end.next = self.start
self.start.next = self.end
else:
end = self.Node(value)
self.end.next = end
end.previous = self.end
self.end = end
self.end.next = self.start
def prepend(self, value):
self.length += 1
if not self.start:
self.start = self.Node(value)
else:
n = self.Node(value, self.start, self.end)
self.start.previous = n
self.start = n
def __len__(self):
return self.length
def __iter__(self):
self.position = 0
return self
def next(self):
self.position += 1
if self.position-1 >= len(self):
raise StopIteration
if self.position-1 == 0:
return self.start
cnt = 0
n = self.start
while cnt<self.position-1:
n = n.next
cnt += 1
return n
def __getitem__(self, index):
if index == 0:
return self.start
if index == -1:
return self.end
cnt = 0
n = self.start
while cnt<index+1:
n = n.next
cnt += 1
return n.val
def __repr__(self):
return repr(tuple(x.val for x in self))
l = LinkedList()
l.append(4)
l.append(5)
l.append(3)
l.prepend(0)
print l
print l[1]
Related
I am just experimenting with a hybrid model of Linked List with some modifications. I have already implemented object.delete_node(index) which just link the next node as it is in vanilla Linked Lists. Now, and want to implement del object[index] which does the same function as object.delete_node(index). How could I implement it? It is implemented in list and dict in Python. Which method is responsible for the same?
Below is the code for my LinkedList which works pretty well.
class Node:
def __init__(self, data = None, next_pointer = None):
self.data = data
self.next_pointer = next_pointer
def __str__(self):
return self.__repr__()
def __repr__(self):
return str(self.data)
class LinkedList:
def __init__(self):
self.head = Node()
self.length = 0
def insert_node(self, data):
new_node = Node(data) # node to be inserted
current_node = self.head
while current_node.next_pointer != None: # it'll only stop at the last node which is obviously empty
current_node = current_node.next_pointer # bring out next pointer
current_node.next_pointer = new_node
self.length += 1
def delete_node(self, index):
if self.length == 0: raise ValueError(f"Can not delete from empty Linked List")
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
count = 0
current_node = self.head
while count < index:
current_node = current_node.next_pointer
count += 1
current_node.next_pointer = current_node.next_pointer.next_pointer if current_node.next_pointer.next_pointer != None else None
self.length -= 1
def _slice_return(self, slice_index):
'''
Implement slicing Operation just like in Python Lists and Strings
'''
index = slice_index.start
stop = min(slice_index.stop, self.length -1)
step = 1 if slice_index.step == None else slice_index.step
if index < 0: raise NotImplementedError("Negative slicing not implemented")
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
ll = LinkedList()
for i in range(index, stop,step):
ll.insert_node(self[i].data)
return ll
def __getitem__(self, index):
if isinstance(index, slice):
return self._slice_return(index)
if (index > self.length - 1) or (index < -self.length -1): raise ValueError(f"index {index} out of bounds of max length")
if index < 0: index = self.length + index
count = 0
current_node = self.head.next_pointer
while count != index:
current_node = current_node.next_pointer
count += 1
return current_node
def __len__(self):
return self.length
def __str__(self):
array = []
node = self.head
count = self.length
while count > 0:
node = node.next_pointer
array.append(node.data)
count -= 1
return(str(array))
def __repr__(self):
return self.__str__()
ll = LinkedList()
ll.insert_node("a")
ll.insert_node("b")
ll.insert_node("A")
ll.insert_node("B")
ll.delete_node(2) # delete 3rd node
Answer based on #jonrsharpe comment:
There is a Datamodel section in python docs with list of available dunder methods
In your case its:
object.__delitem__(self, key)
Called to implement deletion of self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support removal of keys, or for sequences if elements can be removed from the sequence. The same exceptions should be raised for improper key values as for the __getitem__() method.
I am trying to make a Doubly LinkedList, but I am getting the memory location instead of the Node value. Appreciate the help
#!/usr/bin/python3
class Node:
def __init__(self, num):
self.next = None
self.prev = None
self.num = num
class DlinkedList:
def __init__(self):
self.start = None
self.end = None
self.size = 0
def addFirst(self, num):
n = Node(num)
if( self.start == None):
self.start = n
self.end = n
print (self.start)
print (self.end)
print (self.size)
else:
n.next = self.start
d = DlinkedList()
print ("Add node 2")
d.addFirst(2)
print ("Add Node 1")
d.addFirst(1)
the output lools like the following which means that I am doing something wrong here.
I understand the code is not complete yet, but I am trying ti start little by little
The output of the code is:
Add node 2
<__main__.Node object at 0x7f0c61219940>
<__main__.Node object at 0x7f0c61219940>
0
Add Node 1
It looks like you are missing some key steps here. You need to make sure you are incrementing your counter as well as linking in the new node to the front of your doubly linked list. Here is what I ended up with:
#!/usr/bin/python3
class Node:
def __init__(self, num):
self.next = None
self.prev = None
self.num = num
class DlinkedList:
def __init__(self):
self.start = None
self.end = None
self.size = 0
def addFirst(self, num):
n = Node(num)
if(self.start == None):
self.start = n
self.end = n
print (self.start.num)
print (self.end.num)
print (self.size)
else:
n.next = self.start
self.start.prev = n
self.start = n
self.size += 1
d = DlinkedList()
print ("Add node 2")
d.addFirst(2)
print ("Add Node 1")
d.addFirst(1)
print(d.start.num, d.start.next.num, d.end.prev.num, d.end.num)
Example output from iPython
Add node 2
2
2
0
Add Node 1
1 2 1 2
In [3]: d.addFirst(3)
In [4]: print(d.start.num, d.start.next.num, d.end.prev.num, d.end.num)
3 1 1 2
You're printing self.start and self.end which are of type Node and since Node doesn't have a __str__ override, it just prints the memory location. What you want to do is print self.start.num and self.end.num or else add a definition for __str__:
def __str__(self):
return self.num
I am working on a doubly linked list and I can't get my remove function to work correctly. I want to return the value that it is being removed. I have tried various times, with no success. An extra pair of eye would be appreciated.
class DoublyLinked_List:
class __Node:
def __init__(self, val):
self.val = val
self.next = None
self.prev = None
def __init__(self):
self.__header = self.__Node(None)
self.__trailer = self.__Node(None)
self.__header.next = self.__trailer
self.__trailer.prev = self.__header
self.__size = 0
def __len__(self):
return self.__size
def remove_element_at(self, index):
if index > self.__size or index < 0 or index == self.__size:
raise IndexError
current = self.__header.next
if index == 0:
self.__header.next = self.__header.next.next
else:
for i in range(0, index-1):
current = current.next
current.next = current.next.next
self.__size -= 1
return current.val
I am trying to implement Singly Linked List in Python. This is my _Node class:
#!/usr/bin/env python3
class _Node:
"""Node class to create new nodes"""
def __init__(self, data=None, next=None):
"""Construction of node"""
self._data = data
self._next = next
I have removed push(), pop() and other methods from this code sample. They all are working.
class LinkedList:
"""Singly Linked List implementation for storage"""
def __init__(self):
"""Construction of Linked List"""
self._head = None
self._size = 0
def __len__(self):
"""Return length of linked list."""
self._count = 0
self._current = self._head
while self._current:
self._count += 1
self._current = self._current._next
return self._count
def value_at(self, index):
"""Return Value at given index"""
self._current = self._head
self._index = index
count = ans = 0
while count<= self._index:
if self._current == None:
return "List is empty."
ans = self._current._data
self._current = self._current._next
count += 1
return ans
def value_n_from_end(self, n):
"""Get value of nth element starting from end"""
self.n=n
self.n = int(self.__len__() - self.n -1)
print(self.n) #print value as expected
self.value_at(self.n)
Now my problem is that i can get value from value_at() but unable to get value from value_n_from_end() outside the class.
Input:-
l = LinkedList()
print(l)
print(l.__len__())
print(l.value_at(1))
print(l.value_n_from_end(2))
Output:-
5-> 3-> 4-> 6-> //My Linked List
4 //Length of linked list
3 //Value return by l.value_at(1)
1 //This is the value which i want to pass in self.value_at(self.n)
None //This is returned from (l.value_n_from_end(2))
Value of l.value_n_from_end(2) should be same as l.value_at(1) i.e. 3. But i am missing something.
The value_n_from_end does not return anything. You should write:
return self.value_at(self.n)
Indeed, self.value_at(self.n) returns what you want in the function value_n_from_end but then you have to get it back to you by using a return statement. Otherwise it is bound to the function namespace.
My basic idea was to create a linked list, and as each new value comes in, add 1/N times the new value and subtract 1/N times the first value, then move the pointer to first along by one and free the memory that had been associated with first.
This won't ultimately be implemented in Python but just to get the process clear in my head, I tried to write it in Python, but my implementation is flawed. Do I need a doubly linked list for this? Is there an alternative approach (not linked-list based) that would be better?
Here's my attempt so far:
class Link:
def __init__(self,val):
self.next = None
self.value = val
class LinkedList:
def __init__(self,maxlength):
self.current_link = None
self.maxlength = maxlength
self.sum = 0.
self.average = None
self.length = 0
self._first_link = None
def add_link(self,val):
new_link = Link(val)
new_link.next = self.current_link
self.current_link = new_link
if self._first_link is None:
self._first_link = self.current_link
self.sum += val
if self.length < self.maxlength:
self.length += 1
else:
self.sum -= self._first_link.value
self._first_link = self._first_link.next # this line is flawed
self.average = self.sum/self.length
def get_first(self):
return self._first_link.value
# Main
ll = LinkedList(5)
for ii in xrange(10):
ll.add_link(ii)
print ii,ll.get_first(),ll.average
The problem is that _first_link gets set to a value that doesn’t have a next. That is, _first_link gets set to the first item that's added, but its next is None, so I don't see how to move it along by 1 as I want to. This is what makes me wonder if a doubly linked list is needed.
I'd appreciate any advice.
I think the simplest implementation is to use a circular linked list (a.k.a. a ring):
class Link(object):
def __init__(self, value=0.0):
self.next = None
self.value = value
class LinkedRing(object):
def __init__(self, length):
self.sum = 0.0
self.length = length
self.current = Link()
# Initialize all the nodes:
last = self.current
for i in xrange(length-1): # one link is already created
last.next = Link()
last = last.next
last.next = self.current # close the ring
def add_val(self, val):
self.sum -= current.value
self.sum += val
self.current.value = val
self.current = self.current.next
def average(self):
return self.sum / self.length
# Test example:
rolling_sum = LinkedRing(5)
while True:
x = float(raw_input())
rolling_sum.add_val(x)
print(">> Average: %f" % rolling_sum.average())
You can implement this using collections.deque and the numerically stable math for maintaining running averages:
import collections
class AveragingBuffer(object):
def __init__(self, maxlen):
assert( maxlen>1)
self.q=collections.deque(maxlen=maxlen)
self.xbar=0.0
def append(self, x):
if len(self.q)==self.q.maxlen:
# remove first item, update running average
d=self.q.popleft()
self.xbar=self.xbar+(self.xbar-d)/float(len(self.q))
# append new item, update running average
self.q.append(x)
self.xbar=self.xbar+(x-self.xbar)/float(len(self.q))
if __name__=="__main__":
import scipy
ab=AveragingBuffer(10)
for i in xrange(32):
ab.append(scipy.rand())
print ab.xbar, scipy.average(ab.q), len(ab.q)
Okay, I thought of a solution that works in O[1] time. I'm still curious if anyone has a linked-list-based solution, but this solution avoids the LL entirely:
class Recent:
def __init__(self,maxlength):
self.maxlength = maxlength
self.length = 0
self.values = [0 for ii in xrange(maxlength)]
self.index = 0
self.total = 0.
self.average = 0.
def add_val(self,val):
last = self.values[self.index%self.maxlength]
self.values[self.index%self.maxlength] = val
self.total += val
self.total -= last
if self.length < self.maxlength:
self.length += 1
self.average = self.total / self.length
self.index += 1
def print_vals(self):
print ""
for ii in xrange(self.length):
print ii,self.values[ii%self.maxlength]
print "average:",self.average
# Example to show it works
rr = Recent(5)
for ii in xrange(3):
rr.add_val(ii)
rr.print_vals()
for ii in xrange(13):
rr.add_val(ii)
rr.print_vals()