二叉樹遞迴練習
(1)
題目:
請把紙條豎著放在桌面上,然後從紙條的下邊向上⽅對摺,壓出摺痕後再展開。此時有1條摺痕,突起的⽅向指向紙條
的背面,這條摺痕叫做“下”摺痕 ;突起的面向指向紙條正⾯的摺痕叫做“上”摺痕。如果每次都從下邊向上面對
折,對摺N次。請從上到下計算出所有摺痕的面向。
給定折的次數n,請返回從上到下的摺痕的陣列,若為下摺痕則對應元素為"down",若為上摺痕則為"up".
思路:找張紙試幾次就會發現,這其實是一棵滿二叉樹,樹的根是“下”,左孩子是“上”,右孩子是“下”。遍歷
順序是“右,中,左”
public class FoldPaper {
public String[] foldPaper(int n) {
// write code here
List<String> list=new ArrayList();
fold(1,n,true,list);
String[] res=new String[list.size()];
for(int i=0;i<list.size();i++){
res[i]=list.get(i);
}
return res;
}
public void fold(int i,int n,boolean down,List<String> list){//i--當前層數 遍歷順序:右,中,左,(左上右下的樹)
if(i>n) return ;
fold(i+1,n,true,list);
list.add(down?"down":"up");//加入down
fold(i+1,n,false,list);
}
}
(2)
一棵二叉樹原本是搜尋二叉樹,但是其中有兩個節點調換了位置,使得這棵二叉樹不再是搜尋二叉樹,請找到這兩個
錯誤節點並返回他們的值。保證二叉樹中結點的值各不相同。
給定一棵樹的根結點,請返回兩個調換了位置的值,其中小的值在前。
思路:
中序遍歷搜尋二叉樹的結果是升序的,比如1 2 3 4 5 6
兩個節點位置交換髮生錯誤,有兩種情況 1 5 3 4 2 6,有兩次降序的分別為5 3和2 6,錯誤節點是第一次降序的
max,和第二次降序的min;第二種情況1 2 4 3 5 6 ,有一次降序的即4 3,第一個錯誤是max=4,第二個是min=3.
綜合來看,就是找出中序遍歷結果中,第一次降序的max,和最後一次降序的min。
public class FindErrorNode {
public int[] findError(TreeNode root) {
// write code here
if(root==null) return null;
Stack<TreeNode> s=new Stack();
int[] result=new int[2];
result[0]=result[1]=-1;
TreeNode p=root;
TreeNode top=null;
boolean first=true;
while(!s.isEmpty()||p!=null){
if(p!=null){
s.push(p);
p=p.left;
}else{
p=s.pop();
if(top!=null&&top.val>p.val){//p為當前節點,top上一次為彈出的棧頂
result[1]=(result[1]==-1?top.val:result[1]);//結果輸出為最小值在前面
result[0]=p.val;
}
top=p;
p=p.right;
}
}
return result;
}
}
(3)
題目:
從二叉樹的節點A出發,可以向上或者向下走,但沿途的節點只能經過一次,當到達節點B時,路徑上的節點數叫作A到B的距離。對於給定的一棵二叉樹,求整棵樹上節點間的最大距離。給定一個二叉樹的頭結點root,請返回最大距離。保證點數大於等於2小於等於500.
思路:
理解題目的含義,對於一棵以root為根的二叉樹,樹上的最大距離可能來自3中情況:
情況1:完全來自root的左子樹,如圖所示,即最大路徑不經過root結點,只在結點1的左子樹2上面,此時的最大距離為8。
情況2:完全來自root結點的右子樹,如圖所示,最大路徑不經過root結點,只在結點1的右側左子樹3上面,此時最大距離是9。
情況3:來自結點root的兩側,如圖所示,經過root結點,此時的最大距離=root的左子樹的高度(即結點3的最長路徑)+root右子樹的高度(即結點3的最長路徑)+root結點本身。
分析可知,要計算結點root所在子樹的最長距離,需要已知:左子樹②的最長距離LMaxLength,左子樹的高度LHeight;右子樹③的最長距離RMaxLength,右子樹的高度RHeight.然後max(LMax,RMax,(LHeight+RHeight+1)),其最大值就是這棵二叉樹的最大距離,即對於每個子樹,需要求出它的最大距離和最大高度。
顯然這就是一個遞推關係式,可以使用遞迴來實現,即設計一個遞迴函式,給定一個根結點root,求出最大距離max和最大高度height並返回這2個數值。其中maxLength= Math.max(LMax,RMax,(LHeight+RHeight+1));而由於要返回這棵子樹的高度,如何求子樹的高度呢?二叉樹的高度就是它的2個子樹高度中的較大值再加上1,即height=Math.max(LHeight, RHeight)+1;
遞迴的遞推關係有了,關鍵是找到遞迴的起始條件或者理解為終止條件。這裡使用的思想是後序遍歷(先遍歷左右子樹再處理結論),聯絡二叉樹的後序遍歷演算法,進行改編。顯然if(root==null)時:max=0;height=0;
總結:設計一個遞迴函式,輸入根結點root,求出這棵二叉樹的最大距離maxLength和高度length並返回。遞推關係為:
maxLength= Math.max(LMax,RMax,(LHeight+RHeight+1));
height=Math.max(LHeight, RHeight)+1
邊界條件為:
if(root==null) return max=0;height=0;
在Java中不能分開返回2個值,因此要將2個值整合成為一個陣列進行返回即可
public class LongestDistance {
public int findLongest(TreeNode root) {
// write code here
int[] lenAndHei=get(root);
return lenAndHei[0];
}
public int[] get(TreeNode root){//遞迴求根為root的最大距離和高度
int[] lenAndHei=new int[2];
if(root==null){
lenAndHei[0]=lenAndHei[1]=0;
return lenAndHei;
}
//左子樹的最大距離和高度
int[] left=get(root.left);
int leftLen=left[0];
int leftHei=left[1];
//右子樹的最大距離和高度
int[] right=get(root.right);
int rightLen=right[0];
int rightHei=right[1];
//當前樹的最大距離和高度
lenAndHei[0]=Math.max((Math.max(leftLen,rightLen)),(leftHei+rightHei+1));
lenAndHei[1]=Math.max(leftHei,rightHei)+1;
return lenAndHei;
}
}
題目:
有一棵二叉樹,其中所有節點的值都不一樣,找到含有節點最多的搜尋二叉子樹,並返回這棵子樹的頭節點.給定二叉樹的頭結點root,請返回所求的頭結點,若出現多個節點最多的子樹,返回頭結點權值最大的。
思路:
題目(3)中每次遞迴呼叫時需要得到2個資訊(該子樹的最大長度,該子樹的高度)並將這2個資訊組成陣列進行返回,因此每次遞迴呼叫時先接收上次呼叫返回的陣列,然後進行處理,處理完的結果放入到一個新的陣列中再返回這個陣列,注意,(3)中需要收集返回的2個資訊都是int型別的,因此可以形成int[]陣列進行返回;
本題中由於業務邏輯更加複雜,每一次遞迴呼叫時需要利用的資訊有4個,同時遞迴方法執行結束後返回的資訊也有4個(
當前子樹的最大二叉搜尋子樹的根結點TreeNode,
最大搜尋子樹的結點數目int,
當前子樹的最小值int,
當前子樹的最大值int)
由於這4個資訊不是同一型別的,因此不能組成一個陣列進行返回,所以對於TreeNode的資訊,依然可以直接返回,即令該遞迴函式的返回值是TreeNode,對於其他的3個int型別的引數
TreeNodelnode=postOrder(root.left,res);
intlmax=res[0];
intlmin=res[1];
intlnum=res[2];
TreeNodernode= postOrder(root.right,res);
intrmax=res[0];
intrmin=res[1];
intrnum=res[2];
例如,上面的res陣列可以是臨時建立的,也可以是使用的成員變數陣列,反正認為在TreeNode lnode=postOrder(root.left,res);呼叫結束後res就已經被賦值了,於是就可以取出res中的值進行使用了。Res陣列可以重複使用,因為res傳入給遞迴函式只是作為一個容器來讓方法填充資料的。
本題的業務邏輯:
要求以root為根的二叉樹中的最大搜尋二叉子樹的根結點,以root,root的左結點,root的右結點這個基礎結構來分析怎麼求二叉子樹的最大搜尋二叉樹。
顯然以root為根的二叉樹的最大搜尋子樹只可能來自2中情況:
情況1:當root左子樹root.left上的最大搜尋子樹就是left結點,並且root右子樹上root.right上的最大搜尋子樹就是right結點時,並且left子樹上的最大值<root.val<right子樹上的最小值,此時root結點就是這棵樹的最大搜尋子樹的根結點,返回root。
此時注意一種特殊情況,如果root的某一個子節點不存在,例如當左結點不存在時,如果上面的條件依舊滿足,即right就是最大搜尋子樹,且root.val<right,那麼此時的最大搜尋子樹就是right子樹加上結點root;同理當right不存在,且left子樹就是左結點的最大搜尋子樹並且left<root.val,那麼此時的最大搜尋子樹就是left子樹加上root結點,這種情況應該作為left,right都存在的特殊情況一起考慮進去,直接返回root結點,因此採取的巧妙的策略就是當root==null時,令該子樹的結點數目為0,最小值為Integer.MAX_VALUE,於是顯然大於root結點;令最小值為Integer.MIN_VALUE,於是顯然小於root結點,並且返回null最為最大搜尋子樹的根結點。此時必然滿足情況1的判斷條件,於是會返回root作為搜尋子樹的根結點。
情況2:除此之外的情況都說明最大搜尋子樹只可能只出現在一邊在root的左子樹left或者右子樹right上而不可能跨越root的兩邊組成一棵最大搜尋樹。並且已知左子樹left上面搜尋子樹的根結點root1,其結點數目為n1;右子樹right上面搜尋子樹的結點為right,其結點數為n2;因此比較n1與n2的大小即可,取較大者對應的根結點進行返回即可。
public class MaxSubtree {
public TreeNode getMax(TreeNode root) {
// write code here
if(root==null) return null;
int[] res=new int[3];
TreeNode searchNode=get(root,res);
return searchNode;
}
public TreeNode get(TreeNode root,int[] res){//res[0]節點數目,res[1]root為根的樹的最小值,res[2]root為根的樹的最大值
if(root==null){
res[0]=0;
res[1]=Integer.MAX_VALUE;
res[2]=Integer.MIN_VALUE;
return null;
}
//左子樹
int[] left=new int[3];
TreeNode leftNode=get(root.left,left);//遞迴左右子樹
//右子樹
int[] right=new int[3];
TreeNode rightNode=get(root.right,right);
//判斷兩種情況下的最大搜尋樹
//情況一
if(leftNode==root.left&&rightNode==root.right&&left[2]<root.val&&root.val<right[1]){
//leftNode rightNode分別為root的左右孩子,且左孩子的最大值<root.val<右孩子的最小值(無重複值),此時root就是最大搜尋樹
res[0]=left[0]+right[0]+1;
//注意這裡不能這樣寫,因為要考慮左右子樹有一側為null的情況。
/*
res[1]=left[1];//最小值=左孩子的最小值
res[2]=right[2];//最大值=右孩子的最大值
*/
res[1]=Math.min(left[1],root.val);//左子樹可能為空
res[2]=Math.max(right[2],root.val);//右子樹可能為空
return root;
}
//情況二:節點多的子樹為root為根的最大搜尋子樹,最大搜尋子樹來自一邊
res[0]=Math.max(left[0],right[0]);
res[1]=Math.min(Math.min(left[1],right[1]),root.val);//root為根的子樹的最小值,不是最大搜尋樹的最小值
res[2]=Math.max(Math.max(left[2],right[2]),root.val);//樹root的最大值
return left[0]>right[0]?leftNode:rightNode;
}
}
相關文章
- 遍歷二叉樹-------遞迴&非遞迴二叉樹遞迴
- 二叉樹的遞迴套路二叉樹遞迴
- 【C++】翻轉二叉樹(遞迴、非遞迴)C++二叉樹遞迴
- 二叉樹非遞迴遍歷二叉樹遞迴
- 二叉樹的四種遍歷(遞迴與非遞迴)二叉樹遞迴
- 【資料結構】二叉樹遍歷(遞迴+非遞迴)資料結構二叉樹遞迴
- 什麼是遍歷二叉樹,JavaScript實現二叉樹的遍歷(遞迴,非遞迴)二叉樹JavaScript遞迴
- 樹(2)--二叉樹的遍歷(非遞迴)+線索二叉樹二叉樹遞迴
- 二叉平衡樹 python 列表 遞迴Python遞迴
- 樹3-二叉樹非遞迴遍歷(棧)二叉樹遞迴
- 遍歷二叉樹的遞迴與非遞迴程式碼實現二叉樹遞迴
- 二叉樹建立及遍歷演算法(遞迴及非遞迴)二叉樹演算法遞迴
- 非遞迴先序遍歷二叉樹遞迴二叉樹
- 【刷題】二叉樹非遞迴遍歷二叉樹遞迴
- 二叉樹 遞迴 洛谷P1364二叉樹遞迴
- 二叉樹建立後,如何使用遞迴和棧遍歷二叉樹?二叉樹遞迴
- 二叉樹的前中後序遍歷(遞迴和非遞迴版本)二叉樹遞迴
- 二叉樹——後序遍歷的遞迴與非遞迴演算法二叉樹遞迴演算法
- [java] 二叉樹的後序遍歷(遞迴與非遞迴實現)Java二叉樹遞迴
- 遍歷二叉樹的迭代和遞迴方法二叉樹遞迴
- 二叉樹的非遞迴遍歷寫法二叉樹遞迴
- 資料結構之二叉樹遞迴操作資料結構二叉樹遞迴
- Day14 | 二叉樹遞迴遍歷二叉樹遞迴
- java常見遞迴練習題Java遞迴
- python實現二叉樹及其七種遍歷方式(遞迴+非遞迴)Python二叉樹遞迴
- 【練習】二叉樹的實現二叉樹
- 【練習】二叉樹的遍歷二叉樹
- 二叉樹的建立與遍歷(遞迴實現)二叉樹遞迴
- 二叉樹的所有遍歷非遞迴實現二叉樹遞迴
- 二叉樹的非遞迴遍歷——java實現二叉樹遞迴Java
- 遞迴求解二叉樹任意一結點的深度遞迴二叉樹
- 遞迴與分治演算法練習遞迴演算法
- Day14 二叉樹Part2 遞迴的應用(二叉樹相關)二叉樹遞迴
- Leetcode 題解系列 -- 對稱二叉樹(遞迴)LeetCode二叉樹遞迴
- C語言 遞迴實現二叉排序樹的插入C語言遞迴排序
- 【資料結構】——搜尋二叉樹的插入,查詢和刪除(遞迴&非遞迴)資料結構二叉樹遞迴
- 每天刷個演算法題20160521:二叉樹高度(遞迴與非遞迴)演算法二叉樹遞迴
- 二叉樹的遍歷 → 不用遞迴,還能遍歷嗎二叉樹遞迴