Linked List, skipping every nth amount user inputs - python-3.x

This code is part of a file where I am manipulating a linked list. This section is part of a function called alternate(head, skip_amt). The user will call the alternate function in a separate file to test my functions. The user will enter a list of nodes and the skip_amt. If the user enters skip_amt = 3, then my function needs to skip over 3 nodes in the linked list and return the 4th node until the end of the list.
My function below is doing this correctly for 0 steps, 1 step, and 3 steps. How can I change my function so that if the user wants me to skip every nth node and return the (n + 1)th node until the end of the list? I also feel like this code is too long and there must be a shorter way to accomplish what I am trying to do.
def alternate(head, skip_amt):
if (skip_amt == 0):
head = head.next
return head
if (skip_amt == 1):
temp = head
if (temp):
# if list has nodes
head = temp.next
temp = head
while (temp and temp.next):
# skips every other item in linked list a
# removes pointer so old_head returns every other item
temp.next = temp.next.next
temp = temp.next
return head
if (skip_amt > 1):
temp = head.next.next
if (temp):
# if list has nodes
head = temp.next
temp = head
while (temp and temp.next):
# skips every other item in linked list a
# removes pointer so old_head returns every other item
temp.next = temp.next.next
temp.next = temp.next.next.next
temp = temp.next
return head

