Linked list with two pointers trying to catch a loop - python

Hello I need help on detecting a loop and returning False on a linked list but first, let me explain how this linked list looks like:
This would be the node class:
class Node:
def __init__(self, next = None, stairs = None):
self.next = next
self.stairs = stairs

Since your step size doesn't change the code will, once it has visited a node twice visit it over and over again. This is due to the fact that it will make the same decisions at every node that follows the repeated node. Thus you only have to detect if (at least) one node has been visited twice. Two common ways of doing this are the following:
Count how many nodes there are in the linked list (i.e. take a new variable N=0 and increment it on every run of the first loop of the play function). Then in the second loop count how many nodes have been visited. Once that number is larger than N you know that at least one node has been visited at least twice and therefore you have detected a circle and need to break out of the loop (or return).
def play(first, step):
'''(LinkedList, int) -> bool
'''
# locates the end_node
end_list = first
found = False
# Used to find Node population
N = 0
while end_list.next != None and found != True:
if end_list.next == first:
found = True
else:
end_list = end_list.next
N = N + 1
stepcount = 1
count = 1
current = first
winner = False
loop = False
# Goes through the linked list
while current != None and winner != True and loop != True:
# If count == step, then we check if we are at the last square or
# else if we are on a stairs then we may use them.
# If none of them are found then we itterate normally making count back to 1.
# If stepcount passes the overall N (population of all the nodes), then it will detect a loop
if stepcount > N:
loop = True #one node has been visited more than once so there is a loop
elif count == step:
if current == end_list:
winner = True
elif current.stairs:
current = current.stairs
count = 0
else:
current = current.next
count = 0
stepcount = stepcount + 1
else:
current = current.next
count = count + 1
return current == end_list
Keep track of which nodes you have visited: To do this you can just add a new attribute to each node you visit (i.e. node.visited = True). At the beginning (i.e. in the initial loop) of your function play you'd want to make sure that the structure is clean, i.e. set node.visited = False. If you don't want the nodes to be changed after the function play has been called you can just delete them in another loop at the end (i.e. del node.visited).

Possibilities:
Use some sort of node or path disabling, as I suggested last time. Your posting doesn't mention why that doesn't work for you.
Count how many steps you've taken and compare against the node population (call it N). If you take n steps without reaching the end, then you're in a loop.
Try to construct a new list that contains the steps from where you start to the final node. This is a strictly linear linked list, such as START -> 3 -> 6 or START -> 1 -> 2 -> 3 ... If you try to add a node that's already in this linked list, then you have a loop.

Related

Python recursively jumping through a list by a given index jump or less while avoiding zeros

