Recursive LinkedList Insert Method - python

I am trying to create an Insert method which takes a parameter (position) and inserts a Node into a linkedlist at that given position. Thus, the new value would be the value of the parameter (val) and then the next node would take the original value and so forth. This also needs to utilize recursion.
I am currently trying to mess around with the conditionals and first say that if our index matches our desired position, we can set a new_node to our current node and then set our current node to a Node with our desired value. Then our next node can take the value of current node. After that, I have a conditional where if our index is greater, I set the next node to our current node.
I am currently not seeing the insertion change the values in the linkedlist and also getting an infinite loop, which I feel like has to do with when we reach the end of the LinkedList, it is not ending properly. I'd appreciate if anyone has any suggestions of how I could go about fixing this or if my logic is just wrong.
def insert(self, val, position, current=None, index=0, new_node=None):
if self._head is None:
self._head = Node(val)
return
if current is None:
current = self._head
if position > index and current.next is None:
current.next = Node(val)
return
if position == index and current is not None:
new_node = current.next
self._head = Node(val)
if position > index and current is not None:
new_node = current.next
self._head = current
if current is None:
return
self.insert(val, position, current.next, index + 1)

This case seems wrong:
if position == index and current is not None:
new_node = current.next
self._head = Node(val)
This would be the point where you'd want to insert the new node at current. Instead, you set a new_node variable (which does nothing, because you don't do anything else with it), and then you create a new self._head (which effectively deletes the rest of the list).
Your recursion never stops because on each recursion you increment index, but your base case is based on position > index:
# checking that we're at the end of the list
# AND position is ahead of index
if position > index and current.next is None:
current.next = Node(val)
return
self.insert(val, position, current.next, index + 1)
# next moves forward, index moves forward
Recursion should always move you closer to your base case, which means that either position should be increasing or index should be decreasing, but instead index is increasing while position stays the same, which means it's never possible to satisfy your base case.
I might write the function like this, with fewer parameters and only two base cases:
def insert(self, val, index=0, current=None):
if current is None:
current = self._head
if index <= 0 or self._head is None:
# Insert new val as head.
new_node = Node(val)
new_node.next = self._head
self._head = new_node
elif index == 1 or current.next is None:
# Insert new val as current.next.
new_node = Node(val)
new_node.next = current.next
current.next = new_node
else:
# Move down the list.
self.insert(val, index - 1, current.next)
In this implementation, index represents the position relative to current where we want to insert the new node, i.e. if index == 0 we want the new node to take the place of current, and if index == 1 we want the new node to take the place of current.next.
Our first base case checks for index == 0 on the initial call, in which case we want to replace self._head. The next base case checks for index == 1, in which case we want to replace current.next. Note that if current != self._head and index == 0 we'd be stuck because we don't have a pointer to the previous node in order to be able to replace current in the list, so that's why we want to do the current.next insertion at index == 1, before we advance to the next node.
In both base cases we also have an or that covers the case where we're at the end of the list (including the case where the list is empty) and it doesn't make sense to iterate forward regardless of what index is.
When we recurse, we move current forward by one spot, and decrement index by one, since we're one position closer to the desired location. Eventually index will reach 1, or we'll reach the end of the list, at which point we're done and we stop recursing.

Related

Finding lowest odd number in a Linked List in python

I am newly learning about Linked Lists and my assigment requires me to make a function that returns the lowest odd number...and I think I have done that in my get_min_odd function.
I believe my code is only checking the first and second element of the linked list so my question is how can I iterate through a linked list to find the lowest odd number? I would think a for loop but I don't understand what list I'm iterating through...which brings up another question, where are the elements in a linked list stored if not in a list, array, string, etc.?
class LinkedList:
def __init__(self):
self.head = None
#def.....
def add_all(self, a_list):
newNode = Node(a_list)
newNode.next = self.head
self.head = newNode
for i in a_list:
self.next = i
def add_all(self, a_list):
for i in range(len(a_list)):
self.add(a_list[i])
def get_min_odd(self):
lst = []
if self.head.data % 2 == 1:
lst.append(self.head.data)
elif self.head.next.data % 2 == 1:
lst.append(self.head.next.data)
else:
return 999
return min(lst)
my_list = LinkedList()
my_list.add_all([1, 4, -3, 6, 9, 2, 10001, 25, 19, 20])
print(my_list.get_min_odd())
A few issues:
You have two methods that are called add_all. This means the first one will be lost. You actually need an add method instead of that first add_all. And it should not take a list as value, but just a single numeric value. That method should not have to loop (Moreover, assigning i to the same next attribute over and over again makes no sense).
get_min_odd is indeed too much fixed on the first and second node. They might not even exist when the list is short or empty. To loop over a linked list, you need a name that first equals the head node, but then traverses along the next attributes to the next node, and then the next, ... until it reaches the end.
get_min_odd should not create a list with values. That defeats the purpose of a linked linked list. Instead that code should immediately update the minimum it has while traversing the linked list.
Here is the updated code for the two methods that needed correction:
def add(self, value): # name and arg name correction
newNode = Node(value)
newNode.next = self.head
self.head = newNode
and:
def get_min_odd(self):
result = None
node = self.head
while node: # as long as the end of the list is not encountered...
if node.data % 2 == 1:
# adjust the result to the minimum data encountered so far
result = node.data if result is None else min(result, node.data)
node = node.next # traverse to next node
return result

