I am wanting to iterate through my linked list and only retrieve the value at a specific index. I have written a very slow way of accomplishing this:
`
def get_index(self, data):
if self.head is None:
raise Exception("List is empty")
reg_list = []
for i in self:
reg_list.append(i)
print(reg_list[data])
`
This code works but I realize this is not the fastest nor the best method for completing this operation. How can I write this better and without creating a standard list to accomplish the desired outcome?
For clarity as some are asking, here is the entirety of relevant code to make this run as I have written it and to show it is indeed a linked list:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def __repr__(self):
return self.data
class Linked_List:
def __init__(self, nodes=None):
self.head = None
if nodes is not None:
node = Node(data=nodes.pop(0))
self.head = node
for elem in nodes:
node.next = Node(data=elem)
node = node.next
def __repr__(self):
node = self.head
nodes = []
while node is not None:
nodes.append(node.data)
node = node.next
nodes.append("None")
return " -> ".join(nodes)
def __iter__(self):
node = self.head
while node is not None:
yield node
node = node.next
def get_index(self, data):
if self.head is None:
raise Exception("List is empty")
reg_list = []
for i in self:
reg_list.append(i)
print(reg_list[data])
```
It is confusing that your code names the argument data while it is supposed to be an index, and then names the loop variable i, which is supposed to be a node. Why not choose names that actually say what it is? Like index and node?
You can change the given code to the following:
def get_index(self, index):
return next((node for i, node in enumerate(self) if i == index), None)
Note that now:
The function returns the node. This is appropriate for such a method. Only printing the result would make the method less useful.
No error is raised when the list is empty. There is no reason to treat an empty list differently. The function will just return None when the given index is out of range, whether the list is empty or not.
Related
I learned about linked-lists today. I learned how to insert and remove nodes from them. In the following code, it teaches me how to insert nodes with three distinct functions: at beginning (the head node), in between and at the end. However, they teach me how to remove the node in and single function. I don't find the code in the remove function to be very clear. Can anyone help make an easier to understand code under the remove function? Is there a way to make it into three functions just like the inserting ones? I'm open to any suggestion or explanation. Thanks in advance.
Here's my code:
class Node:
def __init__(self, data=None):
self.data = data
self.nextnode = None
class LinkedList:
def __init__(self):
self.headnode = None
def printlist(self):
node = self.headnode
while node is not None:
print (node.data)
node = node.nextnode
def atbegining(self,new_node):
new_node.nextnode = self.headnode
self.headnode = new_node
# Function to add newnode
def AtEnd(self, newnode):
if self.headnode is None:
self.headnode = newnode
return
node = self.headnode
while(node.nextnode):
node = node.nextnode
node.nextnode=newnode
# Function to add node
def Inbetween(self,preNode,newNode):
if preNode is None:
print("The mentioned node is absent")
return
newNode.nextnode = preNode.nextnode
preNode.nextnode = newNode
# Function to remove node
def RemoveNode(self, RemoveVal):
node = self.headnode
if (node is not None):
if (node.data == RemoveVal):
self.headnode = node.nextnode
node = None
return
while (node is not None):
if node.data == RemoveVal:
break
prevnode = node
node = node.nextnode
if (node == None):
return
prevnode.nextnode = node.nextnode
node = None
list1 = LinkedList()
list1.headnode = Node("Mon")
n2 = Node("Tue")
n3 = Node("Wed")
# Link first Node to second node
list1.headnode.nextnode = n2
# Link second Node to third node
n2.nextnode = n3
n4 = Node("Sun")
n5 = Node("Tur")
n6 = Node("Newdate")
list1.atbegining(n4)
list1.AtEnd(n5)
list1.Inbetween(list1.headnode,n6)
list1.RemoveNode("Newdate")
list1.printlist()
RemoveNode is complicated by the fact that there are two structurally distinct kinds of LinkedLists: one whose head is None, and one whose head is not None. You can fix this by making sure every LinkedList contains at least one node. This is typically referred to as a dummy node, and you can use this node to store metadata (such as the length of the list).
The Node class itself does not change.
class Node:
def __init__(self, data=None):
self.data = data
self.nextnode = None
The LinkedList, however, simplifies by creating a dummy node. This provides
the guarantee that every node that stores real data is point to by another node.
class LinkedList:
def __init__(self):
self.headnode = Node(0)
def insert(self, preNode, newNode):
newNode.nextnode = preNode.nextnode
preNode.nextnode = newNode
self.headnode.data += 1
def append(self, newNode):
curr = self.headnode
while curr.nextNode is not None:
curr = curr.nextNode
self.insert(curr, newNode)
def prepend(self, newNode):
self.insert(self.headnode, newNode)
def _find_before(self, val):
pre = self.headnode
while pre.nextnode is not None:
curr = pre.nextnode
if curr.data == val:
return pre
pre = curr
def remove(self, RemoveVal):
pre = self._find_before(RemoveVal)
if pre is None:
return
pre.nextnode = pre.nextnode.nextnode
self.headnode.data -= 1
This simplifies all three insertions. The general case can always apply, since there is always a node that comes before the node you insert. append and prepend are simple wrappers that find the appropriate node to pass to insert.
Likewise, remove simply finds the node before the given value, and if the search succeeds, handles updating the prior node's nextnode attribute.
insert and remove also update the size of the list stored in the dummy node.
A find method becomes a simple wrapper around _find_before; if you find a node before the value you are looking for, just return the node that follows it.
I think that an alternative design will make the code much clearer. Consider for example the following:
class Node:
def __init__(self, data=None):
self.data = data
self.nextnode = None
def printlist(self):
print(self.data)
if self.nextnode is not None:
self.nextnode.printlist()
def push(self, node):
node.nextnode = self
return node
def insertafter(self, node):
node.nextnode = self.nextnode
self.nextnode = node
return self
def append(self, node):
lastnode = self
while lastnode.nextnode is not None:
lastnode = lastnode.nextnode
lastnode.nextnode = node
return self
def remove(self, value):
prev = None
walk = self
while walk is not None:
if walk.data == value:
if prev is None:
return walk.nextnode
else:
prev.nextnode = walk.nextnode
return self
else:
prev = walk
walk = walk.nextnode
return self
list1 = Node("Mon")
n2 = Node("Tue")
n3 = Node("Wed")
# Link first Node to second node
list1 = list1.insertafter(n2)
# Link second Node to third node
n2 = n2.insertafter(n3)
n4 = Node("Sun")
n5 = Node("Tur")
n6 = Node("Newdate")
list1 = list1.push(n4)
list1 = list1.append(n5)
list1 = list1.insertafter(n6)
list1 = list1.remove("Newdate")
list1.printlist()
The main idea is that a Node is the linked list. As long as you have the head of the list kept in a variable, you can have access to the entire list, without the need for a separate data structure.
I am trying to write a code to remove duplicates from a sorted linked list "head". My code below always returns the last duplicate if the list ends with a duplicate. for e.g. [1,2,2,3,3] will return [1,2,3,3]. I can't figure out why. Does anyone have an idea?
class Solution(object):
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
if not head:
return head
l1=newhead=ListNode(head.val)
head=head.next
while head:
if head.val!=l1.val:
l1.next=head
l1=l1.next
head=head.next
return newhead
You should keep track of the leading node of each new value and keep fetching the next node until you get a node with a different value, at which point you assign that node as the next node for the leading node:
class Solution(object):
def deleteDuplicates(self, head):
node = head
while node:
lead = node
while node.next and node.next.val == lead.val:
node = node.next
node = lead.next = node.next
return head
Problem Solution
Create a class Node with instance variables data and next.
Create a class LinkedList with instance variables head and last_node.
The variable head points to the first element in the linked list while last_node points to the last.
Define methods append, get_prev_node, remove and display.
The method append takes a data item as argument and appends a node with that data item to the list.
The method get_prev_node takes a reference node as argument and returns the previous node. It returns None when the reference node is the first node.
The method remove takes a node as argument and removes it from the list.
The method display traverses the list from the first node and prints the data of each node.
Define a function remove_duplicates which takes a linked list as argument and removes duplicates from it.
The function remove_duplicates uses two nested loops to remove duplicate nodes.
Create an instance of LinkedList, remove duplicate nodes and display the list.
Program/Source Code
Here is the source code of a Python program to remove duplicates from a linked list.
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
self.last_node = None
def append(self, data):
if self.last_node is None:
self.head = Node(data)
self.last_node = self.head
else:
self.last_node.next = Node(data)
self.last_node = self.last_node.next
def get_prev_node(self, ref_node):
current = self.head
while (current and current.next != ref_node):
current = current.next
return current
def remove(self, node):
prev_node = self.get_prev_node(node)
if prev_node is None:
self.head = self.head.next
else:
prev_node.next = node.next
def display(self):
current = self.head
while current:
print(current.data, end = ' ')
current = current.next
def remove_duplicates(llist):
current1 = llist.head
while current1:
data = current1.data
current2 = current1.next
while current2:
if current2.data == data:
llist.remove(current2)
current2 = current2.next
current1 = current1.next
a_llist = LinkedList()
data_list = input('Please enter the elements in the linked list: ').split()
for data in data_list:
a_llist.append(int(data))
remove_duplicates(a_llist)
print('The list with duplicates removed: ')
a_llist.display()
Program Explanation
An instance of LinkedList is created.
The user is prompted to enter the data items for the list.
The function remove_duplicates is called to remove duplicates from the list.
The linked list is displayed.
I have found an implementation of a Linked List in Python, online, but it doesn't have any explanation or comments.
I understand the underlying concepts of a Linked List, but there is one key part of the code I don't understand:
class Node:
def __init__(self, data):
self.data = data
self.next = None
def get_data(self):
return self.data
def get_next(self):
return self.next
def set_data(self, data):
self.data = data
def set_next(self, next):
self.next = next
class LinkedList:
def __init__(self):
self.head = None
def is_empty(self):
return self.head == None
def add(self, item):
temp = Node(item)
temp.set_next(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.get_next()
return count
def search(self, item):
current = self.head
while current != None:
if current.get_data() == item:
return True
else:
current = current.get_next()
return False
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.get_data() == item:
found = True
else:
previous = current
current = current.get_next()
if previous == None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())
I don't understand how the size, search and remove methods in the LinkedList class are able to call functions from the Node class via the current variable, after setting it to self.head, which seems to be contained within the scope of the LinkedList class.
Is it because the add method sets self.head = temp, where temp is a Node object?
If possible, could someone explain how this works?
You stated that:
I don't understand how the size, search and remove methods in the LinkedList class are able to call functions from the Node class via the current variable, after setting it to self.head, which seems to be contained within the scope of the LinkedList class.
You can see that in the code, initializing a LinkedList performs this line of code:
self.head = None
Since the head is set to none, the size, search, and remove methods will not run through the whole code. Rather, it will stop when the self.head == None, which is pretty much in the beginning.
For example, let's take a look at the size method.
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.get_next()
return count
In this function, current is set to self.head which is null unless you have added any nodes by calling the add() method. More on that later.
count is set equal to 0. Then a while loop begins which only runs if the current is not None. But since the current is set to self.head which is None, the while loop will not run and the function will return count which is 0. This is a correct implementation because there are currently no nodes in the linkedlist.
Now onto how you can add nodes.
The add method:
def add(self, item):
temp = Node(item)
temp.set_next(self.head)
self.head = temp
Here, the add method takes in itself and an item. The item is an object of some sort whether it be a string, integer, float, etc. Now a variable temp is created and set to a new node which is finally using something from the Node class. Then, temp's next node is set to head and the head is set to temp. What this does is that the linked list continuously updates the head.
Like this:
(head)
NODE1
ADD ONE MORE NODE
(head)
NODE2 NODE1
And so on...
Happy Coding!
I am using Python 3.6.3. I am trying to write a simple Linked List in Python. Here is my code:
class Node(object):
"""Represents a node within a linked list"""
def __init__(self, data, next=None):
self.stuff = data
self.next = next
def __str__(self):
return str(self.stuff)
class LinkedList(object):
def __init__(self):
self.head = None
self.size=0
def append(self, data):
if not self.head:
self.head = Node(data)
return
else:
n=self.head
while n.next:
n = n.next
new_node = Node(data)
n.next = new_node
return
def insertAfter(self, data, newNode):
if not self.head:
return
else:
n=self.head
while n and n.stuff != data:
n = n.next
if not n:
return
else:
newNode.next = n.next
n.next = newNode
return
def printlist(self):
if not self.head:
print("List is empty")
return
else:
n = self.head
while n:
print(str(n))
n = n.next
return
ll = LinkedList()
ll.append(Node("1"))
ll.append(Node("2"))
ll.append(Node("3"))
ll.insertAfter("2", Node("2.5"))
ll.printlist()
I expect it to print:
1
2
2.5
3
But, instead it prints:
1
2
3
After debugging, I realized that changing this line in the insertAfter method:
while n and n.stuff != data:
to:
while n and n.stuff.stuff != data:
prints the expected output.
I don't understand why it is doing this.
Please help.
Thank you
The problem is you are not adding 1, 2, 3 to the LinkedList and letting it create a Node to wrap each. You are adding a Node whose stuff variable is 1, 2, and 3. When you call the append method to add each Node, they get wrapped in another Node by the append method. Therefore you need to call Node.stuff.stuff to access the actual element that is stored.
Look at what your append method is actually doing. It accepts some parameter data and then creates a Node with either the line self.head = Node(data) or the line new_node = Node(data), depending on if the LinkedList already has a head Node or not.
Change ll.append(Node("1")) to just ll.append("1"). Alternatively, change your append method to assume it is being passed a Node object. The first solution is much more preferable as the Node class has little use outside the context of the LinkedList class and the LinkedList doesn't work unless it is populated with Nodes anyways; it seems like extra work to make the user of the LinkedList class have to create a Node every single time.
EDIT: Also what is the reason you are passing in numbers as strings? You don't need to put 1, 2, 2.5, and 3 in quotes unless you specifically want them to be strings- but if so, why?
I've implemented a backwards linked list successfully, but I'm trying to find out how to implement a forwards linked list, and I can't seem to figure out how to do it. The problem is in my insert method. I'm trying to keep track of the previous node, and point it to the newly created node, but I'm missing something.
class Node(object):
def __init__(self, data=None, next_node=None):
self.data = data
self.next_node = next_node
def set_next(self, new_next):
self.next_node = new_next
def get_data(self):
return self.data
def get_next(self):
return self.next_node
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def insert(self, data):
previous_node = self.head
current_node = self.head
new_node = Node(data)
if previous_node is not None:
previous_node.set_next(new_node)
previous_node = self.head
It's not clear what your insert method is supposed to do. If it is meant to insert at the beginning of the list (before head), then you should be setting new_node.set_next(previous_node) and self.head = new_node. If you meant to append to the end of the list, then you will need to scan through the list until you find the node with current_node.get_next() == None and do current_node.set_next(new_node).
Since this looks like homework, I don't want to straight out give you the answer. I will provide some pseudocode to get you started though
def insert(value):
let current = head
until current.next_node == None:
let current = current.next_node
let current.next_node = Node2(value)
Try this.
If the list is empty, set the head, and return. Else, loop "forward" through all the elements until you hit a None node. Once you do, set the appropriate next_node value.
class LinkedList(object):
def __init__(self, head=None):
self.head = head
def empty(self):
return self.head is None
def insert(self, data):
new_node = Node(data) # Want to insert this
if self.empty(): # Is this list empty?
self.head = new_node # Yes? Then add the head
else: # No? There are elements in the list
current_node = self.head # Save a moving reference
while not current_node.get_next() is None: # While there are forward nodes
current_node = current_node.get_next() # Move the reference forward
current_node.set_next(new_node) # Set the last node, thus inserting at the "end"
Let's write out what this does:
def insert(self, data):
previous_node = self.head # set to head of list
current_node = self.head # not used elsewhere
new_node = Node(data) # make a new node with the given data.
if previous_node is not None: # if the list isn't empty
previous_node.set_next(new_node) # point the list head to the new node
previous_node = self.head # does nothing; this was already the list head
Bottom line: this creates a new node and makes it the "next" node of the list head. That's all this does; your list will never be more than 2 nodes long. In fact, your list can't ever get a first node, because you have no code to insert into an empty list.
To fix this, you need a clear, adaptable algorithm, such as:
Create a new node.
Link the existing list behind the new node:
Set new.next to the current head node.
Point the list head to the new node.
Note that this works even with an empty list.
If you want a list that adds to the end, then also maintain a self.tail attribute that points to the last node in the list. Then your insert (append) looks like this:
Create a new node.
Link the new node to the existing tail:
if tail exists:
Set tail.next to the new node.
else:
Set head to the new node
Set tail to the new node.
Is this clear enough to implement?