A simple and clean code can be referred to the following, which is lgn average time complexity:
java code
However, the aforementioned code does not consider corner cases, such as:

at least one of root, p, and q is null;

at least one of p & q is not a node in the tree rooted at root.
The following code with lgn average time complexity can deal with above corner cases.
private boolean binarySearch(TreeNode t, TreeNode n){
if(t==null) return false;
if(t.val == n.val) return true;
return n.val<t.val?
binarySearch(t.left, n) :
binarySearch(t.right, n);
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null  p==null  q==null 
!binarySearch(root, p)  !binarySearch(root, q))
return null;
if(p.val == q.val) return p;
TreeNode r = root;
while((p.valr.val)*(q.valr.val) > 0)
r = p.val<r.val? r.left : r.right;
return r;
}