结构:
除了根节点之外每个节点只有一个父节点,根节点没有父节点。
除了叶节点之外所有节点都有一个或多个子节点,叶子节点没有子节点。
父节点和子节点之间用指针链接。
由于树的操作会涉及大量的指针,因此面试题都不容易。
树的遍历方式:
前序遍历:根-左-右
中序遍历:左-根-右
后序遍历:左-右-根
层序遍历:根-叶
二叉树的特例:
二叉搜索树,左子节点总是小于或等于根节点,右子节点总是大于或等于根节点
可以平均在O(logn)的时间内根据数值在二叉搜索树中找到一个节点
另外两个特例是堆和红黑树
堆分为最大堆和最小堆,最大堆中根节点的值最大,最小堆中根结点的值最小
很多需要快速找到最大值和最小值的问题可以用堆来解决
红黑树是把树中的节点定义为红黑两种颜色,通过规则确保从根节点到叶节点的最长路径的长度不超过最短路径的两倍
面试题7.重建二叉树
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字
二叉树的结构
class biTree{
int value;
biTree left;
biTree right;
biTree(int x){value=x;}
}
重建过程,是一个递归的过程
例如输入前序遍历{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6}
可见,1左边是{2,4,7},右边是{3,5,6,8}
观察原始中序遍历数组,发现正好是1分割左右,因此左边的中序数组额右边的中序数组就可以得到了。
而观察先序数组,先序数组的结构正好是{根节点,左子树中序,右子树中序}这个顺序,因此就可以通过这个规律得到先序遍历的左子树和右子树。
最后递归来一步一步完成二叉树左子树和右子树的重建
public static biTree reConstruct(int[] preOrder, int[] inOrder){
int length=preOrder.length;//数组元素个数
biTree root=new biTree(preOrder[0]);//根节点初始化为先序数组第一个元素
if(preOrder.length==1){//如果先序遍历长度为1,无子节点
root.left=null;
root.right=null;
return root;
}
//找到中序数组根节点值位置
int mark=-1;
for(int i=0;i<length;i++){
if(preOrder[0]==inOrder[i]){
mark=i;
break;
}
}
if(mark>0){//如果数组中找到根节点
//左子树的先序数组
int[] leftPreOrder=new int[mark];
//左子树的中序数组
int[] leftInOrder=new int[mark];
//记录左子树数组
for(int j=0;j<mark;j++){
leftPreOrder[j]=preOrder[j+1];
}
//记录右子树数组
for(int j=0;j<mark;j++){
leftInOrder[j]=inOrder[j];
}
//左子树递归
root.left=reConstruct(leftPreOrder,leftInOrder);
}
else{//如果数组中根节点位置在0,或者没找到根节点
root.left=null;//左子树没有
}
if(preOrder.length-1-mark>0){//右子树的长度:总长度-1-根节点位置
//右子树的先序数组
int[] rightPreOrder=new int[preOrder.length-1-mark];
//右子树的中序数组
int[] rightInOrder=new int[preOrder.length-1-mark];
for(int j=mark+1;j<length;j++){
rightInOrder[j-1-mark]=inOrder[j];
rightPreOrder[j-1-mark]=preOrder[j];
}
root.right=reConstruct(rightPreOrder,rightInOrder);
}
else{
root.right=null;
}
return root;
}
public static void main(String[] args){
int[] preOrder={1,2,4,7,3,5,6,8};
int[] inOrder={4,7,2,1,5,3,8,6};
reConstruct(preOrder,inOrder);
}
面试题8.二叉树的下一个节点
题目:给定一个二叉树和其中一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左右子节点的指针,还有一个指向父节点的指针
分情况讨论:
①如果一个节点既有左子树又有右子树:
下一个节点就是它的右子树中的最左子节点
②如果一个节点有左子树没有右子树:
如果该节点无父节点:那么没有下一个节点
如果该节点有父节点且是父节点的左子节点:那么下一个节点是父节点
如果该节点有父节点且是父节点的右子节点:那么下一个节点需要沿着父节点的指针向上遍历,直到找到一个新节点,原节点存在于这个新节点的左子树中
③如果一个节点没有左子树有右子树:
下一个节点就是它的右子树中的最左子节点
④如果一个节点既没有左子树也没有右子树
如果该节点无父节点:那么没有下一个节点
如果该节点是父节点的左节点:那么下一个节点是父节点
如果该节点是父节点的右节点:那么下一个节点需要沿着父节点的指针向上遍历,直到找到一个新节点,原节点存在于这个新节点的左子树中
综合几种情况发现:
只存在两种情况,即存在右子树和不存在右子树
①如果一个节点有右子树:下一个节点就是它的右子树中的最左子节点
②如果一个节点没有右子树:
如果该节点无父节点:那么没有下一个节点
是父节点的左子节点:那么下一个节点是父节点
是父节点的右子节点:那么下一个节点需要沿着父节点的指针向上遍历,直到找到一个新节点,原节点存在于这个新节点的左子树中
public class nextNode {
BinaryTreeNode getNext(BinaryTreeNode node){
BinaryTreeNode resNode = null;
if(node==null){
return null;
}
if(node.right!=null){//如果一个节点有右子树,下一个节点就是它的右子树中的最左子节点
resNode=node.right;
while(resNode.left!=null){
resNode=resNode.left;
}
return resNode;
}
else if(node.right==null){//如果一个节点没有右子树
if(node.parent==null){//如果该节点无父节点:那么没有下一个节点
return null;
}
else{
//是父节点的左子节点
if(node.parent.left==node){
//那么下一个节点是父节点
resNode=node.parent;
}
//是父节点的右子节点
else if(node.parent.right==node){
//那么下一个节点需要沿着父节点的指针向上遍历,直到找到一个新节点,原节点存在于这个新节点的左子树中
while(node.parent.right==node){
node=node.parent;
}
resNode=node.parent;
}
}
}
return resNode;
}
}