Indeed, there is no reason why you should treat separately the case where skip_amt is 0, or is 1, or is 2... You can do this in one generic way.
Here is some code you could use. I have included utility functions to convert a sequence (i.e. list, range, tuple) to a linked list, and to iterate a linked list.
I would also define alternate as a method. Things become a bit easier if you also define a separate method that walks a given number of steps forward in the list.
class Node:
def __init__(self, val, next=None):
self.val = val
self.next = next
#staticmethod
def fromsequence(seq):
head = None
for val in reversed(seq):
head = Node(val, head)
return head
def __iter__(self): # allow iteration over this linked list
node = self
while node:
yield node.val
node = node.next
def forward(self, steps): # return the node that is `steps` ahead
node = self
for _ in range(steps):
if not node:
return
node = node.next
return node
def alternate(self, skip_amt):
head = self.forward(skip_amt)
node = head
while node:
node.next = node.forward(skip_amt+1)
node = node.next
return head
# demo
head = Node.fromsequence(range(17))
print(list(head)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
head = head.alternate(3)
print(list(head)) # [3, 7, 11, 15]

Needs some tweaking, but it would be along the lines of this
def alternate(list, skip_steps):
print('list head:', list.data)
if (skip_steps == 0):
while True:
print(list.data)
if(list.next == None):
print('end of list')
return
list = list.next
elif (skip_steps == 1):
while True:
print(list.data)
if((list.next== None or list.next.next== None)):
print('end of list')
return
list = list.next.next
else: # Means we have to skip steps > 1
while True:
for i in range(skip_steps):
if(list.next == None):
print('end of list reached before next node')
return
list = list.next
print(list.data)
You should be able to modify it to work for your needs

Related

Index going out of range in bisect_left in Python 3

I'm writing this piece of code, in which I've used bisect_left function from the bisect module which is a first-party module of Python. I'm using it with two parameters only i.e. sorted_list and target(the one for which I have to find the suitable index value).
The issue is: If my target is greater than the sum of lowest value and highest value, the function is returning the index = len(sorted_li), due to which I'm getting index error. I can use try and except but more than that I'm curious to know why it is behaving like so.
Following is my code:
from bisect import bisect_left
li = [10,15,3,6,10]
k = 19
def binary_search(sorted_list,target):
index = bisect_left(sorted_list,target)
print(index)
if sorted_list[index] == target:
return index
else:
return False
def function(sorted_li,k):
"""
Given a list of numbers and a number k, return whether any two numbers from the list add up to k.
For example, given [10, 15, 3, 7] and k of 17, return true since 10 + 7 is 17.
"""
print(sorted_li)
for i in range(len(sorted_li)):
print('Next iteration')
print(sorted_li[i])
target = k - sorted_li[i]
j = binary_search(sorted_li,target)
if j:
if j != i:
print(sorted_li[i])
print(sorted_li[j])
return True
else:
if j + 1 < len(sorted_li):
if sorted_li[j+1] == target:
print(sorted_li[i])
print(sorted_li[j+1])
return True
if j - 1 > 0:
if sorted_li[j-1] == target:
print(sorted_li[i])
print(sorted_li[j-1])
return True
return False
if __name__ == "__main__":
li.sort()
a = function(li,k)
print(a)
It's output is as follows:
but when I'm changing k to 18, the code is working fine, the output is as follows:
I've tried with various sets of numbers for the same. The output remains the same.
You're using bisect_left which has next purpose: it looking for the insertion point for x (which is target in your case) in a to maintain sorted order.
So for your case when you call first binary_search first time for 16 (19 - 3), it compare your number with items in li list using binary algorithm and then it returns position for insert 5, because in your list [3, 6, 10, 10, 15] insertion point should be after 15 which is correct.
If you open documentation you can find next method in searching sorted list
which does exactly you need, it searching for the exact item in list and return position of it if it exists either it raises ValueError because item not found.
def index(a, x):
'Locate the leftmost value exactly equal to x'
i = bisect_left(a, x)
if i != len(a) and a[i] == x:
return i
raise ValueError

How to fix TypeError in Python when comparing values of two nodes of a Linked List

I am trying to compare the value of 2 nodes of 2 separate linked lists (the values will always be int), but it keeps throwing TypeError despite my best efforts to get around it.
Python version 3.7.4 on VS Code for Mac 1.37.0
This is code I wrote to merge two Linked Lists with values as int (which should be pre-sorted before being passed as arguments to the 'merge' method). The output needs to be a single sorted Linked List. I understand TypeError gets thrown if 2 values are being operated on while they are of incompatible type (e.g. str + int or int < object etc.). To get around it, I tried forced type-casting, but that then creates AttributionError, since object at one point is 'None' and it doesn't get typecast to int.
######## Definition of 'Node', 'LinkedList' #######
class Node:
def __init__(self, value):
self.value = value
self.next = None
def __repr__(self):
return str(self.value)
class LinkedList:
def __init__(self, head):
self.head = head
def append(self, value):
if self.head is None:
self.head = Node(value)
return
node = self.head
while node.next is not None:
node = node.next
node.next = Node(value)
#### Where I need help #####
def merge(list1, list2):
"""
Merge and sort two linked lists
Args:
list1, list2: two linked lists that need to be merged. They need to be pre-sorted before being passed as a argument.
Returns:
linked-list: Merged and sorted linked-list, a combination of list1 and list2
"""
merged = LinkedList(None) # create an empty linked list
if list1 is None:
return list2
if list2 is None:
return list1
list1_elt = list1.head # start with heads of the two lists
list2_elt = list2.head
while list1_elt is not None or list2_elt is not None:
# val1 = int(str(list1_elt.value))
# val2 = int(str(list2_elt.value))
# condition = val1 < val2
# print("List1 value: {} and List2 value: {}".format(list1_elt.value, list2_elt.value))
if list1_elt is None:
# print("List2 value: {}".format(list2_elt.value))
merged.append(list2_elt)
list2_elt = list2_elt.next
elif list2_elt is None:
# print("List1 value: {}".format(list1_elt.value))
merged.append(list1_elt)
list1_elt = list1_elt.next
elif list1_elt.value <= list2_elt.value:
# elif val1 <= val2:
# elif condition:
# print("List1 value: {}".format(list1_elt.value))
merged.append(list1_elt)
list1_elt = list1_elt.next
else:
# print("List2 value: {}".format(list2_elt.value))
merged.append(list2_elt)
list2_elt = list2_elt.next
return merged
##### TEST CODE TO CHECK #######
# First Test scenario
linked_list = LinkedList(Node(1))
linked_list.append(Node(3))
linked_list.append(Node(5))
second_linked_list = LinkedList(Node(2))
second_linked_list.append(4)
merged = merge(linked_list, second_linked_list)
node = merged.head
while node is not None:
#This should print 1 2 3 4 5
print(node.value)
node = node.next
In the merge method definition, I keep getting an error at the part:
elif list1_elt.value <= list2_elt.value:
I've tried to store the values in two separate variables with forced typecasting, but I get other errors. I am at my wit's end to understand what I am doing wrong here. This is the error I get:
File "filename.py", line 58, in merge
elif list1_elt.value <= list2_elt.value:
TypeError: '<=' not supported between instances of 'Node' and 'int'
You are setting value with a Node rather than int, at
self.head = Node(value)
node.next = Node(value)
Either you can send int to append function as :
linked_list.append(5)
or directly assign the Node
self.head = value
node.next = value
I would suggest the first method as you should send the value which you want to insert into the list and your append function is also expecting a value.

Print permutation tree python3

I have list of numbers and I need to create tree and print all permutations, e.g. for [1, 2, 3] it should print
123
132
213
231
312
321
Currently my code prints first element only once:
123
32
213
31
312
21
How do I fix it?
class Node(object):
def __init__(self):
self.children = None
self.data = None
self.depth = None
class PermutationTree(object):
def __init__(self, numbers):
numbers = list(set(numbers))
numbers.sort()
self.depth = len(numbers)
self.tree = self.generate_root(numbers)
self.create_tree(numbers, self.tree.children, 1)
def generate_root(self, numbers):
root = Node()
root.children = []
root.depth = 0
for i in numbers:
tree = Node()
tree.data = i
tree.depth = 1
root.children.append(tree)
return root
def create_tree(self, numbers, children, depth):
depth += 1
if depth == self.depth + 1:
return
for child in children:
reduced_numbers = list(numbers)
reduced_numbers.remove(child.data)
child.children = []
for j in reduced_numbers:
tree = Node()
tree.data = j
tree.depth = depth
child.children.append(tree)
self.create_tree(reduced_numbers, child.children, depth)
def print_tree(self):
self.print(self.tree)
def print(self, node):
for i in node.children:
print(i.data, end="")
if not i.depth == self.depth:
self.print(i)
print("")
test = [1, 2, 3]
permutation_tree = PermutationTree(test)
permutation_tree.print_tree()
This is pretty good example showing why side effects should be done in as few places as possible.
Side effects everywhere
Your approach is like this: "I will print one digit every time it is needed." This approach gives you limited control over side effects. Your code prints many times in different recursion depths and it's hard to keep track of what is going on.
Side effects in as few places as possible
Another approach to this problem is creating side effects in one place. "I will generate all permutations first and then print them." Such a code is easier to debug and can give you better control of what is going on in your code.
If you write your code like this:
def print_tree(self): # this function takes care about printing
for path in self.generate_path(self.tree):
print(path)
def generate_perm(self, node): #this function takes care about generating permutations
if not node.children:
return [str(node.data)]
app_str = str(node.data) if node.data else ""
res= []
for child in node.children:
for semi_path in self.generate_perm(child):
res.append (app_str + semi_path)
return res
P.S.
generate_perm can be rewritten to be more efficient with yields.

Inserting int into sorted multidimensional list

I need to insert an int into a sorted list.
Here's what I got so far:
def insert_in_list(x, tree, index=0):
if not tree:
return tree
elif x < tree[index]:
tree.insert(index, x)
return tree
else:
return insert_in_list(x, tree, index+1)
This is working on all one-dimensional lists for example, [1,5,10]. However, I need it to work on any list containing additional lists, for example: [1,[5,7[8,9,10]]18,22].
Tried this:
def insert_in_list(x, tree, index=0):
if not tree:
return x
elif isinstance(tree[0], list):
return [insert_in_list(x, tree[0])] + insert_in_list(x, tree[1:])
elif x < tree[index]:
tree.insert(index, x)
return tree
else:
return insert_in_list(x, tree, index+1)
However, insert_in_list(4, [[2,3,6], 8, 10])) returns [[[[2, 3, 4, 6]]], 4, 8, 10]. So the recursion doesn't stop after inserting the first 4. help me out
It happen because you need to tell your function to stop looking for x < item when the upper bound is found.
This is an additional signal (variable done) that propagates as soon as an upper bound is found.
def insert_in_list(x, tree, done = False):
for index, item in enumerate(tree):
if isinstance(item, list):
done = insert_in_list(x, item)
elif done:
return True
elif x < item:
tree.insert(index, x)
return True
Additionnaly
you don't need the index as parameter since you'll insert inside the loop, other instances of your function don't need to know that last index.
list are mutable, so it is allways the same list even if you don't pass it as parameter.

