Using model Binary Tree code snippet, how to implement this? - binary-tree

I have a code for Binary Tree (Not BST) from MIT 6.006 (2020, MIT OCW). By the way, I have no idea of how to add elements into the Tree using below two Classes (symbol A or T seems to be used instead of a self). Specifically,
how to insert elements into Binary_Tree
how to incorporate the build(X) function into the Binary_Tree
Could anybody give an example of how I can utilize this code?
# From Recitation 6, MIT 6.006, Spring 2020
class Binary_Node:
def __init__(A, x): # O(1)
A.item = x
A.left = None
A.right = None
A.parent = None
def subtree_iter(A): # O(n), in-order traversal
if A.left: yield from A.left.subtree_iter()
yield A
if A.right: yield from A.right.subtree_iter()
def subtree_first(A): # O(h)
if A.left: return A.left.subtree_first()
else: return A
def subtree_last(A): # O(h)
if A.right: return A.right.subtree_last()
else: return A
def successor(A): # O(h)
if A.right: return A.right.subtree_first()
while A.parent and (A is A.parent.right):
A = A.parent
return A.parent
def predecessor(A): # O(h)
if A.left: return A.left.subtree_last()
while A.parent and (A is A.parent.left):
A = A.parent
return A.parent
def subtree_insert_before(A, B): # O(h)
if A.left:
A = A.left.subtree_last()
A.right, B.parent = B, A
else:
A.left, B.parent = B, A
def subtree_insert_after(A, B): # O(h)
if A.right:
A = A.right.subtree_first()
A.left, B.parent = B, A
else:
A.right, B.parent = B, A
def subtree_delete(A): # O(h)
if A.left or A.right:
if A.left: B = A.predecessor()
else: B = A.successor()
A.item, B.item = B.item, A.item
return B.subtree_delete()
if A.parent:
if A.parent.left is A: A.parent.left = None
else: A.parent.right = None
return A
class Binary_Tree:
def __init__(T, Node_Type = Binary_Node):
T.root = None
T.size = 0
T.Node_Type = Node_Type
def __len__(T): return T.size
def __iter__(T):
if T.root:
for A in T.root.subtree_iter():
yield A.item
def build(X):
A = [x for x in X]
def build_subtree(A, i, j):
c = (i + j) // 2
root = self.Node_Type(A[c])
if i < c:
root.left = build_subtree(A, i, c - 1)
root.left.parent = root
if c < j:
root.right = build_subtree(A, c + 1, j)
root.right.parent = root
return root
self.root = build_subtree(A, 0, len(A) - 1)

Related

Why AVL sort is not in place?

