1. 数据结构
二叉树是由根节点和左子节点,右子节点构成的数据结构,左子节点和右子节点不一定存在。
// 这就是二叉树在Java语言中的定义
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
}
2. 二叉树常规的遍历
- 前序遍历
头节点->左子节点->右子节点 - 中序遍历
左子节点->头节点->右子节点 - 后序遍历
左子节点->右子节点->头节点 - 层遍历
一般采用循环和队列来实现
前序遍历,中序遍历,后序遍历采用的都是递归序的方式,每个节点在遍历过程中都会经历三次。
//递归的方法体
private void f(TreeNode root){
if (root == null) return;
pre(root.left);
pre(root.right);
}
结合 f 方法和上图做如下说明:
1,1 节点进f方法:1
2,程序告诉它走左边:2
3,程序告诉它走左边:4
4,程序告诉它走左边:null,返回 4
5,程序告诉它走右边:null,返回 4
6,返回到:2
7,程序告诉它走右边:5
8,程序告诉它走右边:null,返回 5
9,程序告诉它走右边:null,返回 5
10,返回 2
......
如此得到的结果:1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1
前序遍历结果就是将第一次出现的数连起来;1 2 4 5 3 6 7
中序遍历结果就是将第二次出现的数连起来;4 2 5 1 6 3 7
后序遍历结果就是将第三次出现的数连起来;4 5 2 6 7 3 1
关于层遍历,从队列头取出节点后打印,然后将该节点的左右节点依次放到队列中。最终结果就是层打印的结果
3,二叉搜索树
二叉搜索树是左节点的值比根节点的值小,右节点的值比根节点的值大。二叉搜索树的中序遍历出来的序列是递增序列。
4. 常见算法
-
寻找两个节点最近的祖先节点
递归查找,根据搜索二叉树的原理可知,当两个节点一个比根节点小一个比根节点 大,则公共的祖先节点为根节点,因为2个节点分在树的2边;当搜索的2个节点其中一个就 是根节点,则公共最近的祖先节点就是该节点。如果以上条件不满足,当2个节点都比根节 点小则向左子树递归;当2个节点都比根节点大则向右子树递归。
-
二叉搜索树单节点(k)剔除,剔除范围外[l,r]的节点
单节点剔除,可以根据二叉搜索树特性,判断递归向左节点还是向右节点。当找到节点时,如果节点的左节点==null返回右节点,反之亦然;当左右节点都存在则将左子树挂在右子树的最后一个左子节点上,返回右子树。 范围节点剔除,如果root节点的值比高范围(r)还大,说明返回的树在左子树上,因为二叉搜索树的特性右子树的值一定比根节点的值大,则右子树的节点值一定在高范围(r)外;如果root节点的值比低范围(l)还小,说明返回的树在右子树上,因为二叉搜索树的特性左子树的值一定比根节点的值小,则左子树的节点值一定在低范围(l)外。如果根节点的值在范围内,则继续进行递归。
-
根据二叉树的前序,后序或前序,中序或中序,后序遍历的结果构造二叉树
- 前序,后序构造二叉树
假如前序遍历的数组为pre,后序遍历的数组为post。
1,确定待构建的二叉树根节点为pre[0]。
2,确定左子树范围,假设左子树存在,左分支的头节点为 pre[1],但左子树的头节点也出现在左分支 > 的后序遍历最后。假定左子树有L个节点,则出现等式 pre[1] = post[L-1],可以用循环post来确定L的 值。左子树节点在pre[1 : L+1] 和 post[0 : L]中,按上续步骤依次递归。
3,确定右子树的范围,右子树节点在pre[L : N] 和 post[L+1 : N-1]中,按上续步骤依次递归,N为数的 长度。public TreeNode constructFromPrePost(int[] pre, int[] post) { if (pre.length == 0){ return null; } TreeNode root = new TreeNode(pre[0]); if (pre.length == 1){ return root; } //查找左子树的长度 int leftL = 0; for (int i = 0; i < post.length; i++) { if (post[i] == pre[1]){ leftL = i + 1; break; } } root.left = constructFromPrePost(Arrays.copyOfRange(pre,1,leftL+1), Arrays.copyOfRange(post,0,leftL)); root.right = constructFromPrePost(Arrays.copyOfRange(pre,leftL + 1,pre.length), Arrays.copyOfRange(post,leftL,post.length-1)); return root; }- 前序,中序构造二叉树
假如前序遍历的数组为pre,中序遍历的数组为in。
1,待构建二叉树的根节点为pre[0]
2,确定左子树范围和右子树的范围,可以通过根节点的值在in数组中找到根节点在后序遍历的位置,因为有 中序遍历数组,在中序遍历数组中根节点的左侧为左子树,右侧为右子树。public TreeNode constructFromPreIn(int[] pre, int[] in) { if (preorder.length == 0){ return null; } TreeNode root = new TreeNode(preorder[0]); int inorderRootIndex = 0; for (int i = 0; i < inorder.length; i++) { if (preorder[0] == inorder[i]){ inorderRootIndex = i; break; } } root.left = constructFromPreIn(Arrays.copyOfRange(preorder,1,inorderRootIndex + 1), Arrays.copyOfRange(inorder,0,inorderRootIndex)); root.right = constructFromPreIn(Arrays.copyOfRange(preorder,inorderRootIndex + 1,inorder.length), Arrays.copyOfRange(inorder,inorderRootIndex + 1,inorder.length)); return root; } -
完全二叉树
- 说明
除最后一层外,其他各层的节点都是全的,最后一层的节点都集中在左侧。
- 判断二叉树是否为完全二叉树可以用层遍历和标记节点序号2种方式
层遍历,对二叉树进行层遍历,如果遇到null节点则标记为最后一个节点,继续层遍历,如果在后续中 存在不为null的节点则认为非完全二叉树。
public boolean isCompleteTree(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); boolean isLastNode = false; while (!queue.isEmpty()) { TreeNode treeNode = queue.poll(); //已经挑出了最后一个节点,但是还存在后续节点 if (treeNode != null && isLastNode) { return false; } //如果节点为null,则认为是最后一个节点 if (treeNode == null) { isLastNode = true; continue; } queue.offer(treeNode.left); queue.offer(treeNode.right); } return true; }标记节点序号,如果为二叉树的节点依次标记为自增整数时,会发现如下图
假如,我们定义root节点的序号为rootCode,左节点的序号为 lCode,右节点的序号为 rCode。会出现 等式关系 lcode = rootCode * 2 rCode = rootCode * 2 +1。 切入正题,如果完全二叉树,节点的数量等于最后一个节点的序号。如上图,节点数量是5,最后一个节 点的code也是 5。
public boolean isCompleteTree1(TreeNode root) { List<ANode> aNodes = new ArrayList<>(); //默认1号节点的序号为1 aNodes.add(new ANode(1,root)); //统计用的,用来确定节点的个数 int i = 0; while (i < aNodes.size()){ ANode aNode = aNodes.get(i); if (aNode.treeNode != null){ //为节点包装序号 aNodes.add(new ANode(aNode.code * 2,aNode.treeNode.left)); aNodes.add(new ANode(aNode.code * 2 + 1,aNode.treeNode.right)); } i++; } //获取最后节点的序号 return aNodes.get(i -1).code == aNodes.size(); } class ANode { int code; TreeNode treeNode; public ANode(int code, TreeNode treeNode) { this.code = code; this.treeNode = treeNode; } }