Python linked list remove duplicate - python

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.

Related

Retrieving Index from Linked List

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.

Pointer in doubly linked list pointing at the wrong thing

I'm trying to convert a string into a doubly linked list. I want to be able to traverse it, find a match, and then insert another linked list at a certain position. However, I'm having trouble with my pointers. I have 'current.prev', 'current', and 'current.next' pointers that should all be pointing at the nodes in that order. Instead, my .prev and current pointers always point to the same thing. I need to be able to point at the different parts of the linked list, otherwise I won't be able to traverse back to a certain spot so I can insert a new linked list.
this is the code that creates doubly linked lists for me:
#-----------------------doubly-linked-lists-----------------------#
class NodeD:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLL:
def __init__(self):
self.head = None
def append(self, new_data):
new_node = NodeD(new_data)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
return
def printList(self, node):
while node:
print(node.data, end=""),
last = node
node = node.next
this is the code that makes a string into a linked list:
#making-linked-lists-from-strings#
def dnaToLL(dnainput):
dnaLL = DoublyLL()
head = NodeD(dnainput[0])
dnaLL.head = head
current = dnaLL.head
for i in range(1, len(dnainput)):
current.prev = current
current = current.next
current.next = NodeD(dnainput[i])
return dnaLL
I've been using this to test where the pointers are pointing:
dnaLL = dnaToLL(DNA)
dnaLL = dnaLL.head
print(dnaLL.prev.data +dnaLL.data+dnaLL.next.data)
but with this input:
S1 = "neho"
DNA = "imfinehowru"
I get this output:
iim
any idea where I've gone wrong? I am assuming it's where I've tried to make strings into doubly linked lists, but any other order that I put the pointers in gives me errors.
Thank you for your help!
The problem is that whe you do current.prev = current, you're saying "the previous node is the current node" - not the actual previous node.
Keep in mind that the first node in the list should have .prev = None. You'd only want to set .prev for future nodes. This modification should fix your dnaToLL() function:
def dnaToLL(dnainput):
dnaLL = DoublyLL()
head = NodeD(dnainput[0])
dnaLL.head = head
current = dnaLL.head
for i in range(1, len(dnainput)):
# note how the order is changed
current.next = NodeD(dnainput[i])
current.next.prev = current
current = current.next
return dnaLL

Understanding a Linked List implementation in Python

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!

Python - Linked List node comparision requires additional dereferencing

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?

Trouble with forward linked list

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?

Categories