I was recently told that AVL sort is not in place. Can anyone please explain it? From the below code, I am not sure where I assign extra space when sorting. In this code, when a data structure is built or an element are inserted, elements are ordered by their key.
Reference for the claim: They are using this claim to motivate "binary heap"
[1].https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r08.pdf
Reference for code:
[2]. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r06.pdf
[3]. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r07.pdf
def height(A):
if A: return A.height
else: return -1
class Binary_Node:
def __init__(self, x):
self.item = x
self.parent = None
self.left = None
self.right = None
self.subtree_update()
def subtree_update(self):
self.height = 1 + max(height(self.left), height(self.right))
def subtree_iter(self):
if self.left: yield from self.left.subtree_iter()
yield self
if self.right: yield from self.right.subtree_iter()
def subtree_first(self):
if self.left: return self.left.subtree_first()
else: return self
def subtree_last(self):
if self.right: return self.right.subtree_last()
else: return self
def sucessor(self):
if self.right: return self.right.subtree_first()
while self.parent and (self is self.parent.right): #A is parent's left child and A's parent exists
self = self.parent
return self.parent
def predecessor(self):
if self.left: return self.left.subtree_last()
while self.parent and (self is self.parent.left):
self = self.parent
return self.parent
def subtree_insert_before(self, A):
if self.left:
self = self.left.subtree_last()
self.right, A.parent = A, self
else:
self.left, A.parent = A, self
self.maintain()
def subtree_insert_after(self, A):
if self.right:
self = self.right.subtree_first()
self.left, A.parent = A, self
else:
self.right, A.parent = A, self
self.maintain()
def delete(self):
if not self.left and not self.right: # when self is leaf
if self.parent:
A = self.parent
if A.left is self: A.left = None
else: A.right = None
self.parent = None
if self.left:
self.item, self.left.subtree_last().item = self.left.subtree_last().item, self.item
self.left.subtree_last().delete()
else:
self.item, self.right.subtree_first().item = self.right.subtree_first().item, self.item
self.right.subtree_last().delete()
def subtree_delete(self):
if self.left or self.right:
if self.left: B = self.predecessor()
else: B = self.sucessor()
self.item, B.item = B.item, self.item
return B.subtree_delete()
if self.parent:
if self.parent.left is self: self.parent.left = None
else: self.parent.right = None
self.parent.maintain()
return self
def subtree_rotate_right(self):
assert self.left
B, E = self.left, self.right
A, C = B.left, B.right
B, self = self, B
self.item, B.item = B.item, self.item
B.left, B.right = A, self
self.left, self.right = C, E
if A: A.parent = B
if E: E.parent = self
B.subtree_update()
self.subtree_update()
def subtree_rotate_left(self):
assert self.right
A, D = self.left, self.right
C, E = D.left, D.right
self, D = D, self
self.item, D.item = D.item, self.item
self.left, self.right = A, C
D.left, D.right = self, E
if A: A.parent = self
if E: E.parent = D
self.subtree_update()
D.subtree_update()
def skew(self):
return height(self.right) - height(self.left)
def rebalance(self):
if self.skew() == 2:
if self.right.skew() < 0:
self.right.subtree_rotate_right()
self.subtree_rotate_left()
elif self.skew() == -2:
if self.left.skew() > 0:
self.left.subtree_rotate_left()
self.subtree_rotate_right()
def maintain(self):
self.rebalance()
self.subtree_update()
if self.parent: self.parent.maintain()
class Binary_Tree:
def __init__(self, Node_Type = Binary_Node):
self.root = None
self.size = 0
self.Node_Type = Node_Type
def __len__(self): return self.size
def __iter__(self):
if self.root:
for A in self.root.subtree_iter():
yield A.item
def build(self, X):
A = [x for x in X]
def build_subtree(A, i, j):
c = (i + j) // 2
root = self.Node_Type(A[c])
if i < c:
root.left = build_subtree(A, i, c - 1)
root.left.parent = root
if j > c:
root.right = build_subtree(A, c + 1, j)
root.right.parent = root
return root
self.root = build_subtree(A, 0, len(A) - 1)
class BST_Node(Binary_Node):
def subtree_find(self, k):
if self.item.key > k:
if self.left: self.left.subtree_find(k)
elif self.item.key < k:
if self.right: self.right.subtree_find(k)
else: return self
return None
def subtree_find_next(self, k):
if self.item.key <= k:
if self.right: return self.right.subtree_find_next(k)
else: return None
elif self.item.key > k:
if self.left: return self.left.subtree_find_next(k)
else: return self
return self
def subtree_find_prev(self, k):
if self.item.key >= k:
if self.left: return self.left.subtree_find_prev(k)
else: return None
elif self.item.key < k:
if self.right: return self.right.subtree_find_prev(k)
else: return self
return self
def subtree_insert(self, B):
if B.item.key < self.item.key:
if self.left: self.left.subtree_insert(B)
else: self.subtree_insert_before(B)
elif B.item.key > self.item.key:
if self.right: self.right.subtree_insert(B)
else: self.subtree_insert_after(B)
else:
self.item = B.item
class Set_Binary_Tree(Binary_Tree):
def __init__(self): super().__init__(BST_Node)
def iter_order(self): yield from self
def build(self, X):
for x in X: self.insert(x)
def find_min(self):
if self.root: return self.root.subtree_first()
def find_max(self):
if self.root: return self.root.subtree_last()
def find(self, k):
if self.root:
node = self.root.subtree_find(k)
if node:
return node.item
def find_next(self, k):
if self.root:
node = self.root.subtree_find_next(k)
if node:
return node.item
def find_prev(self, k):
if self.root:
node = self.root.subtree_find_prev(k)
if node:
return node.item
def insert(self, x):
new = self.Node_Type(x)
if self.root:
self.root.subtree_insert(new)
if new.parent is None: return False
else:
self.root = new
self.size += 1
return True
def delete(self, k):
assert self.root
node = self.root.subtree_find(k)
assert node
ext = node.subtree_delete()
if ext.parent is None: self.root = None
self.size -= 1
return ext.item
Wikipedia defines an in-place algorithm as follows:
In computer science, an in-place algorithm is an algorithm which transforms input using no auxiliary data structure. However, a small amount of extra storage space is allowed for auxiliary variables. The input is usually overwritten by the output as the algorithm executes. An in-place algorithm updates its input sequence only through replacement or swapping of elements.
So one of the properties of an algorithm that is called "in-place" is that it does not copy all input values into an newly allocated data structure. If an algorithm creates a binary search tree (like AVL), for which node objects are created that are populated with the input values, then it cannot be called in-place by the above definition, even if at the end of the process the values are copied back into the input array.
As a comparison, heap sort does not have to create a new data structure, as the input array can be used to reorganise its values into a heap. It merely has to swap values in that array in order to sort it. It is therefore an in-place algorithm.

