一、遞歸實現
思想:借助棧結構來保存路徑上的結點,首先從根結點開始,一直往左找,如果左邊找到就返回true;否則,如果左邊找不到並且右子樹不為空的情況下再繼續往右子樹找。如果左右子樹都找不到,就彈出棧頂結點並返回false。方法運行完畢后,棧中保存的元素就是一條從根到給定結點的路徑。
public static boolean searchNode(TreeNode root,Stack<TreeNode> s,TreeNode node) { if(root == null) return false; s.push(root); if(root.val == node.val) return true; boolean b = false; //先去左子樹找 if(root.left != null) b = searchNode(root.left,s,node); //左子樹找不到並且右子樹不為空的情況下才去找 if(!b && root.right != null) b = searchNode(root.right,s,node); //左右都找不到,彈出棧頂元素 if(!b) s.pop(); return b; }
程序運行結束后,棧中保存的就是要求的路徑,參數root代表根節點,s代表棧,node代表給定的節點。如果不想用值來比較,就直接把if(root.val == node.val)換成if(root == node),道理都是一樣的。
二、非遞歸實現
思想:這個稍微有些復雜,當然也是要借助棧來完成。其實這里和二叉樹的非遞歸先序遍歷的思想差不多,只是在這個基礎上進行一些改造。首先,新建一個棧,保存根節點。然后開始一直向左查找,查找的過程中把結點入棧。如果在向左找的過程中遇到了給定的結點,那么就輸出並返回,這個過程比較好理解。關鍵是下面的彈棧的過程,如果在向左找的過程中遇到了null,說明當前棧頂元素的左子樹為null。那么我們向棧頂元素的右子樹開始查找。令p為棧頂元素,如果棧頂元素的右子樹為null,那么彈出棧頂元素,並用pre來保存剛彈出的元素,之所以設置pre,是因為如果當前棧頂元素的右子樹不為null的時候,不能輕易彈出,首先得去右子樹上去查找,如果右孩子被彈出了,說明右子樹上肯定沒有,那么當前結點才可以彈出。
public static void searchNode(TreeNode root,TreeNode node) { if(root == null || node == null) return; Stack<TreeNode> s = new Stack<>(); TreeNode p = root; TreeNode pre = null; //上一次出棧的結點 while(p != null || !s.isEmpty()) { while(p != null) { //這個while循環的思想還是一直往左找,找的過程結點入棧,如果找到了就打印輸出並返回。 s.push(p); if(p.val == node.val) { for (TreeNode treeNode : s) { System.out.print(treeNode.val + " "); } return; } p = p.left; } //走到這一步說明棧頂元素的左子樹為null,那么就開始往棧頂元素的右子樹上去找。 if(!s.isEmpty()) { p = s.peek(); //如果棧頂元素的右子樹為null,或者右子樹被遍歷過,則彈棧。 while(p.right == null || pre != null && p.right == pre) { pre = s.pop(); p = s.peek(); } //繼續遍歷p的右子樹 p = p.right; } } }