I'm working through Cracking the Coding Interview 6th ed and am unsure of their definition of 'next'
Their code defining "Linked List" can be found here. I'm trying out the second exercise, which is to find the kth from the end element of a random linked list.
My code:
from LinkedList import LinkedList
def kth_to_last(ll, k):
num_seen = 0
length_list = count_length(ll)
val = ll.head
# ISSUE IS HERE
while val.next != None:
print 'hi'
val.next = val.next.next
"""
while num_seen < (length_list - k):
val = val.next
num_seen += 1
"""
return val.next
# Counts length of LL
def count_length(ll):
val = ll.head
count = 1
while val.next != None:
count += 1
val.next = val.next.next
return count
ll = LinkedList()
ll.generate(10, 0, 99)
print(ll)
kth_to_last(ll, 3)
It's counting through the list just fine, but for the first definition, I can't get it to move through the linked list (it won't print 'hi' at all).
I plan to do something like I have commented out (they also have 'tail' defined so I might try that out), but I'm confused why I can move through the list just fine within 'count_length,' but then I can't seem to move through it within 'kth_to_last'?
Edit: To clarify, if I print val.next within 'kth_to_last' it has a value of 'None'
Edit2:
If I comment out the "count_length," the next proceeds just fine. Could someone explain to me why calling this function alters next. Has it stuck me at the end of the list?
My code:
def kth_to_last(ll, k):
"""
num_seen = 0
length_list = count_length(ll)
"""
# Start at head
val = ll.head
while val.next != None:
print val.next
val = val.next
This prints the list just fine
You should do val = val.next instead of val.next = val.next.next. The way you're doing it, the list will be truncated to a single element when you call count_length. Because you do count_length at the top of kth_to_last, by the time you get around to walking your list (where your 'hi' is), the list has already been reduced to a single node.
Remember, a linked list is a structure where each node's next property is a pointer to the next node. Your code is modifying the value of next, which is changing the structure of your linked list.
When you process a linked list (in count_length, or in kth_to_last), what you want to do is point yourself at each node in turn. You're not trying to modify the nodes themselves, so you won't assign to their value or next attributes. The way to do this is to change what your pointer (val) is pointing at, and the thing that you want it to point at next is the next node along. Therefore:
val = ll.head
while val is not None:
# do something with val here
val = val.next
Related
I am creating the Josephus problem using a circular doubly linked list. I am getting an Attribute error, which I assume is because my current_node (first node) does not have a .prev yet.
I understand that the prev of my first node should point to the next of my last node to create a circular doubly linked list.
Can someone guide me on whether I have correctly identified the error? If yes, how can I rectify it?
If not, then what are the other ways I can correct the error?
#Initialize the node
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
def remove(self, n):
print("Student " +str(n)+ " was removed")
class Circle:
# Initializing the DLL
def __init__(self):
self.head = None
self.tail = None
#Inserting elements 2 to n in the dll
def insert_after(self, x, data):
y = Student(data) # make a new Node object.
z = Student(data)
z = x.next
y.prev = x
y.next = z
x.next = y
z.prev = y
def josephus_solution(self, dllist, n, k):
no_of_active_nodes = n
current_node = Student(1)
#last_node = Student(n)
#print(current_node.prev)
for i in range(2, n + 1):
dllist.insert_after(current_node, i)
count = 0
#print(current_node.data)
while (current_node.next != current_node.prev):
#print(current_node.next.prev)
current_node = current_node.next
count += 1
#print(current_node.data)
if (count == k):
current_node.remove(current_node.data)
current_node.prev.next = current_node.next
current_node.next.prev = current_node.prev
count = 0
no_of_active_nodes -= 1
#print(no_of_active_nodes)
if (no_of_active_nodes == 1):
print("Student" + str(current_node.data) + "Recieves the scholarship")
return current_node.data
dllist = Circle()
n = 5 #int(input('Input number of people (n): '))
k = 2 #int(input('The nth person will be executed. Input k: '))
ans = dllist.josephus_solution(dllist, n, k)
Error
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/tmp/ipykernel_24/3762059582.py in <module>
54 n = 5 #int(input('Input number of people (n): '))
55 k = 2 #int(input('The nth person will be executed. Input k: '))
---> 56 ans = dllist.josephus_solution(dllist, n, k)
/tmp/ipykernel_24/3762059582.py in josephus_solution(self, dllist, n, k)
32 #print(current_node.prev)
33 for i in range(2, n + 1):
---> 34 dllist.insert_after(current_node, i)
35 count = 0
36 #print(current_node.data)
/tmp/ipykernel_24/3762059582.py in insert_after(self, x, data)
24 x.next = y
25
---> 26 z.prev = y
27
28 def josephus_solution(self, dllist, n, k):
AttributeError: 'NoneType' object has no attribute 'prev'
The direct reason for the error is that z is None and your code can therefore not access any prev attribute of it in the line z.prev = y. The cause is that when the first node is created, its prev and next attributes are initialised to None, and when this node is passed as x argument to insert_after, then with z = x.next a None value is assigned to z.
There are several issues with your approach:
In insert_after it makes no sense to call Student twice, since you want to insert one node, not two
Although your Circle class has a head and a tail attribute, they are never used after their initialisation to None, so there is actually nothing in the Circle instance that helps you to maintain the list. You might as well define all methods on the Student class.
The while condition in josephus_solution is probably intended to test that only one node remains in the list, but it actually verifies whether there are two left. Admittedly, this works when current_node is a node that was just deleted, but then the returned data is the data of the deleted node and not the remaining active node, and current_node is not a just-deleted node, then this condition will make the loop exit when it still has two active nodes.
Some other remarks:
As you call josephus_solution as a method, it should not be necessary to also pass an instance as argument. self is already available to that method
I would split the creation of the nodes into a separate -- generic -- method, and let josephus_solution work on an existing linked list.
As in a circular list none of the next or prev attributes should ever be None, you'll make things easier when initialising them to self instead of None.
The remove method should not be responsible for printing a message, but should on the other hand be responsible for rewiring the prev and next references. At any rate, there is no need for this method to get an argument, as it knows its own attributes through self.
In the original Josephus problem, the first node is numbered as 1, so with k=2 the first node to remove is the one with number 2. Your code would first delete the one with number 3. To align with the original problem, move the current_node = current_node.next statement to be the last line in the body of the loop. This also helps to be sure that current_node is never a just-deleted node when the while condition is evaluated.
As your while condition already takes care of when to stop, there should be no reason to keep track with a no_of_active_nodes variable.
There are more remarks to make, but these are what I believe the most important.
Here is a correction of your code, taking all of the above into account:
class Student:
def __init__(self, data):
self.data = data
# Better make prev/next self references to avoid None:
self.next = self
self.prev = self
def remove(self):
# Perform the remove here
self.prev.next = self.next
self.next.prev = self.prev
def insert(self, data):
# Should only create one node, not two
node = Student(data)
node.next = self
node.prev = self.prev
self.prev = node.prev.next = node
# Handy method to create a circular list from values
#classmethod
def fromiterator(cls, iterator):
head = cls(next(iterator))
for value in iterator:
head.insert(value)
return head
def josephus_solution(self, k):
count = 0
current_node = self
while current_node.next is not current_node: # This is how to test for 1 node
count += 1
if count == k:
current_node.remove() # No argument
print("Student " +str(current_node.data) + " was removed")
count = 0
current_node = current_node.next # do this at the end of the iteration
# No need to keep track of active nodes ...
return current_node
# These are the parameters of the "original" problem (see Wikipedia)
n = 41
k = 3
dllist = Student.fromiterator(iter(range(1, n+1)))
# Don't pass the list as argument: it already is available as `self`
dllist = dllist.josephus_solution(k)
# Perform output in the main code, not inside method
print("Student " + str(dllist.data) + " recieves the scholarship")
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
Example 1:
Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:
Input: 1->1->1->2->3
Output: 2->3
I have tried and succeeded in my code with most cases, the only case I am missing is when the list ends in duplicates and there are non duplicates throughout.
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
first = head
if first is None:
return []
second = head.next
if second is None:
return first
first.next = second
if first.val == first.next.val:
while first.val == first.next.val:
if first.next.next is None:
return []
first = first.next
return self.deleteDuplicates(first.next)
else:
first.next = self.deleteDuplicates(first.next)
return first
The error I get for [1,2,3,4,4] is "AttributeError: 'list' object has no attribute 'val'".
You can do this in $O(n)$ time and $O(n)$ space (worst case) by just keeping track of what you've seen so far, assuming your data is hashable.
from collections import defaultdict
def mark_duplicates(current_node, seen_so_far):
next = current_node.next
seen_so_far[current_node.data] += 1
if next is None: return seen_so_far
return mark_duplicates(next, seen_so_far)
def remove_duplicates(current_node, seen):
next = current_node.next
prev = current_node.prev
if seen[current_node.data] > 1:
if prev is not None: prev.next = next
if next is not None: next.prev = prev
# No need to delete current_node, the GC will do it
if next is not None: remove_duplicates(next, seen)
notes = mark_duplicates(head_of_list, defaultdict(int))
remove_duplicates(head_of_list, notes)
A defaultdict(int) is just a dictionary that'll return 0 when you try to access a key that doesn't exist. So this counts how many times each value appears, then removes everything that's shown up more than once.
The code works fine, I just needed to change return [] to simply "return" as [] wasn't being recognized as an empty linked list, but rather just an empty list. Thanks to Devesh Kumar Singh for recognizing this error, but he didn't post it as an answer so I am posting it on his behalf.
I've been playing with BST (binary search tree) and I'm wondering how to do an early exit. Following is the code I've written to find kth smallest. It recursively calls the child node's find_smallest_at_k, stack is just a list passed into the function to add all the elements in inorder. Currently this solution walks all the nodes inorder and then I have to select the kth item from "stack" outside this function.
def find_smallest_at_k(self, k, stack, i):
if self is None:
return i
if (self.left is not None):
i = self.left.find_smallest_at_k(k, stack, i)
print(stack, i)
stack.insert(i, self.data)
i += 1
if i == k:
print(stack[k - 1])
print "Returning"
if (self.right is not None):
i = self.right.find_smallest_at_k(k, stack, i)
return i
It's called like this,
our_stack = []
self.root.find_smallest_at_k(k, our_stack, 0)
return our_stack[k-1]
I'm not sure if it's possible to exit early from that function. If my k is say 1, I don't really have to walk all the nodes then find the first element. It also doesn't feel right to pass list from outside function - feels like passing pointers to a function in C. Could anyone suggest better alternatives than what I've done so far?
Passing list as arguments: Passing the list as argument can be good practice, if you make your function tail-recursive. Otherwise it's pointless. With BST where there are two potential recursive function calls to be done, it's a bit of a tall ask.
Else you can just return the list. I don't see the necessity of variable i. Anyway if you absolutely need to return multiples values, you can always use tuples like this return i, stack and this i, stack = root.find_smallest_at_k(k).
Fast-forwarding: For the fast-forwarding, note the right nodes of a BST parent node are always bigger than the parent. Thus if you descend the tree always on the right children, you'll end up with a growing sequence of values. Thus the first k values of that sequence are necessarily the smallest, so it's pointless to go right k times or more in a sequence.
Even in the middle of you descend you go left at times, it's pointless to go more than k times on the right. The BST properties ensures that if you go right, ALL subsequent numbers below in the hierarchy will be greater than the parent. Thus going right k times or more is useless.
Code: Here is a pseudo-python code quickly made. It's not tested.
def findKSmallest( self, k, rightSteps=0 ):
if rightSteps >= k: #We went right more than k times
return []
leftSmallest = self.left.findKSmallest( k, rightSteps ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k, rightSteps + 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
EDIT The other version, following my comment.
def findKSmallest( self, k ):
if k == 0:
return []
leftSmallest = self.left.findKSmallest( k ) if self.left != None else []
rightSmallest = self.right.findKSmallest( k - 1 ) if self.right != None else []
mySmallest = sorted( leftSmallest + [self.data] + rightSmallest )
return mySmallest[:k]
Note that if k==1, this is indeed the search of the smallest element. Any move to the right, will immediately returns [], which contributes to nothing.
As said Lærne, you have to care about turning your function into a tail-recursive one; then you may be interested by using a continuation-passing style. Thus your function could be able to call either itself or the "escape" function. I wrote a module called tco for optimizing tail-calls; see https://github.com/baruchel/tco
Hope it can help.
Here is another approach: it doesn't exit recursion early, instead it prevents additional function calls if not needed, which is essentially what you're trying to achieve.
class Node:
def __init__(self, v):
self.v = v
self.left = None
self.right = None
def find_smallest_at_k(root, k):
res = [None]
count = [k]
def helper(root):
if root is None:
return
helper(root.left)
count[0] -= 1
if count[0] == 0:
print("found it!")
res[0] = root
return
if count[0] > 0:
print("visiting right")
find(root.right)
helper(root)
return res[0].v
If you want to exit as soon as earlier possible, then use exit(0).
This will make your task easy!
Is there a way to detect the first element of a circular list in Python? In Java and C++ you can just establish a pointer to the first element.
Question I came across: Given a circular linked list, implement an algorithm which returns the node at the beginning of the loop.
A circular linked list has no true beginning & end. But from your comment, I think you want to detect when you reach the element you started with while looping through the list.
#The structure of ListNode
class ListNode:
def __init__(self, val):
self.val = val
self.next = None
# Supposes you have a circular linked list and you have a reference to head. You can do as follows to print the whole list.
current = head.next
while current != head: # stop when it comes back to head
print current.val
current = current.next
I think you need a depth first search to answer this in generality, here's the sort of thing:
a = [1,2,3]
b = [4,5,6]
a[1] = b
b[2] = a
def is_list(l):
try:
it = iter(l)
return l
except TypeError:
return None
def dfs(a, colors):
l = is_list(a)
print 'is_list:', l
if l:
if colors.has_key(id(l)):
print 'cycle detected'
return l
colors[id(l)] = ''
for ll in l:
dfs(ll, colors)
colors = {}
dfs(a, colors)
my mission is to build a function which return the number of "uncles" in a given binary tree A node is an uncle only if his brother(the second child of his father) has children.
This is my code:
"
def uncle(root,count = None):
if count is None:
count = [0]
a,b = 0,0
if root.left:
if root.left.left or root.left.right:
a = uncle(root.left)
a = 1
if root.right:
if root.right.right or root.right.left:
b = uncle(root.right)
b = 1
if b > 0:
count[0] += 1
if a > 0:
count[0] += 1
return count[0]
"
And this is the bin_tree class:
"
Class bin_tree():
def __init__(self,data, left_child = None, right_child = None):
self.data = data
self.left = left_child
self.right = right_child
"
My problem is this:
When I switched the lines:
a = uncle(root.left)
a = 1
It didn't work(meaning = the variable a has changed back its value to 0 for some reason), and I absolutely doesn't know why..I thought it should have worked because
it doesn't matter if I call in recursion to the function first or say a = 1 firstly.
Can someone help me please?
I haven't tested this but it should work. The code you wrote can be cut down a lot.
For starters you have count as a list when it doesn't really need to be.
Your a and b never get used so there is no point in assigning anything to them. That is probably why you get an error regardless of what value you give to a or b.
Then you don't need to check if a or b are greater than 1. You can just increment count when you find out there is another uncle in the tree.
However the main reason that it is not working is that everytime you call uncle you are not passing your new count. This means that it takes the default value of which you have given as None. This then causes the list to reset to [0]. Well actually a new list is been created which is different from the previous one even though they have the same name. So although you may think you are updating the same count, you are not. This is a scope problem.
def uncle(root,count=0):
if root.left:
if root.left.left or root.left.right:
count = uncle(root.left, count)
count += 1
if root.right:
if root.right.right or root.right.left:
count = uncle(root.right, count)
count += 1
return count