How in linked list variable is referencing value to previous variable from which it copied a value?

I am trying to learn linked list from geeksforgeeks I saw this code:
def append(self, new_data):
# 1. Create a new node
# 2. Put in the data
# 3. Set next as None
new_node = Node(new_data)
# 4. If the Linked List is empty, then make the
# new node as head
if self.head is None:
self.head = new_node
return
# 5. Else traverse till the last node
last = self.head
while (last.next):
last = last.next
# 6. Change the next of last node
last.next = new_node
In this code Node last is copying all the value from self.head then after traversing the list to the end where it appends the node in last. In this case how the reference of last automatically gets to self.head when we use it again for printing the linkedlist.
Like in following code this arr won't change then why is this happening in case of linked list?
arr = [3,4,5,6]
prr = arr
prr.append(7)
print(arr) #[3,4,5,6]

How to return the middle node of a linked list

I'm confused about how the while loop condition works in a linked list when checking for the middle node of a linked list
This is the correct code that I have for finding the middle node of a linked list
class Node(object):
def __init__(self, data):
self.data = data
self.next = None
class linkedList(object):
def __init__(self):
self.head = None
def append(self, data):
node = Node(data)
if self.head == None:
self.head = node
temp = self.head
while temp.next:
temp = temp.next
temp.next = node
def middle(self):
first = self.head
second = self.head
while second and second.next:
second = second.next.next
first = first.next
print(first.data)
If I change the while loop to
while second:
or
while second.next:
I receive an error that says
AttributeError: 'NoneType' object has no attribute 'next' on line 24
I'm just wondering why it's important to have both second and second.next
The solution works by using two pointers. First one takes 1 step at a time and second 2 steps at a time. However, in taking 2 steps there are two things to verify:
There is a next step that is valid
There is a step after the next step mentioned above
If you skip one check it will enter the loop but at the boundary condition it not find the next. To illustrate: If there are 4 nodes and you only check for second.next at the 3nd node you'll have second.next as valid and you'll enter the while loop but inside that you are directly accessing second.next.next
F,S
|
1 --> 2 --> 3 --> 4 --> None
For starters, your append method doesn't work, and gets stuck in an infinite while loop, since you don't exit out of append on adding the first element. The correct version is
def append(self, data):
node = Node(data)
if self.head == None:
self.head = node
return
else:
temp = self.head
while temp.next:
temp = temp.next
temp.next = node
As to your other question, we want to find the middle of the loop for both even and odd lists, the second.next covers the odd list case, and the second covers the even list case, since either the 2nd pointer will point to null, or it will be null itself, and if you use only one of them, you will get the error you described it, hence you need to have both conditions in the while loop

Removing the lastNode in a singly Linked List

Can I check
how do we remove a last node from a single linked list?
is it the same as how we remove the first node?
remove first Node
def deleteAtHead(self):
temp = self.head
self.head = self.head.next
delete temp
remove last Node
def deleteAtTail(self):
prev = None
temp = self.tail
self.tail= self.tail.prev
delete temp
You have to crawl back to the tail starting at the head.
The tail is the first node with no next: next is None.
Keeping track of the next-to-last (prev), you set its next to None.
def deleteAtTail(self): # remove_last would likely be a better name
""" removes the last element of the singly linked list
"""
temp = self.head
while(temp.next is not None):
prev = temp
temp = temp.next
prev.next = None
Setting prev.next to None removes the tail node (it will be garbage collected if there are no other references to it)
You cannot do remove operation in single-linked list with O(n) = c complexity because you don't have reference for previous list node. You will have to traverse to the end of the list remembering current and previous nodes and then you will have to trim previous node from the reference to it's next, which is the last node you are trying to remove. This will always have O(n) = n complexity for single-linked lists.
Because this is a singly linked list you will have to iterate through the list to get to the node just before tail. I think your function would simply need to be
def deleteAtTail(self):
temp = self.head
while(temp.next != None):
if temp.next == tail: #finds the node just before tail
break
temp = temp.next
temp.next = None
self.tail = temp
We set self.tail = temp because temp is equal to the node just before the end of the list. So we delete temp (by removing its last reference) and then set tail equal to node we earlier identified as the second to last node in the list as it will in fact be the new end of the list.
EDIT:
To address the concerns of some of the comments on this answer. It is good practice to maintain a head, tail and current pointer when dealing with a singly or doubly linked list. Knowing where tail is located is extremely valuable for making insertions into your list something that is computationally reasonable.
This means that your insertion method can go from this:
def insert(value):
temp = self.head
while(temp.next != None):
temp = temp.next
temp.next = Node()
temp.next.value = value
which has a time complexity O(n) to this:
def insert(value):
self.tail.next = Node()
self.tail.next.value = value
self.tail = self.tail.next
which is has a substantially more favorable time complexity of O(1).

