判斷一棵樹是否是合法二叉樹

題目:給定一棵樹,判斷是否是合法的二叉搜索樹。
如果合法返回true,不合法則返回false。

首先,應該明確二叉搜索樹的定義,二叉搜索樹是一個遞歸的定義,即二叉搜索樹的左子節點小於根節點,而右節點大於根節點,同時左右子樹也是一棵二叉搜索樹。

錯誤的想法:
先判斷根節點和左右子節點的大小關係,如果不符合大小關係,直接返回false。如果符合,則遞歸調用判斷左右子樹是否合法。
如下:

/**
* 驗證是否是合法二叉搜索樹
 */
public boolean isValidBST(TreeNode root) {
    if (root == null) {
        return true;
    }
	
	// 判斷根節點與左子節點的關係
    boolean isLeftValid = true;
    if (root.left != null) {
        isLeftValid = root.val > root.left.val;
    }

	// 判斷根節點與右子節點的關係
    boolean isRightValid = true;
    if (root.right != null) {
        isRightValid = root.val < root.right.val;
    }
	
	// 左子點及右子節點遞歸調用,判斷是否符合這種大小關係
    return isLeftValid && isRightValid && isValidBST(root.left) && isValidBST(root.right);
}

這種想法只考慮了根節點與其左右子節點的關係,即只考慮了每三個節點之前的大小關係,而沒有考慮子節點與所有剩餘子節點的大小關係。
如對於如下的二叉樹則會得到錯誤結果:

				4
		1				6
	0	    2     3		7

這棵二叉樹很顯然不是二叉搜索樹,因爲其中的3小於4。

正確的解法一

/**
* 驗證是否是合法二叉搜索樹
*/
public boolean isValidBST(TreeNode root) {
   return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}

/**
* 當前root值的上下界要在指定的範圍內
* @param root
* @param lower 下界
* @param upper 上界 
* @return
*/
public boolean isValidBST(TreeNode root, long lower, long upper) {
   if (root == null) {
       return true;
   }

   // 不符合搜索樹的定義
   if (root.val <= lower || root.val >= upper) {
       return false;
   }

   return isValidBST(root.left, lower, root.val) && isValidBST(root.right, root.val, upper);
}

正確的解法二:

// 用long型的數據來記錄前一個節點,int型過不了leetcode測試用例
long pre = Long.MIN_VALUE;
/**
 * @param root
 * @return 本質上還是利用了中序的排序性質,只是用了遞歸的方式。
 */
public boolean isValidBST(TreeNode root) {
    if (root == null) {
        return true;
    }

    // 先遞歸判斷左子樹是否合法
    if (!isValidBST(root.left)) {
        return false;
    }

    // 在判斷當前節點與左子節點的大小關係
    if (root.val <= pre) {
        return false;
    }
    pre = root.val;

    // 在判斷右子樹是否合法
    return isValidBST(root.right);
}

正確的解法三:
利用二叉搜索樹的中序遍歷有序性,邊遍歷邊比較。

/**
 * 在遍歷的過程中比較, 在leetcode上這種方式的時間還沒有上面的遞歸調用快。 
 */
public boolean isValidBST3(TreeNode root) {
    if (root == null) {
        return true;
    }

    Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
    TreeNode cur = root;
    // stack是空的,故加入cur不爲空的判斷
    while (!stack.isEmpty() || cur != null) {
        while (cur != null) {
            stack.push(cur);
            cur = cur.left;
        }

        TreeNode tmp = stack.pop();
        if (tmp.val <= pre) {
            return false;
        } else {
            pre = tmp.val;
        }

        cur = tmp.right;
        // 這裏不能在push了,因爲cur在while循環開頭就會push,如果這裏push就會push兩次了。 
        // if (cur != null) {
            // stack.push(cur);
        // }
    }

    return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章