Inserting int into sorted multidimensional list - python-3.x

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.

Related

List comprehention works but normal for loop doesn't - Python

I defined a function to change an element of a list to be number 0, if this element is not a number. It works using list comprehension but it doesn't work when I use a normal for loop.
I'm trying to understand what's is the error in the for loop.
See the code below:
def zerozero(mylist):
mylist = [0 if type(x) == str else x for x in mylist]
return mylist
def zerozero2(mylist):
for x in mylist:
if type(x) == str:
x = 0
else:
x = x
return mylist
Your second function is not quite equivalent. You would need something like this:
def zerozero2(mylist):
new_list = []
for x in mylist:
if type(x) == str:
new_list.append(0)
else:
new_list.append(x)
return new_list
In this manner you can mimic the functionality of the list comprehension, creating a new list and appending items to it as you iterate through.
If you want to modify your list 'in place', you can use this sort of construction:
for idx, x in enumearte(mylist):
if type(x) == str:
mylist[idx] = 0
else:
mylist[idx] = x
However, practically speaking this is unlikely to have much impact on your code efficiency. You can't do this with a list comprehension, and in either case you can just re-assign the new list back to your original variable when you return from the function:
mylist = zerozeroX(mylist)
So what happens is your function is returning the same list as your input.
What you should do is create an empty list first. For example my_list_0 = [].
def zerozero2(mylist):
my_list_0 = []
for x in mylist:
if type(x) == str:
x=0
else:
x=x
my_list_0.append(x)
return my_list_0
The list comprehension essentially returns the new values into your original list, so this is why it is different.

Linked List, skipping every nth amount user inputs

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

How to walk a tree until a node is found, summing up attributes along the way

I'm building a tree with arbitrary structure. Leaves of the tree have a particular attribute that no other nodes have. I'm trying to come up with a way to traverse the tree to find a particular leaf and keep a running sum of the attributes of the leaves I've visited. I need the cumulative sum of the leaves I've visited that are not the leaf I'm searching for, as well as the attribute of the leaf I am trying to find. I need it to search left-to-right, top-to-bottom.
An example tree might look something like this:
My Node class is:
class Node:
def __init__(self, name, children=[], **kwargs):
self.__dict__.update(kwargs)
self.name = name
self.children = children
def insert(self, child):
self.children.append(child)
If I built the example tree as
l1 = Node("leaf1", attr=1)
l2 = Node("leaf2", attr=2)
n2 = Node("node2", children=[l1, l2])
l3 = Node("leaf3", attr=3)
l4 = Node("leaf4", attr=4)
n1 = Node("node1", children=[n2, l3, l4])
l6 = Node("leaf6", attr=6)
n4 = Node("node4", children=[l6])
l5 = Node("leaf5", attr=5)
l7 = Node("leaf7", attr=7)
n3 = Node("node3", children=[l5, n4, l7])
root = Node("root", children=[n1, n3])
and I wanted to search_node(root, "leaf4"), I'd expect a result of (6, 4) - the first element of the tuple is the sum of all leaves visited that were not leaf 4, and the second element is the attribute attached to leaf 4. search_node(root, "leaf1") would result in (0, 1), as there were no leaves visited that were not leaf 1.
Here's the code that I have so far:
def search_node(node, search_name):
cum_sum = 0
if node.children:
for child in node.children:
if child.name == search_name:
return (cum_sum, child.attr)
subtree_attr_sum, node_attr = search_node(child, search_name)
if node_attr is not None:
return (subtree_attr_sum, node_attr)
else:
cum_sum += subtree_attr_sum
return (cum_sum, None)
else:
return (node.attr, None)
It seems to work on the left half of the tree, but not on the right half. Admittedly, this code is ugly and could probably be reduced.
Okay. A one-line modification in your code:
def search_node(node, search_name):
cum_sum = 0
if node.children:
for child in node.children:
if child.name == search_name:
return (cum_sum, child.attr)
subtree_attr_sum, node_attr = search_node(child, search_name)
if node_attr is not None:
return (cum_sum + subtree_attr_sum, node_attr) # THIS LINE
else:
cum_sum += subtree_attr_sum
return (cum_sum, None)
else:
return (node.attr, None)
You made an oversight. You're not adding the cumulative sum to the result you got via recursion.
Also, a possible bug in your logic: Why are you not checking the root as well?

Python: Unexpected result in a recursive function

I've written a recursive function to gauge, e.g. list depth and for some reason it returns unexpected results.
I have two functions:
1. checks if the object is iterable
2. gauges the depth of the object, e.g. list
I think I'm missing something in the second function but I couldn't wrap my head around why exactly variable n when returned from else turns into a funny result.
I set print to see how n gets changed in every stage and it seemed working as expected but when returned from else it turns into a wrong number.
Here are two functions' code:
def isiterable(obj):
'''
takes in an obj and returns 1 if iterable or 0 if not
strings are discarded as iterable
:param obj: any object
:return: int
'''
if isinstance(obj, str):
return 0
else:
try:
iter(obj)
return 1
except TypeError as err:
return 0
my second function is recursive where I'm experiencing problems
def get_depth(a, n=0):
if isiterable(a):
return n + f(a[0], n+1)
else:
return n
I've three examples:
a = [[[1,2], [3,4]], [[5,6],[7,8]]]
b = [[1,2], [2,3]]
c = [2]
I'm expecting get_depth to return 3 for list a, 2 for list b and 1 for list c.
for some reason results for a get doubled and return 6. In b case it is 3 instead of 2.
Many thanks
You don't need to add n when you return from get_depth.
def get_depth(a, n=0):
if isiterable(a):
return get_depth(a[0], n+1)
else:
return n
Because, when a have more depth, you will calculate the get_depth function again witn n+1 which is already count the depth correctly and the extras are not needed.
Btw, you have to think about what if this case?
d = [1, 2, [3, 4]]
I can modify a bit such as:
def get_depth(a, n=0):
if isiterable(a):
temp = []
for i in range(0, len(a)):
temp.append(get_depth(a[i], n+1))
return max(temp)
else:
return n

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