Below gave faster run time , probably because it terminates as soon as it finds a largest BST - even though asymptotic complexity is not O(n). Worst case performance is likely only when its there're no BST greater than size 1 (leaf)

```
class Solution(object):
def largestBSTSubtree(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def isbst(root, minv, maxv):
if root:
return minv < root.val < maxv and isbst(root.left, minv, root.val) and isbst(root.right, root.val, maxv)
return True
def size(root):
if root:
return size(root.left) + size(root.right) + 1
return 0
if not root:
return 0
sentinel = TreeNode(None)
q = []
ans = 0
q.append(root)
q.append(sentinel)
while q:
node = q.pop(0)
if node is sentinel:
if ans > 1:
return ans
if q:
q.append(sentinel)
continue
if isbst(node, -float('inf'), float('inf')):
#print('isbst', node.val)
ans = max(ans,size(node))
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return ans
```

Also inspired by this code, I wrote following, which led to double the run time - looks like namedtuple incur overhead

```
class Solution(object):
def largestBSTSubtree(self, root):
"""
:type root: TreeNode
:rtype: int
"""
self.ans = 0
Result = collections.namedtuple('Result', ['isBst', 'size', 'minv', 'maxv'])
def func(root, minv, maxv):
if root:
left = func(root.left, minv, root.val)
right = func(root.right, root.val, maxv)
isBst = left.isBst and right.isBst and left.maxv < root.val < right.minv
size = 0
if left.isBst:
self.ans = max(self.ans, left.size)
if right.isBst:
self.ans = max(self.ans, right.size)
if isBst:
size = left.size + 1 + right.size
self.ans = max(self.ans, size)
minv = min(left.minv, root.val)
maxv = max(right.maxv, root.val)
return Result(isBst, size, minv, maxv)
return Result(isBst=True, size=0, minv=float('inf'), maxv=-float('inf'))
func(root, -float('inf'), float('inf'))
return self.ans
```