Tree traversals python

I have to define three functions: preorder(t):, postorder(t):, and inorder(t):.
Each function will take a binary tree as input and return a list. The list should then be ordered in same way the tree elements would be visited in the respective traversal (post-order, pre-order, or in-order)
I have written a code for each of them, but I keep getting an error when I call another function (flat_list()), I get an index error thrown by
if not x or len(x) < 1 or n > len(x) or x[n] == None:
IndexError: list index out of range
The code for my traversal methods is as follows:
def postorder(t):
pass
if t != None:
postorder(t.get_left())
postorder(t.get_right())
print(t.get_data())
def pre_order(t):
if t != None:
print(t.get_data())
pre_order(t.get_left())
pre_order(t.get_right())
def in_order(t):
pass
if t != None:
in_order(t.get_left())
print(t.get_data())
in_order(t.get_right())
def flat_list2(x,n):
if not x or len(x) < 1 or n > len(x) or x[n] == None:
return None
bt = BinaryTree( x[n] )
bt.set_left( flat_list2(x, 2*n))
bt.set_right(flat_list2(x, 2*n + 1))
return bt
this is how i call flat_list2
flat_node_list = [None, 55, 24, 72, 8, 51, None, 78, None, None, 25]
bst = create_tree_from_flat_list2(flat_node_list,1)
pre_order_nodes = pre_order(bst)
in_order_nodes = in_order(bst)
post_order_nodes = post_order(bst)
print( pre_order_nodes)
print( in_order_nodes)
print( post_order_nodes)
You should actually write three function that return iterators. Let the caller decide whether a list is needed. This is most easily done with generator functions. In 3.4+, 'yield from` can by used instead of a for loop.
def in_order(t):
if t != None:
yield from in_order(t.get_left())
yield t.get_data()
yield from in_order(t.get_right())
Move the yield statement for the other two versions.
First things first, I noticed that your indentation was inconsistent in the code block that you provided (fixed in revision). It is critical that you ensure that your indentation is consistent in Python or stuff will go south really quickly. Also, in the code below, I am assuming that you wanted your t.get_data() to still fall under if t != None in your postorder(t), so I have indented as such below. And lastly, I noticed that your method names did not match the spec you listed in the question, so I have updated the method names below to be compliant with your spec (no _ in the naming).
For getting the list, all you need to do is have your traversal methods return a list, and then extend your list at each level of the traversal with the other traversed values. This is done in lieu of printing the data.
def postorder(t):
lst = []
if t != None:
lst.extend(postorder(t.get_left()))
lst.extend(postorder(t.get_right()))
lst.append(t.get_data())
return lst
def preorder(t):
lst = []
if t != None:
lst.append(t.get_data())
lst.extend(preorder(t.get_left()))
lst.extend(preorder(t.get_right()))
return lst
def inorder(t):
lst = []
if t != None:
lst.extend(inorder(t.get_left()))
lst.append(t.get_data())
lst.extend(inorder(t.get_right()))
return lst
This will traverse to the full depths both left and right on each node and, depending on if it's preorder, postorder, or inorder, will append all the traversed elements in the order that they were traversed. Once this has occurred, it will return the properly ordered list to the next level up to get appended to its list. This will recurse until you get back to the root level.
Now, the IndexError coming from your flat_list, is probably being caused by trying to access x[n] when n could be equal to len(x). Remember that lists/arrays in Python are indexed from 0, meaning that the last element of the list would be x[n-1], not x[n].
So, to fix that, replace x[n] with x[n-1]. Like so:
def flat_list2(x,n):
if not x or len(x) < 1 or n < 1 or n > len(x) or x[n-1] == None:
return None
bt = BinaryTree( x[n-1] )
bt.set_left( flat_list2(x, 2*n))
bt.set_right(flat_list2(x, 2*n + 1))
return bt

Resources