摘要:例如輸入前序遍歷序列和中序遍歷序列,則重建二叉樹并返回。代表前序數(shù)組,代表中序數(shù)組。中序遍歷的左右子樹比較好找,但是前序遍歷的左右子樹想到比較難找。
jdk 版本: jdk 1.8
題目:
輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建出該二叉樹。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不含重復(fù)的數(shù)字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹并返回。
解題思路:
對于樹的操作來說,不管是遍歷還是建樹,首先想到的應(yīng)該是用遞歸算法來解。
第一次嘗試的思路大概是這樣的:
前序數(shù)組的第1個元素即是樹的根節(jié)點。關(guān)鍵在于怎么確定樹的左右子樹。
例如:
在此聲明幾個變量。pre:代表前序數(shù)組,in:代表中序數(shù)組。
假設(shè)當(dāng)前遍歷到pre的第2個元素,即根節(jié)點的下一個元素。在in中判斷第2個元素跟當(dāng)前節(jié)點(即根節(jié)點)的大小。
如果在in中第2個元素的位置小于當(dāng)前節(jié)點的位置,那么可以判定,當(dāng)前此節(jié)點是當(dāng)前節(jié)點的左子樹,當(dāng)然,前提是當(dāng)前節(jié)點左子樹為空。
否則,可能是當(dāng)前節(jié)點的右子樹。(注意是可能。)
關(guān)鍵: 如何判斷是不是當(dāng)前節(jié)點的右子樹?
即當(dāng)前節(jié)點在in中的位置的下一個元素是不是被訪問過,如果沒有被訪問過,即是右子樹,否則不是,應(yīng)當(dāng)返回。
下面貼出我第一次嘗試的代碼。(可以過)
public class Solution { int currentPosition = 1; boolean [] state; public TreeNode reConstructBinaryTree(int [] pre, int [] in) { TreeNode root = initTreeNode(pre[0]); //創(chuàng)建根節(jié)點 TreeNode currentNode = root; state = new boolean [pre.length]; for(int i = 0 ; i < pre.length; i++) { if(in[i] == pre[0]) { state[i] = true; }else { state[i] = false; } } digui(currentNode,pre,in,state); return root; } public void digui(TreeNode currentNode,int [] pre,int [] in,boolean [] state) { if(currentPosition >= pre.length) { // 遞歸結(jié)束條件 return ; } int parentVal = currentNode.val; int nextVal = pre[currentPosition]; if(findPosition(parentVal,nextVal,in)) { //左邊 TreeNode node = initTreeNode(nextVal); if(currentNode.left == null) { currentNode.left = node; mark(in,pre[currentPosition],state); currentPosition ++; } digui(node, pre, in, state); } //右邊 boolean temp = isRightVisit(parentVal, in, state); // 當(dāng)前節(jié)點在中序遍歷的下一個節(jié)點(右邊第1個)是不是已經(jīng)訪問過了?,是就返回,不是就直接插入當(dāng)前節(jié)點。 if(temp) { return ; } TreeNode node = initTreeNode(pre[currentPosition]); currentNode.right = node; mark(in,pre[currentPosition],state); currentPosition ++; digui(node, pre, in, state); } // 判斷在中序遍歷中當(dāng)前節(jié)點的下一節(jié)點是否訪問過 private boolean isRightVisit(int parentVal, int[] in,boolean [] state){ int temp = 0; for(int i = 0; i < in.length; i++) { if(in[i] == parentVal) { temp = i + 1; break; } } return state[temp == in.length? temp -1 : temp]; } //標(biāo)記以訪問 private void mark(int [] in, int markVal,boolean [] state) { for(int i = 0; i < in.length; i++) { if(in[i] == markVal) { state[i] = true; } } } //在左邊返回ture,在右邊返回false private boolean findPosition(int parent, int son,int [] in) { int pTemp = 0; int sTemp = 0; for(int i = 0; i < in.length; i++) { if(in[i] == parent) { pTemp = i; }else if(in[i] == son) { sTemp = i; } } return sTemp < pTemp; } //初始化TreeNode private TreeNode initTreeNode(int val) { TreeNode node = new TreeNode(val); node.left = null; node.right = null; return node; } }
看了上面代碼,又臭又長。 一般對于遞歸算法,是沒有必要寫這么長的代碼。重新思考一下,
整理出第2次解題思路,還是用的遞歸。
思路:
遍歷前序數(shù)組,從中序數(shù)組中,找到當(dāng)前遍歷的元素,那么左邊的肯定是當(dāng)前節(jié)點的左子樹,右邊的肯定是當(dāng)前節(jié)點的右子樹。那么直接遞歸即可。
中序遍歷的左右子樹比較好找,但是前序遍歷的左右子樹想到比較難找。
解釋一下,遞歸左子樹中的 startPre + i - startIn 。
(i - startIn ),是通過中序遍歷找到左子樹的偏移量(因為中序遍歷中,在當(dāng)前節(jié)點的左邊的,那就是當(dāng)前節(jié)點的左子樹),再加上startPre,即找到在前序遍歷的左子樹的最后一個節(jié)點。
上面理解了,那么理解 startPre + i - startIn + 1。就簡單多了。
在前序遍歷中,當(dāng)前節(jié)點左子樹的最后一個節(jié)點的下一個節(jié)點肯定是右子樹的起始節(jié)點。
public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { TreeNode root=reConstructTree(pre,0,pre.length-1,in,0,in.length-1); return root; } private TreeNode reConstructTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) { //遞歸結(jié)束條件 if(startPre>endPre||startIn>endIn) return null; TreeNode root=new TreeNode(pre[startPre]); for(int i=startIn;i<=endIn;i++) if(in[i]==pre[startPre]){ //左子樹 root.left=reConstructTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1); //右子樹 root.right=reConstructTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn); } return root; } }
總結(jié): 第一次寫出那么長的代碼,其實是對遞歸的思想不大懂,第2次重新整理后,代碼明顯容易讀多了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70654.html
摘要:第一行為前序遍歷,第二行為中序遍歷。例如輸入前序遍歷序列和中序遍歷序列,則重建二叉樹并返回。根據(jù)前序遍歷和中序遍歷的結(jié)果可以拿到左子中序遍歷和右側(cè)為右子樹中序遍歷左子樹的前序遍歷和右子樹的前序遍歷然后遞歸左子樹和右子樹的完成重建。 二叉樹簡介 基本結(jié)構(gòu): function TreeNode(x) { this.val = x; this.left = null; ...
摘要:例如,輸入前序遍歷序列和中序遍歷序列,則重建二叉樹并返回。題解對二叉樹前序中序遍歷的考察,采用遞歸的方法解決問題,難點是確定每一個子樹的臨界點。 題目 輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建該二叉樹。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果都不含重復(fù)的數(shù)字。例如,輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹并返回。 ...
摘要:例如輸入前序遍歷序列和中序遍歷序列,則重建二叉樹并返回。操作給定的二叉樹,將其變換為源二叉樹的鏡像。劍指中還有一道類似的變種題目,就是下面的這道,之字形遍歷二叉樹。最后下面的兩道題目分別運用了二叉樹先序中序遍歷算法。 開篇 以下內(nèi)容可能偏應(yīng)試但很好理解,所以大家一定要堅持看下去,因為我們變強(qiáng)的過程注定孤獨的,堅持下來就會看到明天的太陽。 回顧 showImg(https://user-...
摘要:題目輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建該二叉樹。例如,給出前序遍歷中序遍歷返回如下的二叉樹限制節(jié)點個數(shù)答案要使用這個方法,首先要將一個原始的數(shù)組,從下標(biāo)開始復(fù)制,復(fù)制到上標(biāo)不包括,生成一個新的數(shù)組。 題目輸入某二叉樹的前序遍歷和中序遍歷的結(jié)果,請重建該二叉樹。假設(shè)輸入的前序遍歷和中序遍歷的結(jié)果中都不...
摘要:實現(xiàn)基本數(shù)據(jù)結(jié)構(gòu)棧,隊列,鏈表這篇筆記側(cè)重點二叉樹的三種遍歷前中后迭代非迭代代碼重建二叉樹的代碼與分析和關(guān)于二叉樹的題簡單理解二叉查找樹紅黑樹,的性質(zhì),實際用途。同時,以對應(yīng)的節(jié)點為邊界,就會把中序遍歷的結(jié)果分為左子樹和右子樹。 雖是讀書筆記,但是如轉(zhuǎn)載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 以下是算法導(dǎo)論第十...
閱讀 3653·2021-11-23 09:51
閱讀 1995·2021-11-16 11:42
閱讀 3245·2021-11-08 13:20
閱讀 1099·2019-08-30 15:55
閱讀 2210·2019-08-30 10:59
閱讀 1245·2019-08-29 14:04
閱讀 1028·2019-08-29 12:41
閱讀 2029·2019-08-26 12:22