Linked Lists Homework in Python

I have to do the following for my homework:
Suppose that your linked list is not empty, and is pointed to by the variable head. Write a method called findMin() that goes through the list and finds the least value (you can compare strings just like numbers) and returns that.
Suppose that your linked list is not empty, and is pointed to by the variable head. Write a method called getLast() that goes through the list and returns the last thing in the list. This is the value of the last node in the linked list chain.
Finish the append method. It runs to the end and attaches the new node to the last existing nodes in the chain. If there are no nodes in the chain yet, it sets self.head to that node. Beware! This is tricky! You might consult your textbook.
I attempted to solve the first one and I am just lost, I don't know if I am messing up the class structure or what (I just learned classes like 3 days ago)
Here's my attempt...
class LinkedList:
class Node:
def __init__(self,value):
self.value = value
self.next = none
def __init__(self):
self.head = None
def findMin(self):
currentNode = self.head
minNode = self.head
while currentNode != none:
if currentNode.next < currentNode:
minNode = currentNode
currentNode = currentNode.next
return minNode
As #mgilson mentioned in comments, there are a number of things wrong, and not all were listed.
I think you would benefit a lot from writing (in comments, why not) what you think each line is doing.
Let's start with
currentNode = self.head
I think this is trying to say "let's start at the head of the list, by setting currentNode to point to that".
As written, this appears to be accessing a member variable of the current node, called 'head'. But the Node class definition doesn't have a defined member called head! And... why do you need to do that? You are told "the current node is the head"!
So you probably mean
currentNode = self # start at the head of the list
Next:
minNode = self.head
This is saying "The node with the current known minimum is stored in this one"
As before, you probably mean:
minNode = self # The head currently has the minimum we know about
Then:
while currentNode != none:
First, if you use a syntax highlighting editor, straight away it will tell you that 'None' has a capital 'N'.
No other problems here.
Then:
if currentNode.next < currentNode:
minNode = currentNode
currentNode = currentNode.next
This is trying to say "if the value of the next node is less than the value of this one, then the minimum is now ..." ... actually what? It's saying it's the current one! But it's not: if this if statement is true, then the next one contains the minimum! And hang on, shouldn't we be comparing with minNode, not currentNode?
Seems like you mean
if currentNode.value < minNode.value:
minNode = currentNode # this node is now the smallest we know about
and this next line needs to be outside the if loop, because it moves us on to the next node:
currentNode = currentNode.next # move on to the next node
Phew, nearly there: now we have to return the minimum value not, the node that has the minimum value (read the instructions carefullly.
return minNode.value
HTH
Ok, let's do this homework!
Let Node be a list cell. It has two fields value and next (also called car/cdr or head/tail in functional languages).
class Node:
def __init__(self, value):
self.value = value
self.next = None
append(node, other) can be defined as follows: if node has no "next" node (i.e. it is the last), attach other right to this node. Otherwise, take the "next" node, and append (recursively) other to that one:
def append(self, other):
if self.next:
self.next.append(other)
else:
self.next = other
Now let's define two essential functionals: map and reduce. map applies a function to each node in order and returns the list of results:
def map(self, fn):
r = [fn(self.value)]
if self.next:
r += self.next.map(fn)
return r
reduce applies a function to each node and combines results into a single value:
def reduce(self, fn, r=None):
if r is None:
r = self.value
else:
r = fn(r, self.value)
if self.next:
return self.next.reduce(fn, r)
return r
Now you're ready for the homework.
Create a list:
lst = Node(1)
lst.append(Node(8))
lst.append(Node(3))
lst.append(Node(7))
print all values:
print lst.map(lambda x: x)
find the last value:
print lst.reduce(lambda r, x: x)
find the max value:
print lst.reduce(max)
etc
Let us know if you have questions.

Categories

Resources