Does an algorithm for extracting sub expressions from an expression exist?

Suppose I had the expression:
v = ((((x * 3) * y) * 5) * z) + 5
And I want to reduce the amount of operations as much as possible in v by moving sub expressions into different variables, yet maintaining that y must only occur in v, such as:
a = x * 3
b = 5 * z
v = ((a * y) * b) + 5
Is there an algorithm that can achieve this?
If the programs are limited to straight-line programs with plus, minus, times, then you could determine the value of v as a polynomial in y and evaluate it using Horner's method.
Here's some rough Python by way of illustration.
import operator
class Formula:
def __init__(self, s=0):
self.s = str(s)
def __repr__(self):
return "Formula({!r})".format(self.s)
def __str__(self):
return self.s
def __add__(self, other):
if isinstance(other, int):
other = Formula(str(other))
if not isinstance(other, Formula):
return NotImplemented
if other.s == "0":
return self
return Formula("({}) + ({})".format(self, other))
def __radd__(self, other):
return self + other
def __mul__(self, other):
if isinstance(other, int):
other = Formula(str(other))
if not isinstance(other, Formula):
return NotImplemented
if other.s == "0":
return 0
if other.s == "1":
return self
return Formula("({}) * ({})".format(self, other))
def __rmul__(self, other):
return self * other
class Polynomial:
def __init__(self, *coefs):
self.coefs = coefs
def print_program(self):
v = 0
for i, coef in enumerate(reversed(self.coefs)):
c = Formula("c{}".format(i))
print("{} = {}".format(c, coef))
v *= Formula("y")
v += c
print("v = {}".format(v))
def __add__(self, other):
if isinstance(other, int):
other = Formula(other)
if isinstance(other, Formula):
other = Polynomial(other)
if not isinstance(other, Polynomial):
return NotImplemented
coefs = list(map(operator.add, self.coefs, other.coefs))
if len(self.coefs) > len(other.coefs):
coefs.extend(self.coefs[len(other.coefs) :])
if len(other.coefs) > len(self.coefs):
coefs.extend(other.coefs[len(self.coefs) :])
return Polynomial(*coefs)
def __radd__(self, other):
return self + other
def __mul__(self, other):
if isinstance(other, int):
other = Formula(other)
if isinstance(other, Formula):
other = Polynomial(other)
if not isinstance(other, Polynomial):
return NotImplemented
coefs = [0] * (len(self.coefs) + len(other.coefs) - 1)
for i, ci in enumerate(self.coefs):
for j, cj in enumerate(other.coefs):
coefs[i + j] += ci * cj
return Polynomial(*coefs)
def __rmul__(self, other):
return self * other
x = Formula("x")
y = Polynomial(0, 1)
z = Formula("z")
v = ((((x * 3) * y) * 5) * z) + 5
v.print_program()
The output is
c0 = (((x) * (3)) * (5)) * (z)
c1 = 5
v = ((c0) * (y)) + (c1)

