LeetCode演算法題-Maximum Depth of Binary Tree
這是悅樂書的第164次更新,第166篇原創
01 看題和準備
今天介紹的是LeetCode演算法題中Easy級別的第23題(順位題號是104)。給定二叉樹,找到它的最大深度。最大深度是從根節點到最遠葉節點的最長路徑上的節點數。葉子是沒有子節點的節點。
例如:給定二叉樹[3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其深度= 3。
本次解題使用的開發工具是eclipse,jdk使用的版本是1.8,環境是win7 64位系統,使用Java語言編寫和測試。
02 第一種解法
最大深度是根節點到最遠的葉子節點路徑上包含的節點數,以上面的二叉樹為例,最長路徑有兩條:3->20->15,3->20->7,這兩條路徑上的節點數都是3個,所以最後得出其深度是3的結論。
特殊情況一:當傳入的二叉樹為空時,它沒有任何節點,它的深度是0。
特殊情況二:只有根節點的時候,它的深度是1,只有它自身一個節點。
正常情況:我們可以一步一步試著推導下,一個簡單的二叉樹深度計算過程,還是以上面的二叉樹為例。
從根節點開始,此時節點數為1,因為只有它一個節點。
進入根節點的子節點,此時最長路徑就是計算9這個左子節點和20這個右子節點的最長路徑,顯然9是一個葉子節點,只有本身一個節點;而20擁有自己的子節點,此時就需要算出從20出發的最長路徑。
20有左子節點15,右子節點7,這時需要繼續計算15和7的最長路徑,而15和7都是葉子節點,所以節點數只有1,再加上20節點本身這個屬於根節點的子節點,20節點的最長路徑就是2。
同為3根節點的兩個子節點9、20,子節點9的最長路徑上節點數為1,子節點20的最長路徑上節點數為2,取最大值後,再加上根節點自身的節點數,1+2=3,3就是最長路徑上的節點數,也就是該二叉樹的深度。
分析到這裡,我們知道了,要計算從根節點到最遠葉子節點的節點個數,就需要先計算其左右子節點的最長路徑,而要計算左右子節點的最長路徑,就需要計算他們自身下面的左右子節點的最長路徑了,對此我們可以使用遞迴,算完最下面葉子節點的個數後,再層層往上求其最大值。
public int maxDepth(TreeNode root) { if (root == null) { return 0; } int left = maxDepth(root.left); int right = maxDepth(root.right); return 1 + Math.max(left, right); }
03 第二種解法
既然可以使用遞迴,那我們也可以試著使用遍歷的方法。從根節點開始,自頂向下遍歷子節點,對此使用佇列來臨時儲存每次遍歷的子節點,遍歷完一層子節點就加1,直到所有子節點都遍歷完。
public int maxDepth2(TreeNode root) {
if (root == null) {
return 0;
}
int depth = 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
Queue<TreeNode> tem = new LinkedList<>();
while(!q.isEmpty()){
TreeNode node = q.poll();
if (node.left != null) {
tem.offer(node.left);
}
if (node.right != null) {
tem.offer(node.right);
}
}
q = tem;
depth++;
}
return depth;
}
特殊情況還是和第一種解法一樣,內層while迴圈是遍歷每層的子節點。
04 第三種解法
第二種解法的內層迴圈那裡,我們使用了新的佇列來接收每次迴圈要進入的下一層節點資料,是否可以改動下,使其更加的簡潔?這裡我們使用佇列的大小來控制它,從根節點開始,進入佇列後,佇列的size為1,開始進入內層迴圈,內層迴圈走了一次後,佇列裡又多了兩個子節點,此時的size為2,然後繼續開始內層迴圈,直到所有的子節點遍歷完。
public int maxDepth3(TreeNode root) {
if (root == null) {
return 0;
}
int depth = 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
int n = q.size();
while(n-- > 0){
TreeNode node = q.poll();
if (node.left != null) {
q.offer(node.left);
}
if (node.right != null) {
q.offer(node.right);
}
}
depth++;
}
return depth;
}
05 驗證與測試
對於上面的兩種解法,使用了一個簡單的二叉樹做了資料測試。
public static void main(String[] args) {
Easy_104_MaximumDepthOfBinaryTree instance = new Easy_104_MaximumDepthOfBinaryTree();
TreeNode t = new TreeNode(1);
TreeNode t2 = new TreeNode(2);
TreeNode t3 = new TreeNode(3);
TreeNode t4 = new TreeNode(4);
TreeNode t5 = new TreeNode(5);
TreeNode t6 = new TreeNode(6);
TreeNode t7 = new TreeNode(7);
TreeNode t8 = new TreeNode(8);
t.left = t2;
t.right = t3;
t2.left = t4;
t2.right = t5;
t3.left = t6;
t3.right = t7;
t7.left = t8;
long start = System.nanoTime();
int result = instance.maxDepth(t);
long end = System.nanoTime();
System.out.println("maxDepth---輸出:"+result+" , 用時:"+(end-start)/1000+"微秒");
long start2 = System.nanoTime();
int result2 = instance.maxDepth2(t);
long end2 = System.nanoTime();
System.out.println("maxDepth2---輸出:"+result2+" , 用時:"+(end2-start2)/1000+"微秒");
long start3 = System.nanoTime();
int result3 = instance.maxDepth3(t);
long end3 = System.nanoTime();
System.out.println("maxDepth3---輸出:"+result3+" , 用時:"+(end3-start3)/1000+"微秒");
}
測試結果如下:
maxDepth---輸出:4 , 用時:23微秒
maxDepth2---輸出:4 , 用時:586微秒
maxDepth3---輸出:4 , 用時:16微秒
從測試的結果可以看出,遍歷和遞迴都是可以解決問題的,第二種解法因為每進一次迴圈都要建立新的物件,這對記憶體和執行時間都是不小的消耗,經過優化後第三種解法更加適合遍歷使用。
06 小結
以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支援!