基本概念
有序树:如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。在有序树中最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子。
森林: m棵互不相交的树的集合
二叉树:节点的一个有限集合,该集合或者为空,或者由一个根节点加上两棵左子树和右子树组成。二叉树是每个节点最多有两个子树的树结构。
根节点:一棵树最上面的节点称为根节点。
父节点、子节点:如果一个节点下面连接多个节点,那么该节点称为父节点,它下面的节点称为子节点。
叶子节点:没有任何子节点的节点称为叶子节点。
兄弟节点:具有相同父节点的节点互称为兄弟节点。
节点度:节点拥有的子树数。
节点的层数:根节点层数为1,其他节点的层数为双亲节点的层数+1
树的度: 一棵树中最大的结点度数
树的深度:从根节点开始(其深度为0)自顶向下逐层累加的。
树的高度:从叶子节点开始(其高度为0)自底向上逐层累加的。54的高度是2,根节点23的高度是3。
基本性质
- 在非空二叉树中,第i层的结点总数最大为2^i-1,i>=1(满树情况)
- 深度为h的二叉树最多有 2^h-1个结点(h>=1),最少有h个结点
- 对于任意一棵二叉树,设其总结点数为N,如果其叶结点(度为0的结点)数为N0,而度数为2的结点总数为N2,则N0=N2+1,度为1的结点数N1=N-N0-N2
- 具有n个结点的完全二叉树的深度为(logN)+1,底数为2
- 有N个结点的完全二叉树各结点如果用顺序方式存储,如果所有结点从1开始编号则结点之间有如下关系:
若i为结点编号则 如果i>1,则其父结点的编号为i/2
如果2×i<=N,则其左儿子(即左子树的根结点)的编号为2×i
若2×i>N,则无左儿子
如果2×i+1<=N,则其右儿子的结点编号为2×i+1
若2×i+1>N,则无右儿子 - 此外,如果所有结点从0开始编号,那么相应的i号节点的双亲节点的编号为(i-1)/2,左孩子的编号为2i+1,右孩子编号为2i+2。
- 给定N个结点,能构成h(N)种不同的二叉树。 h(N)为卡特兰数的第N项。h(n)=C(2*n,n)/(n+1)
- 设有i个分支点,I为所有分支点的道路长度总和,J为叶的道路长度总和J=I+2i
证明3:
因为二叉树中所有结点的度数均不大于2,不妨设n0表示度为0的结点个数,n1表示度为1的结点个数,n2表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到:n=n0+n1+n2 (1)由度之间的关系可得第二个等式:n=n0*0+n1*1+n2*2+1即n=n1+2n2+1 (2)将(1)(2)组合在一起可得到n0=n2+1
证明4:
深度为k的最多有2^k-1个结点,完全二叉树的前k-1层是深度为k-1的满二叉树,一共有2^(k-1)-1个结点。由于完全二叉树深度为k,故第k层上还有若干个结点,因此该完全二叉树的结点个数n满足 2^(k-1)-1<n≤2^k-1
由此可推出:2^(k-1)≤n<2k,取对数后有: k-1≤logn<k 又因k-1和k是相邻的两个整数,故有k=logn+1,2为底。
面试题:如果一个完全二叉树的结点总数为768个,求叶子结点的个数。
由二叉树的性质知:n0=n2+1,将之带入768=n0+n1+n2中得:768=n1+2n2+1,因为完全二叉树度为1的结点个数要么为0,要么为1,那么就把n1=0或者1都代入公式中,很容易发现n1=1才符合条件。所以算出来n2=383,所以叶子结点个数n0=n2+1=384。
总结规律:如果一棵完全二叉树的结点总数为n,那么叶子结点等于n/2(当n为偶数时)或者(n+1)/2(当n为奇数时)
实现二叉排序树
思路:所有左子树小于根节点值,所有右子树值大于根节点,采用中序遍历可以从小到大排序。
代码实现:
package JBTree;
class Node{
public int data;
public Node left;
public Node right;
public Node(int data){
this.data = data;
this.left = null;
this.right = null;
}
}
public class BinaryTree {
private Node root;
public BinaryTree(){
root = null;
}
//将data插入到二叉排序树
private void insert(int data){
Node newNode = new Node(data);//定义新结点
if (root==null) {
root = newNode;//根结点为null,在此处插入新结点
}else{
Node current = root;//根结点不为空,需要定义当前结点和父结点
Node parent; //父结点
while (true) {
parent = current; //父节点为当前结点
if (data<current.data) {//待插入的数比当前结点的数据小,往左走
current = current.left;
if (current==null) {//当前比较的结点是null,走到底了,该插入数据了,数据将插入到父结点的左子结点。
parent.left=newNode;//(其实就是将数据插入到当前位置,当前位置表示为parent.left)
return;
}
}
else {
current=current.right;//待插入的数比当前结点的数据小,往右走
if (current==null) {
parent.right = newNode;
return;
}
}
}
}
}
//将数值插入构建二叉树
public void buildeTree(int[] data){
for (int i = 0; i < data.length; i++) {
insert(data[i]);
}
}
//中序遍历,输出即有序数字
public void inOrder(Node localRoot){
if (localRoot!=null) {
inOrder(localRoot.left);
System.out.print(localRoot.data+" ");
inOrder(localRoot.right);
}
}
// 前序遍历
public void preOrder(Node localRoot) {
if (localRoot != null) {
System.out.print(localRoot.data + " ");
preOrder(localRoot.left);
preOrder(localRoot.right);
}
}
// 后序遍历
public void postOrder(Node localRoot) {
if (localRoot != null) {
postOrder(localRoot.left);
postOrder(localRoot.right);
System.out.print(localRoot.data + " ");
}
}
public static void main(String[] args){
BinaryTree binaryTree = new BinaryTree();
int[] data={2,8,7,4,9,3,1,6,7,5};
binaryTree.buildeTree(data);
System.out.print("二叉树的中序遍历:");
binaryTree.inOrder(binaryTree.root);
}
}
层序遍历二叉树
思路:先将根结点放入队列中,
然后每次都从队列中取出一个结点打印该结点的值
若这个结点有子结点,则将它的子结点放入队列尾,直到队列为空
代码实现:
/**
* 层序遍历
* 思路:
* 先将根结点放入队列中,
* 然后每次都从队列中取出一个结点打印该结点的值
* 若这个结点有子结点,则将它的子结点放入队列尾,直到队列为空
* @param root2
*/
public static void LaywerTraversal(Node root2){
if(root2==null) return;
Queue<Node> q = new LinkedList<Node>();
q.add(root2);
while(!q.isEmpty()){
Node n =q.poll();
System.out.print(n.data+" ");
if(n.left!=null){
q.add(n.left);
}
if(n.right!=null){
q.add(n.right);
}
}
}
已知先序遍历和后序遍历,如何求后序遍历
思路:
代码实现:
package JBTree;
/*class Node {
public int data;
public Node left;
public Node right;
public Node() {
}
public Node(int data) {
this.data = data;
this.left=null;
this.left=null;
}
}*/
public class BinaryTree1 {
private Node root;
public BinaryTree1() {
root=null;
}
//后序遍历实现
public void postOrder(Node localRoot){
if(localRoot!=null){
postOrder(localRoot.left);
postOrder(localRoot.right);
System.out.print(localRoot.data+" ");
}
}
public void postOrder(){
this.postOrder(this.root);
}
/**
* 还原二叉树
* @param preOrder
* @param inOrder
*/
public void initTree(int[] preOrder,int []inOrder){
this.root=this.initTree(preOrder, 0,preOrder.length-1,inOrder,0,inOrder.length);
}
public Node initTree(int[] preOrder, int start1, int end1, int[] inOrder, int start2,
int end2) {
if(start1>end1 || start2>end2){
return null;
}
int rootData=preOrder[start1];
Node head=new Node(rootData);
//根节点所在的位置
int rootIndex=findIndexInArray(inOrder,rootData,start2,end2);
int offSet=rootIndex-start2-1;//左子树个数
//构建左子树
head.left=initTree(preOrder,start1+1,start1+1+offSet,inOrder,start2,start2+offSet);
//构建右子树
head.right=initTree(preOrder,start1+2+offSet,end1,inOrder,rootIndex+1,end2);
return head;
}
/**
* 找到中序遍历中的根结点的下标
* @param a
* @param x
* @param begin
* @param end
* @return
*/
public int findIndexInArray(int[] inOrder, int rootData, int begin,
int end) {
for (int i = begin; i <= end; i++) {
if(inOrder[i]==rootData){
return i;
}
}
return -1;
}
public static void main(String[] args) {
BinaryTree bt=new BinaryTree();
int[]preOrder={1,2,4,8,9,5,10,3,6,7};
int[]inOrder={8,4,9,2,10,5,1,6,3,7};
bt.initTree(preOrder, inOrder);
System.out.println("二叉树的后序遍历:");
bt.postOrder();
}
}
如何求二叉树中结点的最大距离
思路:
左子树到根结点的最大距离leftMaxDistance
右子树到根结点的最大距离rightMaxDistance
两者之和maxDistance = leftMaxDistance + rightMaxDistance
代码实现:
package JBTree;
/*class Node {
public int data;
public Node left;
public Node right;
public int leftmaxdistance;
public int rightmaxdistance;
public Node() {
}
public Node(int data) {
this.data = data;
this.left=null;
this.left=null;
}
}*/
public class BinaryTreeDis {
private int maxLen = 0;
private int max(int a, int b){
return a>b? a:b;
}
public void findmaxdistance(Node root) {
if(root==null) {
return ;
}
if(root.left==null) {
root.leftmaxdistance=0;
}else {
findmaxdistance(root.left);
}
if(root.right==null) {
root.rightmaxdistance=0;
}else {
findmaxdistance(root.right);
}
if(root.left!=null) {
root.leftmaxdistance=max(root.left.leftmaxdistance,root.left.rightmaxdistance)+1;
}
if(root.right!=null) {
root.rightmaxdistance=max(root.right.leftmaxdistance,root.right.rightmaxdistance)+1;
}
if(root.leftmaxdistance+root.rightmaxdistance>maxLen) {
maxLen=root.leftmaxdistance+root.rightmaxdistance;
}
}
}
二叉树的类型
参考 https://blog.csdn.net/zhuboke3344/article/details/78759501
类型 | 定义 | 图示 |
满二叉树 Full Binary Tree | 除最后一层无任何子节点外,每一层上的所有节点都有两个子节点,最后一层都是叶子节点。满足下列性质: 1)一颗树深度为h,最大层数为k,深度与最大层数相同,k=h; 2)叶子节点数(最后一层)为2k−1; 3)第 i 层的节点数是:2i−1; 4)总节点数是:2k−1,且总节点数一定是奇数。 | |
完全二叉树 Complete Binary Tree | 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。满足下列性质: 1)只允许最后一层有空缺结点且空缺在右边,即叶子节点只能在层次最大的两层上出现; 2)对任一节点,如果其右子树的深度为j,则其左子树的深度必为j或j+1。 即度为1的点只有1个或0个; 3)除最后一层,第 i 层的节点数是:2i−1; 4)有n个节点的完全二叉树,其深度为:log2n+1或为log2n+1; 5)满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树。 | |
平衡二叉树 Balanced Binary Tree | 又被称为AVL树,它是一颗空树或左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。 | |
二叉搜索树 Binary Search Tree | 又称二叉查找树、二叉排序树(Binary Sort Tree)。它是一颗空树或是满足下列性质的二叉树: 1)若左子树不空,则左子树上所有节点的值均小于或等于它的根节点的值; 2)若右子树不空,则右子树上所有节点的值均大于或等于它的根节点的值; 3)左、右子树也分别为二叉排序树。 | |
红黑树 Red Black Tree | 是每个节点都带有颜色属性(颜色为红色或黑色)的自平衡二叉查找树,满足下列性质: 1)节点是红色或黑色; 2)根节点是黑色; 3)所有叶子节点都是黑色; 4)每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。) 5)从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。 |