A doubly linked list with index pointer? - python

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):

Related

How tail works within a linked structures?

I am new into programming and I am learning more complex Data Structures using Python and I find difficult to understand the concept of adding an element to a linked list using a head and a tail.
class Bag:
def __init__(self):
self._head = None
self._tail = None
self._size = 0
def add(self, item):
newNode = _BagListNode(item)
if self._head is None:
self._head = newNode
else:
self._tail.next = newNode
self._tail = newNode
self._size += 1
class _BagListNode(object):
def __init__(self, item):
self.item = item
self.next = None
def __repr__(self):
return f"{self.item}"
The point is when I add the first element, everything clear. As the head is None at first, it'll add the newNode to both tail and head. The problem starts when I add the second element: I do not understand why the second element is added to the element that has been added before, at the same time with the self._tail when this line of code self._tail.next = newNode is executed. After this line of code, the self._tail becomes the second element and this seems pretty logical as I have to keep tracking the tail as I keep on adding elements and self._head now have two elements but in code there is no line of code where to self._head is added a new element.
For example:
bag = Bag()
bag.add(1)
bag.add(2)
bag.add(3)
print(bag._head.item, bag._head.next.item, bag._head.next.next.item)
and the result is:
1 2 3
I hope my question is clear enough. I appreciate your time so much. Thank you!
After this line of code, the self._tail becomes the second element and this seems pretty logical as I have to keep tracking the tail as I keep on adding elements and self._head now have two elements but in code there is no line of code where to self._head is added a new element.
I think what you might be missing here is that self._head is not a Bag in and of itself, but rather a pointer to a _BagListNode object.
When new items are added on to the bag, they are affixed as the next node on the previous tail, becoming the new tail. This does not affect the head at all in your implementation. An alternative, perhaps clearer, implementation could simply make an item the new head of the list, as pictured here:
One tip my data structures professor gave me was to draw a picture of what is happening to improve your intuitive understanding. You might consider drawing a picture of what happens when the third item is inserted into the bag.

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.

how to sort a list that is contained in an object

The task is to code a sorting algorithm using the below code as a starting point. The issue is I cannot seem to figure out how I go about starting the code, I'm not looking for the full solution - just techniques in how to sort lists of tuples that are actually part of an object. I get errors when I try to iterate through the list, saying cannot iterate through an object.
class LinkedList:
def __init__(self, data):
self.label = data[0][0]
self.value = data[0][1]
self.tail = None if (len(data) == 1) else LinkedList(data[1:])
countries = LinkedList([("Ukraine",41879904),("Brunei",442400),("Christmas Island (Australia)",1928)
You can use a pointer to iterate through linked list.:
curr = countries
while curr:
print("Label {}, Value {}".format(curr.label, curr.value))
curr = curr.tail
In order to sort linked list, firstly, you need to implement helper functions to remove/insert a node to given linked list at certain position. Once you have such methods, you can implement any of the famous sorting algorithms (e.g quick sort) using your helper methods you just created.
To iterate over this list you need to keep getting the tail reference of the object:
class LinkedList:
def __init__(self, data):
self.label = data[0][0]
self.value = data[0][1]
self.tail = None if (len(data) == 1) else LinkedList(data[1:])
countries = LinkedList([("Ukraine",41879904),("Brunei",442400),("Christmas Island (Australia)",1928)])
nextObj = countries
while nextObj is not None:
print(nextObj.label, nextObj.value)
nextObj = nextObj.tail
print("done!")
output:
Ukraine 41879904
Brunei 442400
Christmas Island (Australia) 1928
done!
To get an element at a certain index, you start iterating from the first element and just keep a counter.
functools, #total_ordering
One of the powerful features in python. you can sort objects in a classic and easy straitforward way.
Functools module in python helps in implementing higher-order functions. Higher-order functions are dependent functions that call other functions. Total_ordering provides rich class comparison methods that help in comparing classes without explicitly defining a function for it. So, It helps in the redundancy of code.
There are 2 essential conditions to implement these comparison methods
At least one of the comparison methods must be defined from lt(less than), le(less than or equal to), gt(greater than) or ge(greater than or equal to).
The eq function must also be defined
from functools import total_ordering
#total_ordering
class LinkedList(object):
def __init__(self, data):
self.label = data[0][0]
self.value = data[0][1]
def __lt__(self, other):
return self.label < other.value ##lets assume that the sort is based on label data member(attribute)
def __eq__(self, other):
return self.label == other.value
##I dont know what data is. just pass your data objects to list of LinkedList objects. and sort it with sorted method (treat them like int objects)!
my_list = [LinkedList(data0),LinkedList(data1),LinkedList(data2)]
new_list=my_list.sorted()
for obj in new_list:
print(...)

Python basic data structure Implementation

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()

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