A sequence that forms the same AVL and splay trees? - data-structures

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)

Related

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

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)

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]

Binary Search stuck in infinite loop

I am trying to test a binary search algorithm. The logic seems right, however, I'm running into some issues.
There are two different methods, case and if. If one method fails, the other is right.
1.
arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
case m
when m < t
l = (m + 1)
when m == t
puts "hello world"
when m > t
u = (m - 1)
end
end
end
search(arr, 5)
2.
def search(arr, value)
if arr.nil?
return "value not in array"
end
l = 0
u = (arr.length - 1)
t = value
if l > u
puts "array is wrong!"
return
end
while l < u
m = (l + u) / 2
if m < t
l = (m + 1)
elsif m == t
puts "hello world"
break
elsif m > t
u = (m - 1)
end
end
end
I thought my code would work, but something is causing my while loop to go on infinitely. What am I doing to cause an infinite loop?

Resources