Python basic data structure Implementation - python

I am a C++ coder. Recently started with Python. I was having a look at a simple Linked List implementation in Python. I am bit confused here. Not only here but also in Tree implementation and so on with the same problem.
class Element contains data and pointer to next node. Perfect no problem. However in class LinkedList I can see self.tail.next=e, now next is a variable of Element class even if it is public than also an object of Element class has to access it. Here how can we write something like self.tail.next = e as tail is just a variable of LinkedList class and is not an object of Element class. I am confused.
class Element:
def __init__(self,x):
self.data=x
self.next=None
class LinkedList:
def __init__(self):
self.head=None
self.tail=None
def append(self,x):
# create a new Element
e = Element(x)
# special case: list is empty
if self.head==None:
self.head=e
self.tail=e
else:
# keep head the same
self.tail.next=e
self.tail=e

Python works with references. Everything is always passed by reference, the values are always shared via references (unless explicitly copied).
Assigning an object means assigning the reference to that object. This way self.tail.next = e means: self.tail is expecting to refer to the object of the Element class. The object has the .next attribute. The self.tail.next = e means that the last element of the non-empty list is going to point to the just appended new element. Then the self.tail = e means that the reference to the last element is moved to the just appended last element.
Any variable in Python is just a reference variable with the given name. It is automatically dereferenced. Because of that it may look strangely to those familiar with classical compiled languages, like C++.
I am not sure if you can display the articles at Expert Exchange without creating the account. If yes, have a look at http://www.experts-exchange.com/Programming/Languages/Scripting/Python/A_6589-Python-basics-illustrated-part-2.html and namely the http://www.experts-exchange.com/Programming/Languages/Scripting/Python/A_7109-Python-basics-illustrated-part-3.html for the images that explain the problem.

The only place where you initialize self.tail is in append and in there you set it to be equal to e. Just a little bit above you have e = Element(x) and so e is an object of type Element. Please note that in the moment you call self.tail.next=e you know head is not none and thus tail is also not None but an instance of Element.
Hope this helps.

You wrote:
now next is a variable of Element class even if it is public than also an object of Element class has to access it.
There is a misunderstanding there. Public members (i.e. all members except those that start with two underscores) are accessible from anywhere, not only from within methods of the same class.

next and tail are Elements, that's why. This would be true for a linked list in any language, including C or C++. A linked list is a list of elements linked together with pointers/references.
When the list is traversed, those links are used to get from node (element) to node. It would not make sense if they referred to other linked lists, would it? The first element in the list is the head, the last element is the tail, the next element pointed to in a node is the element after it in the list, the previous node is the element before it.

In python everything is a reference.
Please: separate the list implementation from your (application) data.
As in C++ it is good style to encapsulate the access methods - nevertheless because there are no access restrictions to instance fields they can be accessed from everywhere.
An idea of a generic double linked list (this is only a skeleton which should be enhanced - but I hope it transports the idea).
class DoubleLinkedList(object):
class ListNode(object):
def __init__(self):
self.__next = None
self.__prev = None
def next(self):
return self.__next
def prev(self):
return self.__prev
def set_next(self, next_node):
self.__next = next_node
def set_prev(self, prev_node):
self.__prev = prev_node
def is_last(self):
return self.next()==None
def __init__(self):
'''Sets up an empty linked list.'''
self.__head = DoubleLinkedList.ListNode()
self.__tail = DoubleLinkedList.ListNode()
self.__head.set_next(self.__tail)
self.__tail.set_prev(self.__head)
def first(self):
return self.__head.next()
def last(self):
return self.__tail.prev()
def append(self, list_node):
list_node.set_next(self.__tail)
list_node.set_prev(self.__tail.prev())
self.__tail.set_prev(list_node)
list_node.prev().set_next(list_node)
########################################
class MyData(DoubleLinkedList.ListNode):
def __init__(self, d):
DoubleLinkedList.ListNode.__init__(self)
self.__data = d
def get_data(self):
return self.__data
ll = DoubleLinkedList()
md1 = MyData(1)
md2 = MyData(2)
md3 = MyData(3)
ll.append(md1)
ll.append(md2)
ll.append(md3)
node = ll.first()
while not node.is_last():
print("Data [%s]" % node.get_data())
node = node.next()

