自學演算法之判斷一個二叉樹是否平衡/搜尋/完全二叉樹
阿新 • • 發佈:2018-12-16
話不多說,在面試中遇到過,一臉矇蔽,被虐出翔…以下所述,僅僅是手撕程式碼時候使用,若是需要線上程式設計,可以根據該思路編寫對應AC程式碼。
-
如何判斷一個二叉樹是否平衡?要解決這個問題,首先要知道什麼是平衡二叉樹。
-
平衡二叉樹定義如下:
- 首先,是一個二叉樹,且每個節點的左右子樹的高度差的絕對值不超過1。
- 其次,空樹是平衡的。
-
試想要實現該程式碼,需要怎麼記錄其中的高度差、當前節點是否平衡、以及怎麼統計每個節點的左右子樹的高度,話不多說,解決思路如下:
-
高度差和左右子樹高度,其實我們只需要記錄一個,除此之外,還需記錄當前節點是否平衡,那麼總共有兩個變數,資料結構如下:
static
-
確定返回資料結構後,我們可以使用深度遍歷,判斷每個節點是否平衡。
- 使用前序遍歷,遍歷二叉樹所有節點。
public static returnDate process(TreeNode head) { // 空=平衡 if(head ==
-
-
-
平衡二叉樹已經解決,那麼我再看怎麼判斷一個二叉樹是BST樹。
- BST,即binary search tree二叉搜尋樹,其性質如下:
- 若任意結點的左子樹不空,則左子樹上的所有節點均小於其根節點。
- 若任意結點的右子樹不空,則右子樹上的所有節點均大於其根節點。
- 任意節點的左、右子樹皆為BST樹。
- 沒有值相等的數,即數值不重複。
- 其實,有很多種方法解決該問題,筆者只提供其中一種。
- 解決思路:如果某二叉樹的中序遍歷是升序的,則該樹是BST樹。
- 中序遍歷有遞迴和非遞迴兩種實現方法,在此筆者選擇非遞迴實現。
public static boolean isBST(TreeNode head) { if(head == null) return true; Stack<TreeNode> stack = new Stack<>(); TreeNode cur = head; // 記錄狀態 boolean is_bst = true; // 前一個節點 int pre = Integer.MIN_VALUE; while(!stack.isEmpty() || cur != null) { if(cur != null) { stack.push(cur); cur = cur.left; } else { cur = stack.pop(); // 迭代 if(is_bst) { // 初始化前一個節點 pre = cur.val; is_bst = false; } else if(pre > cur.val) { return false; } else { // 記錄前一個節點 pre = cur.val; } cur = cur.right; } } return true; }
- BST,即binary search tree二叉搜尋樹,其性質如下:
-
最後一個判斷平衡二叉樹邏輯比較複雜,萬事開頭難,先從簡單走起。
-
完全二叉樹的定義:
-
對於深度為K的,N個節點 二叉樹,當且僅當其每一個節點都與深度為K的滿二叉樹中編號從1到N的結點。
-
其實,看了其定義,筆者也看不懂。
-
簡單點,要畫一個完全二叉樹,必須先畫根節點,再畫左結點,最後畫右結點,畫的時候順序必須嚴格一致。
-
-
解決思路:
- 先確定遍歷方式:筆者使用層序遍歷
- 明確違反完全二叉樹條件:
- 如果一個節點,有右子結點,沒有左子結點,則一定不是完全二叉樹。
- 如果一個節點左右子節點不全,即有左沒右或兩個都沒有,若出現該狀態,則接下來的節點都必須是葉子節點。
/** * 判斷一個樹是否是完全二叉樹 * 思路, 按層遍歷 * Q1,如果一個節點有右結點,而沒有左結點,一定不是。 * Q2,如果一個節點左右不全, 即有左沒右,或左右都沒有, 該節點之後出現的結點,必須是葉子結點。 * @author [email protected] * */ public static boolean isCBT(TreeNode head) { if(head == null) return true; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(head); // 左右孩子 TreeNode left = null; TreeNode right = null; // 開啟狀Q2 boolean flag = false; while(!queue.isEmpty()) { TreeNode current = queue.poll(); left = current.left; right = current.right; // 開啟狀態後, 節點不會存在左右子節點 if(flag && (left != null || right != null) || (left == null && right != null)) { return false; } // 層序遍歷, 入隊邏輯 if(left != null) { queue.offer(left); } if(right != null) { queue.offer(right); } // 遇到Q2, 開啟狀態, 即以後的每一個節點都是葉子節點 if(left == null || right == null) { flag = true; } } return true; }
-
-
最後,只要記住每種二叉樹的性質,程式碼很好寫,僅是效率的差別而已。