标签:
二叉树中的三种遍历方式,是我们最为熟知的,通过先序遍历+中序遍历或者是中序遍历+后序遍历都可以唯一确定一棵二叉树;但是注意,先序遍历+后序遍历不能确定一棵二叉树,但是如果一棵二叉树中只有度为0和度为2的节点,那么这种遍历方式也是可以确定一棵确定的二叉树的。
先序+中序–>构造二叉树 |
下面我们分别来看一下,根据先序+中序遍历的顺序,如何恢复一棵二叉树,代码如下:
//首先采用递归的方式
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTree(preorder, 0, inorder, inorder.length - 1, 0);
}
private TreeNode buildTree(int[] preorder, int idx, int[] inorder, int end, int start) {
if (idx >= preorder.length || start > end) {
return null;
}
TreeNode root = new TreeNode(preorder[idx]);
int i;
//找出根节点,
for (i = end; i >= start; i--) {
if (preorder[idx] == inorder[i]) {
break;
}
}
//中序数组中,左子树集合[start, i-1]
root.left = buildTree(preorder, idx + 1, inorder, i - 1, start);
//中序数组中,右子树集合[i+1, end],注意,先序队列的index,idx为根节点的位置,那么右子树只能位于idx的右边,直到end,但是先序的index的值为idx+i-start+1,因为在上面中序序列分割的左子树有(i-start)个元素,再加上根节点,所以下面右子树在先序遍历序列中的起点位置就是idx+i-start+1 (注意这个部分).
root.right = buildTree(preorder, idx + i - start + 1, inorder, end, i+1);
return root;
}
根节点就是先序遍历的首节点,找出中序列表中节点对应位置,将中序列表一分为二,再根据中序列表左右子树的划分关系来划分先序列表的左右子树的关系,这样就走下去了。
//非递归的方式
public TreeNode buildTree(int[] preorder, int[] inorder) {
int len = preorder.length;//长度相同
if(preorder==null||len == 0){
return null;
}
TreeNode[] stack = new TreeNode[len];
boolean[] flag = new boolean[len];
int k=-1, unSee=-1;
for(int i = 0, j = 0 ; i < len||j<len ;){
if(k<0){
k++;
stack[k] = new TreeNode(preorder[i]);
flag[k] = false;
unSee++;
i++;
}else{
if(unSee>=0&&inorder[j] == stack[unSee].val){
for(int x = unSee; x < k; x++){
if(x==unSee){
stack[x].left = stack[x+1];
}else{
stack[x].right = stack[x+1];
}
}
k = unSee;
flag[unSee] = true;
j++;
while(unSee>=0&&flag[unSee]==true){
unSee--;
}
}else{
k++;
stack[k] = new TreeNode(preorder[i]);
flag[k] = false;
unSee = k;
i++;
}
}
}
for(int i = 0; i<k; i++){
stack[i].right = stack[i+1];
}
return stack[0];
}
中序+后序–>构造二叉树 |
递归的方式:
Map<Integer,Integer> map = new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(inorder == null || inorder.length == 0) return null;
for(int i = 0; i< inorder.length;i++){
map.put(inorder[i],i);
}
return dfs(inorder,0,inorder.length,postorder,0,postorder.length-1);
}
private TreeNode dfs(int[] inorder,int istart,int iend,int[] postorder,int pstart,int pend){
if(pstart > pend) return null;
TreeNode root = new TreeNode(postorder[pend]);
int imid = map.get(root.val);//后序遍历的尾节点就是根节点
root.left = dfs(inorder,istart,imid-1,postorder,pstart,pstart + imid - istart-1);//利用中序遍历列表,找出二叉树中左右子树的分割点,判定出左右子树的长度,然后利用这个长度来对后序遍历列表进行分割(后序的整体结构就是左---->右---->中),一次进行,就可以搞到最后了
root.right = dfs(inorder,imid+1,iend,postorder,pstart + imid - istart,pend - 1);
return root;
}
这段程序中,与上面先序+中序不同的是,采用的hashMap的方式来获取中序列表中的对应值的索引,两种方式都比较好理解;具体的递归思想,跟上面类似,不在赘述。
非递归方式
public TreeNode buildTree(int[] inorder, int[] postorder) {
int i=0;
int j=0;
TreeNode root=null;
while (i<inorder.length && j<postorder.length) {
if (inorder[i]==postorder[j]) {
TreeNode node = new TreeNode(inorder[i]);
node.left=root;
root = node;
i++;
j++;
} else {
TreeNode node = new TreeNode(inorder[i]);
node.left=root;
root = node;
int k=j;
while (postorder[k]!=inorder[i]) {
k++;
}
int[] ia = Arrays.copyOfRange(inorder, i+1, i+1+k-j);
int[] pa = Arrays.copyOfRange(postorder, j, k);
node = buildTree(ia, pa);
root.right=node;
i=k+1;
j=k+1;
}
}
return root;
}
关于递归 |
在二叉树的操作中,递归的使用很是普遍,递归代码很是简洁,也比较容易理解,但是递归这种思想最系统堆栈要求是很高的,当数据量大一点之后,可能很容易就出现堆栈溢出,所以将递归程序改写成为非递归程序是很有必要的,非递归程序虽然比较复杂,代码量较大,但是具有普适性。
标签:
原文地址:http://blog.csdn.net/wangxiaotongfan/article/details/51496803