Related

Python object isn't being created as normal even though I have created it as normal within the same function (doubly linked list)

I've created a doubly linked list and I'm having trouble with a function that swaps the first node with the last node, the second node with the second from last, etc. I'm creating various functions within the doubly linked list class (LDL). The problem comes with trying to create a node object. However it's strange as I have created the node objects without problem even within the same function. Here's the code:
class Node:
def __init__(self, data, nextLeft=None, nextRight=None):
self.data = data
self.nextLeft = nextLeft
self.nextRight = nextRight
class LDL:
def __init__(self, head=None):
self.head = head
def insertAtEnd(self, dataToInsert):
...
def reverseSwap(self):
nodeFirst = self.head # THE PROBLEM OBJECT
nodeCurrent = self.head
nodeLast = Node(None)
counter = 0
while nodeCurrent.nextRight is not None:
nodeCurrent = nodeCurrent.nextRight
conter += 1
for i in range(0, int(cont/2)):
temp = nodeFirst.data
nodeFirst.data = nodeLast.data # THE LINE OF THE ERROR
nodeLast.data = temp
nodeFirst = nodeFirst.nextRight
nodeLast = nodeLast.nextLeft
When I try to run this I get the error:
nodeFirst.data = nodeLast.data
AttributeError: 'NoneType' object has no attribute 'data'
However I knew this was coming as I can see while coding that it's not recognised as a Node object, because it doesn't offer to autofill nodeFirst.data or nodeFirst.nextLeft etc.
I have tried setting nodeFirst = nodeCurrent, I have tried placing it at various other parts of the code, I have tried different names, but I can't get it to work.
EDIT: I should point out that nodeCurrent = self.head works fine and as intended. This is also the first time I've come across this when working with linked lists as I have used a similar setup to work with singly linked lists with no problems.
If anybody could point me in the right direction that would be great.
because your init method of LDL class allows head to be None.
Therefore nodeFirst and nodeLast may become None.
to prevent this fix following
def __init__(self, head=None):
self.head=head
to this
def __init__(self, headdata):
self.head=Node(headdata)
or alternatively consider the edge case of list size = 0 in reverseSwap method

A doubly linked list with index pointer?

