二叉树:每个结点至多有两个子树
满二叉树:每一层的结点个数都是最大结点数
完全二叉树:叶子节点在最后两层;对于任一结点,左子树的深度比右子树深度大1或者相等
性质:
二叉树:第i层,至多有2^(i-1)个结点
二叉树:深度为k的二叉树,至多有(2^k)-1个结点
满二叉树:深度为k的满二叉树的结点个数为(2^k)-1
二叉树:任何一个二叉树,度为0的结点的个数n0,度为1的结点的个数n1,度为2的结点的个数的关系: n0 = n2+1,总的结点个数n
原因:n0+n1+n2 = n 除了根节点之外,其它几点都有一个分支进入分支个数n-1,分支都是由度为1或者度为2的结点发出的 n1+2*n2 = n-1 根据这两个公式得到n0=n2+1
完全二叉树:任何一个具有n个结点的完全二叉树的深度为 log以2为底n向下取整 + 1
完全二叉树:任何一个完全二叉树按照层次为结点编号
i=1,该结点是完全二叉树的根,无双亲
i>1,该节点不是根,该结点的双亲的编号为i/2向下取整
一个结点的编号是i,如果2*i大于n,表明该结点没有左孩子,否则,左孩子编号为2*i
一个结点的编号是i,如果2*i+1大于n,表明该结点没有右孩子,否则,右孩子编号为2*i
存储
(1)顺序存储
用一组地址连续的存储单元将二叉树从上到下从左到右按顺序存储,存储成对应的完全二叉树的形式(完全二叉树中编号为i的结点存储在数组下标为i-1的分量中),如果某个结点不存在,该位置存0
数组的长度(2^k)-1 二叉树的深度为k,空间浪费
(2)链式存储
数据域 左孩子 右孩子
指针域有浪费:含有n个结点的二叉树有n+1个空指针域 有n个结点,就有2*n个指针域,就有n-1个分支,所以,有n+1个指针浪费
public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } @Override public String toString() { return "TreeNode [val=" + val + ", left=" + left + ", right=" + right + "]"; } }
二叉树遍历
前序
public static void preOrder(TreeNode node){ //前序遍历,根左右,递归实现 //每次调用时,参数node都是当前子树的根节点 if(node == null){ //如果当前节点是null,直接返回 return; }else{ //如果当前节点不是null,输出当前节点的值,然后遍历当前节点的左右子树 System.out.print(node.val+" "); preOrder(node.left); preOrder(node.right); } } public static void preOrder2(TreeNode root){ //前序遍历,根左右,借助栈,非递归实现 //如果根节点是null,表明这是一棵空树,直接返回 if(root == null){ return; }else{ //如果根节点不是null,遍历 Stack<TreeNode> stack = new Stack<TreeNode>(); //先将根节点入栈 stack.push(root); while(!stack.isEmpty()){//栈空时,表明已经遍历完成 //弹出栈顶保存的那个节点,遍历其左右子树 TreeNode node = stack.pop(); System.out.print(node.val+" "); //由于栈是先进后出的数据结构,因此,先将当前节点的右子树的根节点入栈,再将当前节点的左子树的根节点入栈 if(node.right != null){ stack.push(node.right); } if(node.left != null){ stack.push(node.left); } } } }
中序
public static void inOrder(TreeNode node){ //中序遍历,左根右,递归实现 if(node == null){ return; }else{ //当前节点不是null时,先遍历当前节点的左子树,然后输出当前节点的值,最后遍历当前节点的右子树 inOrder(node.left); System.out.print(node.val+" "); inOrder(node.right); } } public static void inOrder2(TreeNode root){ //中序遍历,左根右,递归实现 if(root == null){ return; }else{ Stack<TreeNode> stack = new Stack<TreeNode>(); //左根右,第一个应该是该二叉树最左下的节点 //从根节点出发,一直寻找节点的左节点,直到最左下的节点 while(root!=null || !stack.isEmpty()){ while(root!=null){ stack.push(root); root = root.left; } //while循环之后,root的值是null,只有当当前节点cur有右子树,root的值会成为右子树的根节点 //现在栈顶元素是最左下的节点,出栈该节点,输出该节点的值,然后看该节点是否存在右节点 //如果存在右节点,继续从该右节点出发,一直寻找节点的左节点,直到最左下的节点 TreeNode cur = stack.pop(); System.out.print(cur.val+" "); if(cur.right != null){ root = cur.right; } } } }
后序
public static void postOrder(TreeNode node){ //后序遍历 左右跟 if(node == null){ return; }else{ postOrder(node.left); postOrder(node.right); System.out.print(node.val+" "); } }
层序
public static void layerOrder(TreeNode root){ //层序遍历 //利用队列实现 if(root == null){ return; }else{ Queue<TreeNode> queue = new LinkedBlockingQueue<TreeNode>(); queue.add(root); while(!queue.isEmpty()){ TreeNode node = queue.remove(); System.out.print(node.val+" "); if(node.left != null){ queue.add(node.left); } if(node.right != null){ queue.add(node.right); } } } }
二叉树重建
public static TreeNode createBitree(List<Integer> preOrder,List<Integer> inOrder){ int size = preOrder.size(); if(size == inOrder.size() && size>0){ TreeNode root = new TreeNode(preOrder.get(0));//创建当前子树的根节点 //当前子树的根节点在中序遍历序列中的下标,找到该下标之后,该以左的子序列为当前根节点的左子树的中序遍历序列,该以右的子序列为当前根节点的右子树的中序遍历序列 int rootIndex = inOrder.indexOf(preOrder.get(0)); System.out.println("rootIndex:"+rootIndex); if(rootIndex==0){ //表明当前节点没有左子树 root.setLeft(null); root.setRight(createBitree(preOrder.subList(rootIndex+1, size), inOrder.subList(rootIndex+1, size))); }else if(rootIndex == preOrder.size()-1){ //表明当前节点没有右子树 root.setLeft(createBitree(preOrder.subList(1, rootIndex+1), inOrder.subList(0, rootIndex))); root.setRight(null); }else{ //当前节点既有左子树又有右子树 root.setLeft(createBitree(preOrder.subList(1, rootIndex+1), inOrder.subList(0, rootIndex))); root.setRight(createBitree(preOrder.subList(rootIndex+1, size), inOrder.subList(rootIndex+1, size))); } return root; }else{ return null; } }
二叉树的计数
具有n个结点、互不相似的二叉树的数目
相似是指二叉树的形态一样,跟结点的值没有关系
参考:https://www.cnblogs.com/gxclmx/p/7485384.html