Finding lowest odd number in a Linked List in python - 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

Related

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 interact head of linkedlist outside the LinkedList class in Python?

I am trying to solve the following problem on algoexpert:
Shift Linked List
Write a function that takes in the head of a Singly Linked List and an integer
k, shifts the list in place (i.e., doesn't create a brand new
list) by k positions, and returns its new head.
Shifting a Linked List means moving its nodes forward or backward and wrapping
them around the list where appropriate. For example, shifting a Linked List
forward by one position would make its tail become the new head of the linked
list.
Whether nodes are moved forward or backward is determined by whether
k is positive or negative.
Each LinkedList node has an integer value as well as
a next node pointing to the next node in the list or to
None / null if it's the tail of the list.
You can assume that the input Linked List will always have at least one node;
in other words, the head will never be None / null.
Sample Input
head = 0 -> 1 -> 2 -> 3 -> 4 -> 5 // the head node with value 0
k = 2
Sample Output
4 -> 5 -> 0 -> 1 -> 2 -> 3 // the new head node with value 4
The outline that the problem gives for the code is the following:
class LinkedList:
def __init__(self, value):
self.value = value
self.next = None
def shiftLinkedList(head, k):
#Write your code here.
pass
I suppose my background on linked list is very limited because from every resource I've read on linked lists, the general outline for it requires the node class and to have all the methods for rotating or shifting inside the LinkedList class.
I assume the head parameter for the function is going to be an integer that denotes the position of the list but how do I refer the head back to the original list? I already have the code written in my Thonny editor but I wrote the function inside the LinkedList class and simply called for it after making my list.
For example:
class Node:
def __init__self(data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def push(self, newhead):
newnode = Node(new_data)
newnode.next = self.head
self.head = newnode
list1 = LinkedList()
list1.head = Node(1)
e2 = 2
e3 = 3
list1.head.next = e2
e2.next = e3
Only once I've established my linked list can I create a method inside the class to shift or rotate it. Or am I wrong?
I tried creating a function the way the algo wanted, but I am still stuck. I think what I am really confused about is whether the argument head is an integer or a LinkedList?
Here is my complete attempt:
class Node:
def __init__(self, data):
self.data = data #assign data
self.next = None #initialize next as null
class LinkedList:
#function to initalize the linked list object
def __init__(self):
self.head = None
def printList(self):
temp = self.head
while(temp):
print(temp.data)
temp = temp.next
def moveToFront(self):
tmp = self.head
sec_last = None
if not tmp or not tmp.next:
return
while tmp and tmp.next:
sec_last = tmp
tmp = tmp.next
sec_last.next = None
tmp.next = self.head
self.head = tmp
def shiftList(head, k):
if not head:
return
tmp = head
length = 1
while(temp.next != None):
tmp = tmp.next
length += 1
if(k>length):
k = k%length
k = length - k
if(k==0 or k==length):
return head
current = head
cmt = 1
while(cmt < k and current != None):
current = current.next
cmt += 1
if(current==None):
return head
kthnode = current
tmp.next = head
head = kthnode.next
kthnode.next = None
return head
I assume the head parameter for the function is going to be an integer that denotes the position of the list
No, the head of a list is a LinkedList instance. So head.value is the value of the first node in the list, and head.next is a reference to the second node in the list.
In your attempt you have changed the definition of LinkedList from the original, and created another class, called Node, which really is what LinkedList is supposed to be (except that you call a property data instead of value). It is understandable that you did this, as you wanted to have a kind of container class for the whole linked list, and reserve a separate class Node for what concerns a single node in that list. But this code challenge does not work with such a container class.
It just works with one class that represents a node, but through its next member the whole list is inferred from it. Moreover, they want the function to return a reference to the node that has become the head of the list once the shifting has completed. This is a different way of working than with a container class, in which you would mutate the head member, and would not return anything: the caller would just access the list through the modified head member.
So, what you intended to do is not inherently wrong, it just is not the data structure that this code challenge is working with. You should go with it.
what I am really confused about is whether the argument 'head' is an integer or a linkedlist
It is a node, but the list is inferred from it. It can be a bit confusing that they call their class LinkedList and not Node, but it is a matter of how you look at it.
So here is how you could solve it:
First find out what the last node is in the list. For this you will have to start from the head (you have nothing else) and step through the list until you bump into its end. Also maintain a counter so that at the end you know how many nodes there are in the list.
Then create a link from that tail node to the head node, so that the list now becomes circular: it has no end anymore
Find out which node comes k steps before the tail node in this cyclic list. As it is not possible to walk backwards in a list, you should actually walk size-k steps forward, and so you need the size of the list which you determined in the first step.
The node after that found node should become the head node.
Make that node itself the new tail node: break the link it has with the node after it (the new head).
Return the new head reference
One more thing should be done to protect this code against huge values for k. Let's say the list has 6 elements, and k is 600, then of course it makes no sense to run over the cyclic list 600 times, as we can predict that you'll end where you started! 600 is a multiple of 6... So to make this efficient, you need to reduce k to a number that is less than the size of the list. The formula for knowing how many steps forward you need to take is -k % size.
Here is the implementation of this idea:
def shiftLinkedList(head, k):
if not head:
return # Nothing to do
# Determine size of the list, and its tail node
size = 1
tail = head
while tail.next:
size += 1
tail = tail.next
# Temporarily make the list cyclic
tail.next = head
# Find out where to cut the cycle
for _ in range(-k % size):
tail = tail.next
head = tail.next
# Cut the cycle
tail.next = None
return head

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