數據結構 - 二叉樹 - 面試中常見的二叉樹算法題
數據結構是面試中必定考查的知識點,面試者需要掌握幾種經典的數據結構:線性表(數組、鏈表)、棧與隊列、樹(二叉樹、二叉查找樹、平衡二叉樹、紅黑樹)、圖。
本文主要介紹樹中的常見的二叉樹數據結構。包括
- 概念簡介
- 二叉樹中樹節點的數據結構(Java)
- 二叉樹的遍歷(Java)
- 常見的二叉樹算法題(Java)
概念簡介
如果對二叉樹概念已經基本掌握,可以跳過該部分,直接查看常見鏈表算法題。
二叉樹基本概念
二叉樹在圖論中是這樣定義的:二叉樹是一個連通的無環圖,並且每一個頂點的度不大於3。有根二叉樹還要滿足根結點的度不大於2。有了根結點之後,每個頂點定義了唯一的父結點,和最多2個子結點。二叉樹性質如下:
- 二叉樹的每個結點至多隻有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。
- 二叉樹的第 i 層至多有
2i−1 個結點。 - 深度爲 k 的二叉樹至多有
2k−1 個結點。 - 對任何一棵二叉樹T,如果其終端結點數爲
n0 ,度爲2的結點數爲n2 ,則n0=n2+1 。 - 一棵深度爲k,且有
2k−1 個節點稱之爲滿二叉樹; - 深度爲k,有n個節點的二叉樹,當且僅當其每一個節點都與深度爲k的滿二叉樹中,序號爲1至n的節點對應時,稱之爲完全二叉樹。
- 平衡二叉樹又被稱爲AVL樹(區別於AVL算法),它是一棵二叉排序樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。
二叉樹中樹節點的數據結構
二叉樹由一系列樹結點組成,每個結點包括三個部分:一個是存儲數據元素的數據域,另一個是存儲左子結點地址的指針域,另一個是存儲右子結點地址的指針域。
定義樹節點爲類:TreeNode。具體實現如下:
public class TreeNode {
public int val; // 數據域
public TreeNode left; // 左子樹根節點
public TreeNode right; // 右子樹根節點
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
}
二叉樹的遍歷
1. 前序遍歷
遞歸解法:
- 如果二叉樹爲空,空操作
- 如果二叉樹不爲空,訪問根節點,前序遍歷左子樹,前序遍歷右子樹
/**
* 1. 前序遍歷
* 遞歸
* @param root 樹根節點
*/
public static void preorderTraversalRec(TreeNode root){
if (root == null) {
return;
}
System.out.print(root.val + "->");
preorderTraversalRec(root.left);
preorderTraversalRec(root.right);
}
非遞歸解法:用一個輔助stack,總是先把右孩子放進棧。
/**
* 1. 前序遍歷
* 非遞歸
* @param root 樹根節點
*/
public static void preorderTraversal2(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>(); // 輔助棧
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) { // 不斷將左子節點入棧,直到cur爲空
stack.push(cur);
System.out.print(cur.val + "->"); // 前序遍歷,先打印當前節點在打印左子節點,然後再把右子節點加到棧中
cur = cur.left;
}
if (!stack.isEmpty()) { // 棧不爲空,彈出棧元素
cur = stack.pop(); // 此時彈出最左邊的節點
cur = cur.right; // 令當前節點爲右子節點
}
}
}
/**
* 1. 前序遍歷
* 非遞歸解法2
* @param root 樹根節點
*/
public static void preorderTraversal(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>(); // 輔助棧保存樹節點
stack.add(root);
while (!stack.isEmpty()) { // 棧不爲空
TreeNode temp = stack.pop();
System.out.print(temp.val + "->"); // 先根節點,因爲是前序遍歷
if (temp.right != null) { // 先添加右孩子,因爲棧是先進後出
stack.add(temp.right);
}
if (temp.left != null) {
stack.add(temp.left);
}
}
}
2. 中序遍歷
遞歸解法:
- 如果二叉樹爲空,空操作
- 如果二叉樹不爲空,中序遍歷左子樹,訪問根節點,中序遍歷右子樹
/**
* 2. 中序遍歷
* 遞歸
* @param root 樹根節點
*/
public static void inorderTraversalRec(TreeNode root){
if (root == null) {
return;
}
inorderTraversalRec(root.left);
System.out.print(root.val + "->");
inorderTraversalRec(root.right);
}
非遞歸解法:用棧先把根節點的所有左孩子都添加到棧內,然後輸出棧頂元素,再處理棧頂元素的右子樹。
/**
* 2. 中序遍歷
* 非遞歸
* @param root 樹根節點
*/
public static void inorderTraversal(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>(); // 輔助棧
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) { // 不斷將左子節點入棧,直到cur爲空
stack.push(cur);
cur = cur.left;
}
if (!stack.isEmpty()) { // 棧不爲空,彈出棧元素
cur = stack.pop(); // 此時彈出最左邊的節點
System.out.print(cur.val + "->"); // 中序遍歷,先打印左子節點在打印當前節點,然後再把右子節點加到棧中
cur = cur.right; // 令當前節點爲右子節點
}
}
}
3. 後序遍歷
遞歸解法:
- 如果二叉樹爲空,空操作
- 如果二叉樹不爲空,後序遍歷左子樹,後序遍歷右子樹,訪問根節點
/**
* 3. 後序遍歷
* 遞歸
* @param root 樹根節點
*/
public static void postorderTraversalRec(TreeNode root){
if (root == null) {
return;
}
postorderTraversalRec(root.left);
postorderTraversalRec(root.right);
System.out.print(root.val + "->");
}
非遞歸解法:雙棧法。
/**
* 3. 後序遍歷
* 非遞歸
* @param root 樹根節點
*/
public static void postorderTraversal(TreeNode root) {
if(root == null) {
return;
}
Stack<TreeNode> stack1 = new Stack<>(); // 保存樹節點
Stack<TreeNode> stack2 = new Stack<>(); // 保存後序遍歷的結果
stack1.add(root);
while (!stack1.isEmpty()) {
TreeNode temp = stack1.pop();
stack2.push(temp); // 將彈出的元素加到stack2中
if (temp.left != null) { // 左子節點先入棧
stack1.push(temp.left);
}
if (temp.right != null) { // 右子節點後入棧
stack1.push(temp.right);
}
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val + "->");
}
}
4. 層次遍歷
思路:分層遍歷二叉樹(按層次從上到下,從左到右)迭代,相當於廣度優先搜索,使用隊列實現。隊列初始化,將根節點壓入隊列。當隊列不爲空,進行如下操作:彈出一個節點,訪問,若左子節點或右子節點不爲空,將其壓入隊列。
/**
* 4. 層次遍歷
* @param root 根節點
*/
public static void levelTraversal(TreeNode root){
if(root == null) {
return;
}
Queue<TreeNode> queue = new LinkedList<>(); // 對列保存樹節點
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.poll();
System.out.print(temp.val + "->");
if (temp.left != null) { // 添加左右子節點到對列
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
}
常見的二叉樹算法題
1. 求二叉樹中的節點個數
遞歸解法:
- 如果二叉樹爲空,節點個數爲0
- 如果二叉樹不爲空,二叉樹節點個數 = 左子樹節點個數 + 右子樹節點個數 + 1
/**
* 1. 求二叉樹中的節點個數
* 遞歸
* @param root 樹根節點
* @return 節點個數
*/
public static int getNodeNumRec(TreeNode root) {
if (root == null) {
return 0;
}
return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
}
非遞歸解法:
/**
* 1. 求二叉樹中的節點個數
* 非遞歸
* @param root 樹根節點
* @return 節點個數
*/
public static int getNodeNum(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>(); // 用隊列保存樹節點,先進先出
queue.add(root);
int count = 1; // 節點數量
while (!queue.isEmpty()) {
TreeNode temp = queue.poll(); // 每次從對列中刪除節點,並返回該節點信息
if (temp.left != null) { // 添加左子孩子到對列
queue.add(temp.left);
count++;
}
if (temp.right != null) { // 添加右子孩子到對列
queue.add(temp.right);
count++;
}
}
return count;
}
2. 求二叉樹的深度(高度)
遞歸解法:
- 如果二叉樹爲空,二叉樹的深度爲0
- 如果二叉樹不爲空,二叉樹的深度 = max(左子樹深度, 右子樹深度) + 1
/**
* 求二叉樹的深度(高度)
* 遞歸
* @return 樹的深度
*/
public static int getDepthRec(TreeNode root) {
if (root == null) {
return 0;
}
return Math.max(getDepthRec(root.left), getDepthRec(root.right)) + 1;
}
非遞歸解法:
/**
* 求二叉樹的深度(高度)
* 非遞歸
* @param root 樹根節點
* @return 樹的深度
*/
public static int getDepth(TreeNode root) {
if (root == null) {
return 0;
}
int currentLevelCount = 1; // 當前層的節點數量
int nextLevelCount = 0; // 下一層節點數量
int depth = 0; // 樹的深度
Queue<TreeNode> queue = new LinkedList<>(); // 對列保存樹節點
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.remove(); // 移除節點
currentLevelCount--; // 當前層節點數減1
if (temp.left != null) { // 添加左節點並更新下一層節點個數
queue.add(temp.left);
nextLevelCount++;
}
if (temp.right != null) { // 添加右節點並更新下一層節點個數
queue.add(temp.right);
nextLevelCount++;
}
if (currentLevelCount == 0) { // 如果是該層的最後一個節點,樹的深度加1
depth++;
currentLevelCount = nextLevelCount; // 更新當前層節點數量並且重置下一層節點數量
nextLevelCount = 0;
}
}
return depth;
}
3. 求二叉樹第k層的節點個數
遞歸解法:
思路:求以root爲根的k層節點數目,等價於求以root左孩子爲根的k-1層(因爲少了root)節點數目 加上以root右孩子爲根的k-1層(因爲 少了root)節點數目。即:
- 如果二叉樹爲空或者k<1,返回0
- 如果二叉樹不爲空並且k==1,返回1
- 如果二叉樹不爲空且k>1,返回root左子樹中k-1層的節點個數與root右子樹k-1層節點個數之和
/**
* 求二叉樹第k層的節點個數
* 遞歸
* @param root 根節點
* @param k 第k個節點
* @return 第k層節點數
*/
public static int getNodeNumKthLevelRec(TreeNode root, int k) {
if (root == null || k < 1) {
return 0;
}
if (k == 1) {
return 1;
}
return getNodeNumKthLevelRec(root.left, k - 1) + getNodeNumKthLevelRec(root.right, k - 1);
}
4. 求二叉樹中葉子節點的個數
遞歸解法:
- 如果二叉樹爲空,返回0
- 如果二叉樹是葉子節點,返回1
- 如果二叉樹不是葉子節點,二叉樹的葉子節點數 = 左子樹葉子節點數 + 右子樹葉子節點數
/**
* 4. 求二叉樹中葉子節點的個數
* 遞歸
* @param root 根節點
* @return 葉子節點個數
*/
public static int getNodeNumLeafRec(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);
}
非遞歸解法:基於層次遍歷進行求解,利用Queue進行。
/**
* 4. 求二叉樹中葉子節點的個數(迭代)
* 非遞歸
* @param root 根節點
* @return 葉子節點個數
*/
public static int getNodeNumLeaf(TreeNode root){
if (root == null) {
return 0;
}
int leaf = 0; // 葉子節點個數
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.poll();
if (temp.left == null && temp.right == null) { // 葉子節點
leaf++;
}
if (temp.left != null) {
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
return leaf;
}
5. 判斷兩棵二叉樹是否相同的樹
遞歸解法:
- 如果兩棵二叉樹都爲空,返回真
- 如果兩棵二叉樹一棵爲空,另外一棵不爲空,返回假
- 如果兩棵二叉樹都不爲空,如果對應的左子樹和右子樹都同構返回真,其他返回假
/**
* 5. 判斷兩棵二叉樹是否相同的樹。
* 遞歸
* @param r1 二叉樹1
* @param r2 二叉樹2
* @return 是否相同
*/
public static boolean isSameRec(TreeNode r1, TreeNode r2) {
if (r1 == null && r2 == null) { // 都是空
return true;
} else if (r1 == null || r2 == null) { // 有一個爲空,一個不爲空
return false;
}
if (r1.val != r2.val) { // 兩個不爲空,但是值不相同
return false;
}
return isSameRec(r1.left, r2.left) && isSameRec(r1.right, r2.right); // 遞歸遍歷左右子節點
}
非遞歸解法:利用Stack對兩棵樹對應位置上的節點進行判斷是否相同。
/**
* 5. 判斷兩棵二叉樹是否相同的樹(迭代)
* 非遞歸
* @param r1 二叉樹1
* @param r2 二叉樹2
* @return 是否相同
*/
public static boolean isSame(TreeNode r1, TreeNode r2){
if (r1 == null && r2 == null) { // 都是空
return true;
} else if (r1 == null || r2 == null) { // 有一個爲空,一個不爲空
return false;
}
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.add(r1);
stack2.add(r2);
while (!stack1.isEmpty() && !stack2.isEmpty()) {
TreeNode temp1 = stack1.pop();
TreeNode temp2 = stack2.pop();
if (temp1 == null && temp2 == null) { // 兩個元素都爲空,因爲添加的時候沒有對空節點做判斷
continue;
} else if (temp1 != null && temp2 != null && temp1.val == temp2.val) {
stack1.push(temp1.left); // 相等則添加左右子節點判斷
stack1.push(temp1.right);
stack2.push(temp2.left);
stack2.push(temp2.right);
} else {
return false;
}
}
return true;
}
6. 判斷二叉樹是不是平衡二叉樹
遞歸實現:藉助前面實現好的求二叉樹高度的函數
- 如果二叉樹爲空, 返回真
- 如果二叉樹不爲空,如果左子樹和右子樹都是AVL樹並且左子樹和右子樹高度相差不大於1,返回真,其他返回假
/**
* 6. 判斷二叉樹是不是平衡二叉樹
* 遞歸
* @param root 根節點
* @return 是否二叉平衡樹(AVL樹)
*/
public static boolean isAVLTree(TreeNode root) {
if (root == null) {
return true;
}
if (Math.abs(getDepth(root.left) - getDepth(root.right)) > 1) { // 左右子樹高度差大於1
return false;
}
return isAVLTree(root.left) && isAVLTree(root.right); // 遞歸判斷左右子樹
}
7. 求二叉樹的鏡像
遞歸實現:破壞原來的樹,把原來的樹改成其鏡像
- 如果二叉樹爲空,返回空
- 如果二叉樹不爲空,求左子樹和右子樹的鏡像,然後交換左右子樹
/**
* 7. 求二叉樹的鏡像
* 遞歸
* @param root 根節點
* @return 鏡像二叉樹的根節點
*/
public static TreeNode mirrorRec(TreeNode root) {
if (root == null) {
return root;
}
TreeNode left = mirrorRec(root.right); // 遞歸鏡像左右子樹
TreeNode right = mirrorRec(root.left);
root.left = left; // 更新根節點的左右子樹爲鏡像後的樹
root.right = right;
return root;
}
遞歸實現:不能破壞原來的樹,返回一個新的鏡像樹
- 如果二叉樹爲空,返回空
- 如果二叉樹不爲空,求左子樹和右子樹的鏡像,然後交換左右子樹
/**
* 7. 求二叉樹的鏡像
* 遞歸
* @param root 根節點
* @return 鏡像二叉樹的根節點
*/
public static TreeNode mirrorCopyRec(TreeNode root) {
if (root == null) {
return root;
}
TreeNode newRoot = new TreeNode(root.val); // 創建新節點,然後交換左右子樹
newRoot.left = mirrorCopyRec(root.right);
newRoot.right = mirrorCopyRec(root.left);
return newRoot;
}
非遞歸實現:破壞原來的樹,把原來的樹改成其鏡像
/**
* 7. 求二叉樹的鏡像
* 非遞歸
* @param root 根節點
* @return 鏡像二叉樹的根節點
*/
public static void mirror(TreeNode root) {
if (root == null) {
return ;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode cur = stack.pop();
// 交換左右孩子
TreeNode tmp = cur.right;
cur.right = cur.left;
cur.left = tmp;
if(cur.right != null) {
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
非遞歸實現:不能破壞原來的樹,返回一個新的鏡像樹
/**
* 7. 求二叉樹的鏡像
* 非遞歸
* @param root 根節點
* @return 鏡像二叉樹的根節點
*/
public static TreeNode mirrorCopy(TreeNode root) {
if (root == null) {
return null;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
Stack<TreeNode> newStack = new Stack<TreeNode>();
stack.push(root);
TreeNode newRoot = new TreeNode(root.val);
newStack.push(newRoot);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
TreeNode newCur = newStack.pop();
if (cur.right != null) {
stack.push(cur.right);
newCur.left = new TreeNode(cur.right.val);
newStack.push(newCur.left);
}
if (cur.left != null) {
stack.push(cur.left);
newCur.right = new TreeNode(cur.left.val);
newStack.push(newCur.right);
}
}
return newRoot;
}
8. 判斷兩個二叉樹是否互相鏡像
遞歸解法:與比較兩棵二叉樹是否相同解法一致(題5),非遞歸解法省略。
- 比較r1的左子樹的鏡像是不是r2的右子樹
- 比較r1的右子樹的鏡像是不是r2的左子樹
/**
* 8. 判斷兩個樹是否互相鏡像
* @param r1 二叉樹 1
* @param r2 二叉樹 2
* @return 是否互相鏡像
*/
public static boolean isMirrorRec(TreeNode r1, TreeNode r2) {
if (r1 == null && r2 == null) {
return true;
} else if (r1 == null || r2 == null) {
return false;
}
if (r1.val != r2.val) {
return false;
}
// 遞歸比較r1的左子樹的鏡像是不是r2右子樹
// 和r1的右子樹的鏡像是不是r2的左子樹
return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
}
9. 求二叉樹中兩個節點的最低公共祖先節點
遞歸解法:
- 如果兩個節點分別在根節點的左子樹和右子樹,則返回根節點
- 如果兩個節點都在左子樹,則遞歸處理左子樹;如果兩個節點都在右子樹,則遞歸處理右子樹
/**
* 9. 求二叉樹中兩個節點的最低公共祖先節點
* 遞歸
* @param root 樹根節點
* @param n1 第一個節點
* @param n2 第二個節點
* @return 最低公共祖先節點
*/
public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {
if (findNodeRec(root.left, n1)) { // 如果n1在左子樹
if (findNodeRec(root.right, n2)) { // 如果n2在右子樹
return root; // 返回根節點
} else { // 如果n2也在左子樹
return getLastCommonParentRec(root.left, n1, n2); // 遞歸處理
}
} else { // 如果n1在右子樹
if (findNodeRec(root.left, n2)) { // 如果n2在左子樹
return root; // 返回根節點
} else { // 如果n2在右子樹
return getLastCommonParentRec(root.right, n1, n2); // 遞歸處理
}
}
}
/**
* 遞歸判斷一個點是否在樹裏
* @param root 根節點
* @param node 查找的節點
* @return 是否找到該節點
*/
private static boolean findNodeRec(TreeNode root, TreeNode node) {
if (node == null || root == null) {
return false;
}
if (root == node) {
return true;
}
// 先嚐試在左子樹中查找
boolean found = findNodeRec(root.left, node);
if (!found) { // 如果查找不到,再在右子樹中查找
found = findNodeRec(root.right, node);
}
return found;
}
/**
* 9. 樹中兩個節點的最低公共祖先節點
* 遞歸解法2(更簡單)
* @param root 樹根節點
* @param n1 第一個節點
* @param n2 第二個節點
* @return 最低公共祖先節點
*/
public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {
if (root == null) {
return null;
}
// 如果有一個match,則說明當前node就是要找的最低公共祖先
if (root.equals(n1) || root.equals(n2)) {
return root;
}
TreeNode commonLeft = getLastCommonParentRec2(root.left, n1, n2);
TreeNode commonRight = getLastCommonParentRec2(root.right, n1, n2);
// 如果一個在左子樹找到,一個在右子樹找到,則說明root是唯一可能得最低公共祖先
if (commonLeft != null && commonRight != null) {
return root;
}
// 其他情況是要不然在左子樹要不然在右子樹
if (commonLeft != null) {
return commonLeft;
}
return commonRight;
}
非遞歸算法:得到從二叉樹根節點到兩個節點的路徑,路徑從頭開始的最後一個公共節點就是它們的最低公共祖先節點
/**
* 9. 樹中兩個節點的最低公共祖先節點
* 非遞歸
* @param root 樹根節點
* @param n1 第一個節點
* @param n2 第二個節點
* @return 第一個公共祖先節點
*/
public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {
if (root == null || n1 == null || n2 == null) {
return null;
}
ArrayList<TreeNode> p1 = new ArrayList<>();
boolean res1 = getNodePath(root, n1, p1);
ArrayList<TreeNode> p2 = new ArrayList<>();
boolean res2 = getNodePath(root, n2, p2);
if (!res1 || !res2) {
return null;
}
TreeNode last = null;
Iterator<TreeNode> iter1 = p1.iterator();
Iterator<TreeNode> iter2 = p2.iterator();
while (iter1.hasNext() && iter2.hasNext()) {
TreeNode tmp1 = iter1.next();
TreeNode tmp2 = iter2.next();
if (tmp1 == tmp2) {
last = tmp1;
} else { // 直到遇到非公共節點
break;
}
}
return last;
}
/**
* 把從根節點到node路徑上所有的點都添加到path中
* @param root 樹根節點
* @param node 終點節點
* @param path 路徑
* @return 是否是目標節點
*/
public static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {
if (root == null) {
return false;
}
path.add(root); // 把這個節點添加到路徑中
if (root == node) {
return true;
}
boolean found = false;
found = getNodePath(root.left, node, path); // 先在左子樹中找
if (!found) {
found = getNodePath(root.right, node, path);
}
if (!found) { // 如果實在沒找到證明這個節點不在路徑中,刪除剛剛那個節點
path.remove(root);
}
return found;
}
10. 判斷是否爲二分查找樹BST
遞歸解法:中序遍歷的結果應該是遞增的。
/**
* 10. 判斷是否爲二分查找樹BST
* @param root 根節點
* @param pre 上一個保存的節點
* @return 是否爲BST樹
*/
public static boolean isValidBST(TreeNode root, int pre){
if (root == null) {
return true;
}
boolean left = isValidBST(root.left, pre);
if (!left) {
return false;
}
if(root.val <= pre) {
return false;
}
pre = root.val;
boolean right = isValidBST(root.right, pre);
if(!right) {
return false;
}
return true;
}
非遞歸解法:參考非遞歸中序遍歷。
/**
* 10. 判斷是否爲二分查找樹BST
* 非遞歸
* @param root 根節點
*/
public boolean isValidBST2(TreeNode root){
Stack<TreeNode> stack = new Stack<>();
//設置前驅節點
TreeNode pre = null;
while(root != null || !stack.isEmpty()){
while (root != null) { // 將當前節點,以及左子樹一直入棧,循環結束時,root==null
stack.push(root);
root = root.left;
}
root = stack.pop();
//比較並更新前驅,與普通遍歷的區別就在下面四行
if(pre != null && root.val <= pre.val){
return false;
}
pre = root;
root = root.right; //訪問右子樹
}
return true;
}