Word break (leetcode #139) Python solution timeout -- is memoization not working?

I'm solving leetcode #139 and for some reason, I'm getting time limit exceeded. Am I using memoization improperly?
class Solution:
memo = set()
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
self.memo = set(wordDict)
return self.word_break(s)
def word_break(self, s):
if s in self.memo:
return True
for i in range(1, len(s)):
head = s[:i]
tail = s[i:]
head_possible = self.word_break(head)
tail_possible = self.word_break(tail)
if head_possible:
self.memo.add(head)
if tail_possible:
self.memo.add(tail)
if head_possible and tail_possible:
return True
return False
Thank you!
Your solution works fine without TLE, if we would just enable lru_cache():
class Solution:
memo = set()
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
self.memo = set(wordDict)
return self.word_break(s)
#lru_cache(None)
def word_break(self, s):
if s in self.memo:
return True
for i in range(1, len(s)):
head = s[:i]
tail = s[i:]
head_possible = self.word_break(head)
tail_possible = self.word_break(tail)
if head_possible:
self.memo.add(head)
if tail_possible:
self.memo.add(tail)
if head_possible and tail_possible:
return True
return False
This'd also pass without Time Limit Exceeded:
class Solution:
def wordBreak(self, s, words):
dp = [False] * len(s)
for i in range(len(s)):
for word in words:
k = i - len(word)
if word == s[k + 1:i + 1] and (dp[k] or k == -1):
dp[i] = True
return dp[-1]

Sorted Linked List in Python

I'm having a bit of trouble figuring out how to sort a Singly Linked list in Python. I've figured out how to create a linked list and push data onto it but how do I push it in a sorted format (not sorting after all the data is pushed onto it) or just sorting it in any way?
Objective
Create a sorted singly linked list of numbers based upon user input.
Program logic:
Ask for a number, add that number to the list in sorted position, print the list.
Repeat until they enter -1 for the number.
Current Code
#!/usr/bin/env python
class node:
def __init__(self):
self.data = None # contains the data
self.next = None # contains the reference to the next node
class linked_list:
def __init__(self):
self.cur_node = None
def add_node(self, data):
new_node = node() # create a new node
new_node.data = data
new_node.next = self.cur_node # link the new node to the 'previous' node.
self.cur_node = new_node # set the current node to the new one.
def list_print(self):
node = self.cur_node # cant point to ll!
while node:
print(node.data)
node = node.next
def main():
ll = linked_list()
num=int(input("Enter a num to push onto the list, -1 to stop: "))
while num!=-1:
data=num
ll.add_node(data)
num=int(input("Enter a num to push onto the list, -1 to stop: "))
print("\n")
ll.list_print()
main()
I'm really stuck here. Thank you in advance for any help!
This should do it:
class Node:
def __init__(self):
self.data = None
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def addNode(self, data):
curr = self.head
if curr is None:
n = Node()
n.data = data
self.head = n
return
if curr.data > data:
n = Node()
n.data = data
n.next = curr
self.head = n
return
while curr.next is not None:
if curr.next.data > data:
break
curr = curr.next
n = Node()
n.data = data
n.next = curr.next
curr.next = n
return
def __str__(self):
data = []
curr = self.head
while curr is not None:
data.append(curr.data)
curr = curr.next
return "[%s]" %(', '.join(str(i) for i in data))
def __repr__(self):
return self.__str__()
def main():
ll = LinkedList()
num = int(input("Enter a number: "))
while num != -1:
ll.addNode(num)
num = int(input("Enter a number: "))
c = ll.head
while c is not None:
print(c.data)
c = c.next
Gives
>>> main()
Enter a number: 5
Enter a number: 3
Enter a number: 2
Enter a number: 4
Enter a number: 1
Enter a number: -1
1
2
3
4
5
class Node:
def __init__(self, data):
self.data = int(data)
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def asc_ordered_list(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
return
temp = self.head
if temp.data > data:
new_node.next = temp
self.head = new_node
return
while temp.next:
if temp.next.data > data:
break
temp = temp.next
new_node.next = temp.next
temp.next = new_node
def desc_ordered_list(self, data):
new_node = Node(data)
if self.head is None:
self.head = new_node
return
temp = self.head
if data > temp.data:
new_node.next = temp
self.head = new_node
return
while temp.next:
if temp.data > data and temp.next.data < data:
break
temp = temp.next
new_node.next = temp.next
temp.next = new_node
def display_list(self):
temp = self.head
while temp is not None:
print("data = {0}".format(temp.data))
temp = temp.next
if __name__ == "__main__":
llist = LinkedList()
llist.desc_ordered_list(8)
llist.desc_ordered_list(3)
llist.desc_ordered_list(1)
llist.desc_ordered_list(4)
llist.desc_ordered_list(5)
llist.desc_ordered_list(7)
llist.desc_ordered_list(6)
llist.desc_ordered_list(2)
llist.display_list()
4 years later, but I think that is easier in this way
def append(self, value):
new_node = Node(value)
if self.head is None:
self.head = new_node
else:
current = self.head
while current is not None and current.value <= value:
previous = current
current = current.next
if current is self.head:
aux = self.head
self.head = new_node
new_node.next = aux
else:
previous.next = new_node
new_node.next = current
self.size += 1
class Solution:
def sortList(self,node):
if(node is None):
return
temp=node
while(temp!=None):
i=temp.next
while(i!=None):
if(temp.data>i.data):
n=i.data
i.data=temp.data
temp.data=n
i=i.next
temp=temp.next
I thought this was a bit shorter and easier
class Node:
def __init__(self, data, _next=None):
self.data = data
self.next = _next
def main():
nodes = []
num = int(input("Enter number: "))
while num != -1:
nodes.append(Node(num))
num = int(input("Enter number: "))
# If list is empty then just end function
if len(nodes) == 0:
return
# Let python do the sorting
nodes = sorted(nodes, key=lambda node: node.data)
# Link the nodes together and print them while you're at it
for i in range(len(nodes) - 1):
nodes[i].next = nodes[i + 1]
print(nodes[i].data)
# We need to print the last node
print(nodes[-1].data)

A sequence that forms the same AVL and splay trees?

Is there such a sequence of numbers (1-7, all numbers used, only once each), that would form equal AVL and splay tree?
Well, in the interests of science, I implemented both AVL and splay trees in Python based on their respective Wikipedia articles. Assuming I didn't make a mistake somewhere, my finding is that there are no permutations of {1, ..., 7} that produce the same AVL and splay tree. I conjecture the same is true for all sets of size k > 3. As to the fundamental reasons for this, I have no idea.
If someone would like to vet my code, here it is:
#####################
# Class definitions #
#####################
class Node:
''' A binary tree node '''
def __init__(self, n, p=None, l=None, r=None):
self.n = n
self.p = p
self.l = l
self.r = r
self.h = None
def __str__(self):
return "[%s %s %s]" % (self.n, (self.l if self.l else "-"), (self.r if self.r else "-"))
def __eq__(self, other):
if not isinstance(other, Node):
return NotImplemented
return self.n == other.n and self.l == other.l and self.r == other.r
def __ne__(self, other):
return not (self == other)
class HNode(Node):
''' A binary tree node, with height '''
def __init__(self, n, p=None, l=None, r=None):
super(HNode, self).__init__(n, p, l, r)
self.hup()
def lh(self):
''' Get height of left child '''
return self.l.h if self.l else 0
def rh(self):
''' Get height of right child '''
return self.r.h if self.r else 0
def hup(self):
''' Update node height '''
self.h = max(self.lh(), self.rh())+1
def __str__(self):
return "[%s (%d) %s %s]" % (self.n, self.h, (self.l if self.l else "-"), (self.r if self.r else "-"))
#########################
# Basic tree operations #
#########################
# v u
# / \ / \
# u c --> a v
# / \ / \
# a b b c
def rright(v):
''' Rotate right '''
u = v.l
u.r, v.l = v, u.r
if v.l:
v.l.p = v
u.p, v.p = v.p, u
return u
# u v
# / \ / \
# a v --> u c
# / \ / \
# b c a b
def rleft(u):
''' Rotate left '''
v = u.r
u.r, v.l = v.l, u
if u.r:
u.r.p = u
u.p, v.p = v, u.p
return v
######################
# AVL tree functions #
######################
def avl_lr(v):
v.l = rleft(v.l)
v.l.l.hup()
v.l.hup()
return avl_ll(v)
def avl_ll(v):
u = rright(v)
u.r.hup()
u.hup()
return u
def avl_rl(v):
v.r = rright(v.r)
v.r.r.hup()
v.r.hup()
return avl_rr(v)
def avl_rr(v):
u = rleft(v)
u.l.hup()
u.hup()
return u
def avl_insert(v, n, p=None):
if v is None:
return HNode(n, p)
if n < v.n:
v.l = avl_insert(v.l, n, v)
v.hup()
if v.lh() > v.rh() + 1:
return (avl_ll if (v.l.lh() > v.l.rh()) else avl_lr)(v)
else:
return v
else:
v.r = avl_insert(v.r, n, v)
v.hup()
if v.rh() > v.lh() + 1:
return (avl_rr if (v.r.rh() > v.r.lh()) else avl_rl)(v)
else:
return v
def build_avl_tree(s):
''' Build an AVL tree from the given sequence '''
v = None
for n in s:
v = avl_insert(v, n)
return v
########################
# Splay tree functions #
########################
two = lambda x: (x,x)
def bst_insert(p, n, g=None):
''' Insert a value into a BST, returning a pair consisting of
the root of the tree and the new node '''
if p is None:
return two(Node(n,g))
if n < p.n:
p.l, x = bst_insert(p.l, n, p)
else:
p.r, x = bst_insert(p.r, n, p)
return p, x
def splay(x):
''' Percolate x to the root of its tree '''
if x.p:
p = x.p
g = p.p
if g:
if p.n < g.n:
if x.n < p.n:
x = rright(rright(g))
else:
g.l = rleft(p)
x = rright(g)
else:
if x.n > p.n:
x = rleft(rleft(g))
else:
g.r = rright(p)
x = rleft(g)
p = x.p
if p:
if x.n < p.n:
p.l = x
else:
p.r = x
return splay(x)
else:
if x.n < p.n:
return rright(p)
else:
return rleft(p)
else:
return x
def splay_insert(p, n, g=None):
r, x = bst_insert(p, n, g)
return splay(x)
def build_splay_tree(s):
''' Build a splay tree from the given sequence '''
v = None
for n in s:
v = splay_insert(v, n)
return v
####################
# The Big Question #
####################
from itertools import permutations
def find_matches(n):
''' Generate all permutations of {1, ..., n} that produce
matching AVL and splay trees '''
for s in permutations(range(1, n+1)):
t1 = build_avl_tree(s)
t2 = build_splay_tree(s)
if t1 == t2:
yield s
def find_match(n):
''' Return a permutation of {1, ..., n} that produces matching
AVL and splay trees, or None if no such permutation exists '''
return next(find_matches(n), None)

Resources