doubly linked list with index pointerThis is what we call it in the class but I didn’t find anything similar on the internet and this is the example code but I don’t understand the way it works, if you can give me a visual of the linked list created this way, that would be great
this is the code(in case that you couldn't open the picture):
class CursorList:
def __init__(self,max=100):
self.max=max
self.next=[None]*self.max
self.element=[None]*self.max
self.prev=[None]*self.max
self.avail=0
self.head=None
self.size=0
for i in range (self.max-1):
self.next[i]=i+1
def allocate(self):
if self.avail is None:
raise Exception('out of space')
x=self.avail
self.avail=self.next[self.avail]
return x
def free(self,x):
self.next[x]=self.avail
self.avail=x
def is_empty(self):
return self.size==0
def insert_first(self,e):
index=self.allocate()
self.next[index]=self.head
self.element[index]=e
self.prev[index]=None
if not self.is_empty():
self.prev[self.head]=index
self.head=index
self.size+=1
def delete_first(self):
if self.is_empty():
raise Exception('List is empty')
index=self.head
e=self.element[index]
self.head=self.next[self.head]
self.size-=1
self.free(index)
return e
It is a regular doubly linked list data structure such that the pointing to the next and previous node will be done by index. So, it looks like the following (except you need to see the index of the element as a pointer to that element):

How to move last element of a linked list to first in python (below code)?

#DSA-Prac-1
class Node:
def __init__(self,data):
self.__data=data
self.__next=None
def get_data(self):
return self.__data
def set_data(self,data):
self.__data=data
def get_next(self):
return self.__next
def set_next(self,next_node):
self.__next=next_node
class LinkedList:
def __init__(self):
self.__head=None
self.__tail=None
def get_head(self):
return self.__head
def get_tail(self):
return self.__tail
def add(self,data):
new_node=Node(data)
if(self.__head is None):
self.__head=self.__tail=new_node
else:
self.__tail.set_next(new_node)
self.__tail=new_node
def insert(self,data,data_before):
new_node=Node(data)
if(data_before==None):
new_node.set_next(self.__head)
self.__head=new_node
if(new_node.get_next()==None):
self.__tail=new_node
else:
node_before=self.find_node(data_before)
if(node_before is not None):
new_node.set_next(node_before.get_next())
node_before.set_next(new_node)
if(new_node.get_next() is None):
self.__tail=new_node
else:
print(data_before,"is not present in the Linked list")
def display(self):
temp=self.__head
while(temp is not None):
print(temp.get_data())
temp=temp.get_next()
def find_node(self,data):
temp=self.__head
while(temp is not None):
if(temp.get_data()==data):
return temp
temp=temp.get_next()
return None
def delete(self,data):
node=self.find_node(data)
if(node is not None):
if(node==self.__head):
if(self.__head==self.__tail):
self.__tail=None
self.__head=node.get_next()
else:
temp=self.__head
while(temp is not None):
if(temp.get_next()==node):
temp.set_next(node.get_next())
if(node==self.__tail):
self.__tail=temp
node.set_next(None)
break
temp=temp.get_next()
else:
print(data,"is not present in Linked list")
def change_order(input_list):
'I need the code to be written here'
return input_list
input_list=LinkedList()
input_list.add(9)
input_list.add(3)
input_list.add(56)
input_list.add(6)
input_list.add(2)
input_list.add(7)
input_list.add(4)
result=change_order(input_list)
result.display()
Only the function change_order must be written. No changes should be made in other parts of the program. The input linked list is 9->3->56->6->2->7->4 and the output should be 4->9->3->56->6->2->7.I need answer for this particular code.
This is what i have tried. Since the head of linkedlist class is a private attribute, i face difficulty in assigning the new head.
def change_order(input_list):
temp=input_list.get_head()
while temp and temp.get_next():
sec_last = temp
temp=temp.get_next()
sec_last.set_next(None)
temp.set_next(input_list.get_head())
Your LinkedList class should already provide all the functionality you need to be able to accomplish this without any need to mess around with the pointers yourself:
data = input_list.get_tail().get_data() # get last element
input_list.delete(data) # remove last element
input_list.insert(data, None) # insert that element first
Note that the list interface assumes that all items in the list are unique; some of these methods don't work properly if you have duplicates and will only ever operate on the first match. If you do the insert before the delete, for example, the delete will remove the item at the head that you just inserted, not the item at the tail that you wanted to remove.
This is essentially a bug/constraint of this list implementation; normally a list interface would provide you with some sort of iterator to allow you to handle cases with multiple matches.
If you had to work around that under the parameters of the assignment, being able to modify the head isn't the hard part (because you can do that reliably via insert), but rather popping off the tail (since the only interface that lets you do that is delete, which will flatly not let you access a node by reference, and is therefore always going to do the wrong thing if you're trying to use it to delete the tail when there's a duplicate). The simplest solution IMO is to just convert the entire list into a better format, do whatever you need, and then convert it back. (This is a terrible lesson if the goal is to learn how linked lists work, but it's a good lesson if the goal is learn how sometimes you need to do silly things to work around someone else's incompetence.)
def change_order(input_list):
'I need the code to be written here'
# Define helper functions to convert LinkedList to and from List.
def convert_linked_list_to_list(linked_list):
"""Converts a LinkedList to a native List."""
arr = []
node = input_list.get_head()
while node is not None:
arr.append(node.get_data())
node = node.get_next()
return arr
def rebuild_linked_list_from_list(linked_list, arr):
"""Replaces input LinkedList contents with native List contents."""
while linked_list.get_head() is not None:
linked_list.delete(linked_list.get_head().get_data())
for data in arr:
linked_list.add(data)
# Now do the order change using a List.
arr = convert_linked_list_to_list(input_list)
rebuild_linked_list_from_list(input_list, arr[-1:] + arr[:-1])
return input_list
In real life, you'd define these helpers outside of this function, because they're bound to be useful in other situations where you need to manipulate one of these LinkedList containers in ways that its terrible interface doesn't support, but the parameters of the assignment require everything to be contained to change_order, so there you have it.

Python passing class instance to method and modifying property causes new, unrelated instances to change default behavior

In the code below, I define a Node class which when instantiated, should have an empty "childarr" property. Then, I instantiate the class and pass it to the method, "expnd". This adds three new node to the childarr array. As expected, the first print statement shows that the length of the childarr of the "node" instance is 3. But now, I instantiate a new instance of the "Node" class from scratch. Since I specified in the init method of the class that if nothing is passed for this variable, it should be an empty array, I was expecting nn to have an empty childarr property. However, the second print statement shows it actually has three children as well.
It's very confusing to me why "nn" is being affected by some code that happened before it was instantiated and has nothing to do with it.
Does anyone know of the reason for this behavior and what I'm missing in terms of best practice?
class Node():
def __init__(self, childarr=[]):
self.childarr = childarr
def expnd(node):
for i in range(3):
newnode = Node()
node.childarr.append(newnode)
node=Node()
expnd(node)
print("Length of child array of expanded node:" + str(len(node.childarr)))
nn = Node()
print("Length of child array of new, unrelated node:" + str(len(nn.childarr)))
The problem is that you instantiate all "empty" childarrs with the same empty list instance, created in the definition of __init__.
One way to change this behaviour:
class Node():
def __init__(self, childarr=None):
self.childarr = childarr or []
def expnd(node):
for i in range(3):
newnode = Node()
node.childarr.append(newnode)
node=Node()
expnd(node)
print("Length of child array of expanded node:" + str(len(node.childarr)))
nn = Node()
print("Length of child array of new, unrelated node:" + str(len(nn.childarr)))
Result:
Length of child array of expanded node:3
Length of child array of new, unrelated node:0

Concatenate Python Linked List

I am attempting to concatenate a Python linked list without copying the data contained within the nodes of the list. I have a function that will concatenate the list using copies of the nodes passed in, but I can't seem to get the function that doesn't use copies to work.
These functions are for testing and timing purposes; I know that Python's built-in list is awesome!
Here is the class I have been working with and the concatenate function.
class Cell:
def __init__( self, data, next = None ):
self.data = data
self.next = next
def print_list(self):
node = self
while node != None:
print node.data
node = node.next
The concatenation function is not meant to be a member function of the Cell class.
def list_concat(A, B):
while A.next != None:
A = A.next
A.next = B
return A
This function overwrites the first element of a list if the parameter A has more than one node. I understand why that is happening, but am not sure how to go about fixing it.
Here is the testing code I've been using for this function.
e = Cell(5)
test = Cell(3, Cell(4))
test2 = list_concat(test2, e)
test2.print_list()
Any insight or help would be greatly appreciated.
*edited to fix code formatting
Try this instead:
def list_concat(A, B):
current = A
while current.next != None:
current = current.next
current.next = B
return A
Assigning new values to a function's parameters is a bad programming practice, and the code in your question shows why: You used A for iterating over the original list, and by doing so, you lost the reference to its first element.
I'm not sure on about if extend performs a copy or not, but in case it doesn't, just use
A.extend(B)

Categories