Given a AVL tree and N values to be deleted from the tree. Write a function to delete a given value from the tree.
Example 1:
Tree = 
        4
      /   \
     2     6
    / \   / \  
   1   3 5   7
N = 4
Values to be deleted = {4,1,3,6}
Input: Value to be deleted = 4
Output:
        5    
      /   \
     2     6
    / \     \  
   1   3     7
Input: Value to be deleted = 1
Output:
        5    
      /   \
     2     6
      \     \  
       3     7
Input: Value to be deleted = 3
Output:
        5    
      /   \
     2     6
            \  
             7
Input: Value to be deleted = 6
Output:
        5    
      /   \
     2     7
Your Task:  
You dont need to read input or print anything. Complete the function delelteNode() which takes the root of the tree and the value of the node to be deleted as input parameters and returns the root of the modified tree.
Note: The tree will be checked after each deletion. 
If it violates the properties of balanced BST, an error message will be printed followed by the inorder traversal of the tree at that moment.
If instead all deletion are successful, inorder traversal of tree will be printed.
If every single node is deleted from tree, 'null' will be printed.
 
Expected Time Complexity: O(height of tree)
Expected Auxiliary Space: O(height of tree)
Constraints:
1 ≤ N ≤ 500

Difficulty: MEDIUM_HARD
Tags: Tree algorithms

Data structures

Tree queries
Skill types: Data structures
Expected Time Complexity: O(height of tree)
Expected Auxiliary Space: O(height of tree)

Starter code:
#User function Template for python3



''' structure of tree node:



class Node:

    def __init__(self,x):

        self.data=x

        self.left=None

        self.right=None

        self.height=1



'''



def deleteNode(root, key):

    # code here

    # return root of edited tree



Solutions:
def getHeight(root):
	if root is None:
		return 0
	return root.height

def rightRotate(disbalancedNode):
	newRoot = disbalancedNode.left
	disbalancedNode.left = disbalancedNode.left.right
	newRoot.right = disbalancedNode
	disbalancedNode.height = 1 + max(getHeight(disbalancedNode.left), getHeight(disbalancedNode.right))
	newRoot.height = 1 + max(getHeight(newRoot.left), getHeight(newRoot.right))
	return newRoot

def leftRotate(disbalancedNode):
	newRoot = disbalancedNode.right
	disbalancedNode.right = disbalancedNode.right.left
	newRoot.left = disbalancedNode
	disbalancedNode.height = 1 + max(getHeight(disbalancedNode.left), getHeight(disbalancedNode.right))
	newRoot.height = 1 + max(getHeight(newRoot.left), getHeight(newRoot.right))
	return newRoot

def getBalance(root):
	if root is None:
		return 0
	return getHeight(root.left) - getHeight(root.right)

def getMinValueNode(root):
	if root is None or root.left is None:
		return root
	return getMinValueNode(root.left)

def deleteNode(root, key):
	if root is None:
		return root
	elif key < root.data:
		root.left = deleteNode(root.left, key)
	elif key > root.data:
		root.right = deleteNode(root.right, key)
	else:
		if root.left is None:
			curr = root.right
			root = None
			return curr
		if root.right is None:
			curr = root.left
			root = None
			return curr
		curr = getMinValueNode(root.right)
		root.data = curr.data
		root.right = deleteNode(root.right, curr.data)
	root.height = 1 + max(getHeight(root.left), getHeight(root.right))
	balance = getBalance(root)
	if balance > 1 and getBalance(root.left) >= 0:
		return rightRotate(root)
	elif balance > 1 and getBalance(root.left) < 0:
		root.left = leftRotate(root.left)
		return rightRotate(root)
	elif balance < -1 and getBalance(root.right) <= 0:
		return leftRotate(root)
	elif balance < -1 and getBalance(root.right) > 0:
		root.right = rightRotate(root.right)
		return leftRotate(root)
	return root


def deleteNode(root, key):
	if root == None:
		return root
	if key < root.data:
		root.left = deleteNode(root.left, key)
	elif key > root.data:
		root.right = deleteNode(root.right, key)
	else:
		if root.left == None:
			return root.right
		elif root.right == None:
			return root.left
		tmp = findNext(root.right)
		root.data = tmp
		root.right = deleteNode(root.right, tmp)
	if root == None:
		return root
	root.height = 1 + max(getHeight(root.left), getHeight(root.right))
	bal = getBalance(root)
	if bal > 1 and getBalance(root.left) >= 0:
		return rotateRight(root)
	elif bal > 1 and getBalance(root.left) < 0:
		root.left = rotateLeft(root.left)
		return rotateRight(root)
	elif bal < -1 and getBalance(root.right) <= 0:
		return rotateLeft(root)
	elif bal < -1 and getBalance(root.right) > 0:
		root.right = rotateRight(root.right)
		return rotateLeft(root)
	return root

def getHeight(root):
	if root == None:
		return 0
	return root.height

def getBalance(root):
	if root == None:
		return 0
	return getHeight(root.left) - getHeight(root.right)

def findNext(root):
	while root and root.left:
		root = root.left
	return root.data

def rotateRight(root):
	(x, y, z) = (root, root.left, root.left.left)
	x.left = y.right
	y.right = x
	x.height = 1 + max(getHeight(x.left), getHeight(x.right))
	y.height = 1 + max(getHeight(y.left), getHeight(y.right))
	return y

def rotateLeft(root):
	(x, y, z) = (root, root.right, root.right.right)
	x.right = y.left
	y.left = x
	x.height = 1 + max(getHeight(x.left), getHeight(x.right))
	y.height = 1 + max(getHeight(y.left), getHeight(y.right))
	return y