I am trying to write a function that has only one argument, a list of positive integers.
It has to be recursive and find whether you can get from the start to the end while avoiding landing on 0s. It has to start at first value list[0] but you can jump by either the full amount or less.
For example
list1 = [3,1,2,0,4,0,1]
list2 = [3,2,1,0,2,0,2]
List 1 should return True as you can jump from 3 to 2 to 4 to 1.
List 2 should return False as it does not matter how you jump, you will land at the first 0.
Here is my attempt so far:
def check_level(level):
jump = level[0]
if level[-1] == 0:
return False
elif jump == 0:
return False
elif jump == 0:
return False
elif len(level) > 1:
if level[jump] > len(level):
return True
elif level[-1] == 0:
return False
elif level[0] != 0:
if level[jump] == 0:
level[0] -= 1
return check_level(level)
else:
jump_index = level.index(level[jump])
new_level = level[jump_index:]
return check_level(new_level)
else:
return True
It does not work with all examples and with others it comes up with an error:
if level[jump] > len(level):
TypeError: '>' not supported between instances of 'list' and 'int'```
I am out of ideas on how to approach this and whether my approach is failed from the start... I hate recursion, hence I need to practice it.
The general logic is:
"If you can jump to a True position, the current position is also True"
Implemented like this:
def checklevel(lst):
if not lst:
# no list is True (we are done)
return True
return any( checklevel(lst[i:]) for i in range(1,lst[0]+1))
list1 = [3,1,2,0,4,0,1]
list2 = [3,2,1,0,2,0,2]
assert checklevel(list1)
assert not checklevel(list2)
Note that this is a terrible solution for large lists, you should try a iterative dp-table in that case.
After advancing by a number of positions, you simply have to recurse with the rest of the list. Do it for distances from 1 to the value of the first element and you're done:
def jumpable(A):
if not A: return True # reaching end wins
return any(jumpable(A[i+1:]) for i in range(A[0])) # try all distances
ouput:
print(jumpable([3,1,2,0,4,0,1])) # True
print(jumpable([3,2,1,0,2,0,2])) # False
To avoid creating memory wasting temporary lists you can pass an index down (instead of a sublist) and use it as the starting point for the next level of recursion:
def jumpable(A,i=0):
if i>=len(A) : return True # reaching end
return any(jumpable(A,i+j+1) for j in range(A[i])) # try all distances
With a default value for i=0, you function still has only one parameter when called from the outside.

Returning Boolean value error when traversing a list

I am trying to find if a list is a strictly increasing sequenced if 1 and only 1 element from it is removed. This works for some lists but does not work for others when there isn't any apparent difference between the lists. And for large lists, it exceeds the execution time limit. This is my code:
def almostIncreasingSequence(sequence):
for i in range(len(sequence)):
new_seq = sequence.copy()
del new_seq[i]
if all(i < j for i, j in zip(new_seq, new_seq[1:])):
output = True
else:
output = False
return output
I'm creating a copy of the input list. Then, I'm removing the element i and then returning True or False depending on whether the list is a strictly increasing sequence. I am creating the copy of the list inside the for-loop to make sure that I am removing only one element. These are some of the test runs where the code does not return the appropriate value:
Input:
sequence: [10, 1, 2, 3, 4, 5]
Output: false
Expected Output: true
Input:
sequence: [1, 2, 5, 3, 5]
Output: false
Expected Output: true
And in this test case, the code exceeds the execution time limit:
Input:
sequence: [-9996, -9995, -9994, -9993, -9991, -9989, -9987, -9986, -9985, -9983, -9982, -9980, -9978, -9977, -9976, -9975, -9974, -9972, -9968, -9966, -9965, -9961, -9957, -9956, -9955, -9954, -9952, -9948, -9942, -9939, -9938, -9936, -9935, -9932, -9931, -9927, -9925, -9923, -9922, -9921, -9920, -9919, -9918, -9908, -9905, -9902, -9901, -9900, -9899, -9897, -9896, -9894, -9888, -9886, -9880, -9878, -9877, -9876, -9874, -9872, -9871, -9870, -9869, -9868, -9867, -9865, -9857, -9856, -9855, -9854, -9853, -9852, -9851, -9849, -9848, -9846, -9845, -9843, -9842, -9841, -9840, -9837, -9834, -9828, -9826, -9824, -9823, -9820, -9816, -9814, -9812, -9811, -9810, -9809, -9807, -9806, -9804, -9803, -9801, -9800]
Output: undefined
Expected Output: false
Your loop will always overwrite (with returning False or True) the previous result in each iteration. So it actually "forgets" about any violation in the previous iteration. Only the last iteration determines the output, so your algorithm is wrong.
Secondly, even if you would fix that error (by exiting the loop if the new list is not sorted), this algorithm represents a time complexity of O(n²), because in each iteration:
a copy of the list is made with .copy()
a del is performed, requiring a shift of all values that follow it
a new copy of the list is made with new_seq[1:]
zip is called, creating yet another list
all is called, iterating that new list
All of these actions are killing the performance.
Instead you should only perform one iteration over the list, and solve the problem without any nested iterations. One clue is that you don't actually have to perform the removal of the element. You only need to check the situation as if it were removed.
An efficient algorithm only needs to look at three values: the currently iterated value, and the two preceding values. If those three are not in order, you can determine which one should be removed, but only represent that removal in those three variables (keep the list untouched):
def almostIncreasingSequence(sequence):
beforePrev = prev = float('-inf')
allowExceptions = True
for curr in sequence:
if curr <= prev: # Order is not maintained:
if not allowExceptions: # It's not the first time
return False
allowExceptions = False
# Decide whether to skip the current or previous value
if curr > beforePrev:
prev = curr;
else: # Normal case: keep track of two preceding values
beforePrev, prev = prev, curr
return True
Explanation
Three list values are kept in separate memory during the traversal of the list: curr, prev and beforePrev. The latter two lag behind the curr value. So at each iteration, curr gets the current list value, and at the end of the iteration, we shift the values: beforePrev ← prev ← curr. So essentially, these three values correspond to the 3 most recently visited values -- at least when there is no anomaly in the list.
So let's say at a certain moment you find an anomaly: you find a position where curr <= prev, i.e. where the sequence is not strictly increasing; then it is clear one of the values has to be removed: either curr or prev should be removed to have any hope that the list can be "fixed".
It could be that the algorithm had already decided that a value had to be removed at an earlier iteration (allowExceptions is False): but we are only allowed to remove one value from the list, so in that case we decide that there is no solution and return False
But if this was the first time we got into this situation, then we should decide whether to identify curr or prev as offending value. We may assume that beforePrev and prev are in the correct order (we verified that in the preceding iteration), so there really are only two possibilities left for the relative ordering of beforePrev prev and curr: either curr > beforePrev, or not.
If curr > beforePrev, then those two are placed in a good ordering relative to each other, so if we would remove prev from between those two, we would restore that part of the list into an increasing ordering.
If curr <= beforePrev, then they are not in a good ordering relative to each other, so we should remove curr to restore the increasing ordering.
To save time we will not actually perform the removal of the chosen item from the list: we will just apply the result of that removal to the variables beforePrev, prev, and curr, so that in the next iteration they have values as if the list had one item removed:
As we removed an element, beforePrev should remain the same in the next iteration of the loop: so we don't touch its value, like we would normally do.
If we remove prev, then the new prev becomes what curr is now. If we remove curr, there is nothing to do: prev also remains the same for the next iteration, and curr will get its new value any way at the start of the next iteration.
Because we "deleted" an item we also set allowExceptions to False, so that we know that we are not allowed to perform a second deletion.
Remains to say that at the first iteration, we don't really have a beforePrev and prev, so we set them to -infinity, so that in a first iteration there will be no decision to delete yet.
You are returning the value of the last check, i.e. when you remove the last item in the list. Change it to return True in the first match or False if nothing matched
def almostIncreasingSequence(sequence):
for i in range(len(sequence)):
new_seq = sequence.copy()
del new_seq[i]
if all(i < j for i, j in zip(new_seq, new_seq[1:])):
return True
return False

Retrieve Graph Lowest Height Node with Filter

Given a Tree T, sometimes binary or not, I need to retrieve the lowest Node that matches a criteria in each branch.
So, I need to retrieve a list (array) of those red marked nodes, where they label is equal to "NP" node.label() == 'NP'.
Actually I'm using NLTK Tree (nltk.tree.Tree) data structure, but you can post the pseudocode only, and I can implement it.
Here is the code that I've tried:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
if type(subtree) == nltk.tree.Tree:
t = traverseTree(subtree)
if subtree.label() == 'NP' and len(t) == 0: h.append(subtree)
return h
you have a conditional that if the there are no better candidates for your specification then append subtree, but what if len(t)>0? in that case you want to keep the nodes found in sub calls:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
if type(subtree) == nltk.tree.Tree:
t = traverseTree(subtree)
#RIGHT HERE!! need to extend by t or the other found nodes are thrown out
h.extend(t)
if subtree.label() == 'NP' and len(t) == 0:
h.append(subtree)
return h
Keep in mind that if t is always empty you would append all the valid nodes one level below, but any end-of-branch "NP" nodes will be found and returned in t so you want to pass them up a level in the recursion.
Edit: the only case where this would fail is if the top level node is "NP" and there are no sub-nodes of "NP" in which case tree should be added to h:
#after for loop has finished
if len(h) == 0 and tree.label() == "NP":
h.append(tree)
return h
edit2: if you add tree to h then the check for subtrees will never actually come true since they are checking the same node with the same conditionals just in differnt levels of recursion, so you can actually just write the function like this:
def traverseTree(tree):
if not isinstance(tree, nltk.Tree): return []
h = []
for subtree in tree:
#no need to check here as well as right inside the call
h.extend(traverseTree(subtree))
if tree.label() == 'NP' and len(h) == 0:
h.append(tree)
return h

Breadth search algorithm implementation

I wrote a python code for Breadth search algorithm for 8 puzzle probelm. The states of the game are coded as a list of lists (two dimensional list). The goal is to reach [[1,2,3],[8,0,4],[7,6,5]] from a given state. The code output the path from a given state to the goal. It works fine but for this particular case [[0,1,2],[7,8,3],[6,5,4]] does not give the whole path rather it gives the last three states in the path.
Would you please try to point out the place of the bugs. Here is the code. Note The program starts from the statement:
"""
This program solves the 8 puzzel problems using Breadth First Search Algorithm.
"""
import copy
def successor(astate):
"""
This function takes an instance of the 8 puzzel problem and generates all the legal successors.
"""
up = copy.deepcopy(astate)
"""
important note:
I use [:] and list() to copy lists but it was not working fine, hence deecopy is used instead.
"""
down = copy.deepcopy(astate)
left = copy.deepcopy(astate)
right = copy.deepcopy(astate)
successors = []
for i in range(3):
for j in range(3):
if astate[i][j] == 0:
row = i
col = j
if row != 0:
dummy = up[row][col]
up[row][col] = up[row -1][col]
up[row-1][col] = dummy
successors.append(up)
if row != 2:
dummy = down[row][col]
down[row][col] = down[row+1][col]
down[row+1][col] = dummy
successors.append(down)
if col != 2:
dummy = right[row][col]
right[row][col] = right[row][col+1]
right[row][col+1] = dummy
successors.append(right)
if col != 0:
dummy = left[row][col]
left[row][col] = left[row][col-1]
left[row][col-1] = dummy
successors.append(left)
return successors
def puzzle(astate):
"""
This function takes a given instance of the 8 puzzel problem and returns the path to the goal where the goal is defined as below.
"""
goal = [[1,2,3],[8,0,4],[7,6,5]] #The goal state.
generation = [astate] #Nodes generated at each level.
tracking = {} #Track the path to the goal.
path = [] #The path from the root to the goal.
parent = generation.pop(0) #Takes the first element of the list!!
successors = successor(parent) #Generate successors.
key = str(parent) #keys odictionaries must be hashable and mutable. Lists are illegal keys.
tracking[key] = successors #Associate successors with their parent.
for asuccessor in successors:
generation.append(asuccessor) #Generate the first level.
if goal in generation: #if the goal is among the successors returns the path to it.
path.insert(0, key)
path.insert(0, goal)
return path
else:
while generation != []: #keep searching!
parent = generation.pop(0)
successors = successor(parent) #generate successors
key = str(parent)
tracking[key] = successors
if goal in [astate for astate in successors]: #if the goal is among the successors backtrack its path.
path.insert(0, str(goal)) #Just because path contains states as strings!!
path.insert(0, key)
for key in tracking.keys():
for value in tracking.get(key):
if str(parent) == str(value): #If the current (parent) is among the values of (key) then (key) is its parent.
path.insert(0, key)
parent = key
break
return path
else: #keep searching
for asuccessor in successors:
if asuccessor not in generation: #If the current successors is already generated do not add it.
if str(asuccessor) not in tracking.keys(): #If the successor is a previous parent do not add it.
generation.append(asuccessor)
return

Python - Calculating Distance in Dijkstra

I am having some trouble in determining the distance of each node from the start node, or rather getting any information back at all.
I get no output from my function, attached in the following link.
#Values to assign to each node
class Node:
distFromSource = infinity
previous = invalid_node
visited = False
#for each node assign default values
def populateNodeTable(network):
nodeTable = []
index = 0
f = open('network.txt', 'r')
for line in f:
node = map(int, line.split(','))
nodeTable.append(Node())
print "The previous node is " ,nodeTable[index].previous
print "The distance from source is " ,nodeTable[index].distFromSource
index +=1
nodeTable[startNode].distFromSource = 0
return nodeTable
#calculate the distance of each node from the start node
def tentativeDistance(currentNode, nodeTable):
nearestNeighbour = []
for currentNode in nearestNeighbour:
currentDistance == currentNode.distFromSource + [currentNode][nearestNeighbour] #gets current distance from source
print "The current distance"
if currentDistance != 0 & currentNode.distFromSource < Node[currentNode].distFromSource:
nodeTable[currentNode].previous = currentNode
nodeTable[currentNode].length = currentDistance
nodeTable[currentNode].visited = True
nodeTable[currentNode] +=1
nearestNeighbour.append(currentNode)
for currentNode in nearestNeighbour:
print nearestNeighbour
return nearestNeighbour
My logic is, at least in my mind, correct; however, I don't get as much as an error message when the code is run.
You're setting nearestNeighbour to be an empty list, and then you're looping over it with for currentNode in nearestNeighbour -- which does nothing, because the list is empty -- and then you're returning from the function.
(I assume tentativeDistance is the function you're calling and seeing nothing from.)
You should rethink your algorithm design. Try looking up a pseudocode definition of Dijkstra's algorithm and implementing that in Python. In particular, you should think about the control flow in your program.
You might want to have a look at this cookbook recipe for a Python implementation of Dijkstra and see if you can understand it.

Categories

Resources