二叉树-1
一、二叉树的概念与基本性质
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集,或者由一个根节点和两棵互不相交的、分别称为左子树和右子树的二叉树组成(递归的好苗子)。介绍一下常用的三种特殊二叉树:
斜树: 所有结点只有左子树或所有结点只有右子树,如图所示
满二叉树: 所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,如图所示
完全二叉树: 对一棵具有n各结点的二叉树按层序遍历编号,如果编号为i(i>=1&&i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同,则这棵二叉树称为完全二叉树(其实很好理解,就是结点按顺序排列)。我们可以得到如下性质1)叶子结点只会出现在最后两层。2)度为1的结点只能有一个,并且该结点只有左孩子。继续看图
二叉树的几个性质(不证明,有兴趣可以自行查找):
1)二叉树的第i层上最多有2i-1 个结点(i>=1)。
2)在一棵深度为k的二叉树中,最多有2k-1个结点(满二叉树情况下),最少有k个结点(斜树)。
3)在一棵二叉树中,如果叶子结点的个数为n0 ,度为2的结点个数为n2 ,则n0 =n2 +1(这个性质比较重要)。
这个性质证明一下:假设n为二叉树的结点总数,n1 为二叉树中度为1的结点个数,我们可以得到
n=n0 +n1 +n2
考虑二叉树中的分支数,因为只有顶点没有入度(也就是除了顶点外,每个结点都有一条连向它的边,注意是连向它的,不是它连出去的边,一个结点连出去的边有0条,1条,2条三种可能情况),所以总的分支数为n-1,同时n1 的结点连着一个结点,n2 的结点连着两个结点所以总分支数也可以写成n-1=n1 +2*n2 ,将上面的式子的代入,移项可得n0 =n2 +1。
4)具有n个结点的完全二叉树 的深度为floor(log2 n)+1;
5)对一棵具有n个结点的完全二叉树中的结点从1开始按层序编号,则对于任意的编号为i(i>=1&&i<=n)的结点,有:1.如果i>1,则结点i的双亲的编号为floor(i/2)。2.如果某编号为i的结点有左孩子的话,则它左孩子的编号为2*i;,同理,如果有右孩子的话,右孩子的编号为2i+1(作用在于当你用数组来实现完全二叉树的时候可以根据这个性质来确定子结点和父结点)。
二、二叉树的遍历
上面啰嗦地介绍了一些性质和概念,其实如果嫌烦可以不求甚解,有个印象就好。本节主要来介绍下对于一棵二叉树我们怎么去遍历(从根结点出发,按照某种次序访问二叉树的所有结点,使得每个结点被访问一次且仅被访问一次 )它。
前序遍历
顺序是先访问根节点–>左子树–>右子树,废话少说直接上代码
递归遍历
public static void re_preorder(Node root){
if(root==null){
return;
}
System.out.println(root.val);
re_preorder(root.left);
re_preorder(root.right);
}
辅助栈遍历
public static void preorder2(Node root){
Stack<Node> s=new Stack<Node>();
if(root!=null) {
s.push(root);
}
while(!s.empty()){
Node temp=s.pop();
System.out.println(temp.val);
if(temp.right!=null){
s.push(temp.right);
}
if(temp.left!=null){
s.push(temp.left);
}
}
}
Morris遍历
public static void m_preorder(Node root){
if(root==null){
return;
}
Node pnode=root;
while(pnode!=null){
if(pnode.left==null){
System.out.println(pnode.val);
pnode=pnode.right;
}else{
Node pre=pnode.left;
while(pre.right!=null&&pre.right!=pnode){
pre=pre.right;
}
if(pre.right==null){
pre.right=pnode;
System.out.println(pnode.val);
pnode=pnode.left;
}else{
pre.right=null;