目录
1.二叉树的创建
快速掌握为叉树的基本操作,这里只是创建一颗简单的二叉树,并不是二叉树的真正创建方式;后期会讲解二叉树的真正创建方式。
public class BinaryTree {
public static class TreeNode {
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
//public TreeNode root;
public TreeNode createTree() {
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
E.right = H;
C.left = F;
C.right = G;
return A;
}
}
我们在了解完二叉树的性质后,就能知道,二叉树的定义是递归式的,因此后续基本操作都是按照该概念实现的。
2.二叉树的基操
2.1.二叉树的遍历
二叉树的遍历分为三种:
-
NLR :前序遍历( 先序遍历 )—— 访问根结点 ---> 根的左子树 ---> 根的右子树。(根左右)
-
LNR :中序遍历 —— 根的左子树 ---> 根节点 ---> 根的右子树。 (左根右)
-
LRN :后序遍历 (Postorder Traversal)—— 根的左子树 ---> 根的右子树 ---> 根节点。(左右根)
前序遍历:
// 前序遍历
public void preOrder(TreeNode root) {
if(root == null) return;
System.out.println(root.val);//根
preOrder(root.left);//左
preOrder(root.right);//右
}
中序遍历:
// 中序遍历
public void inOrder(TreeNode root) {
if(root == null) return;
inOrder(root.left);//左
System.out.println(root.val);//根
inOrder(root.right);//右
}
后序遍历:
// 后序遍历
public void postOrder(TreeNode root) {
if(root == null) return;
postOrder(root.left);//左
postOrder(root.right);//右
System.out.println(root.val);//根
}
分享两种思路(以后序遍历为例):遍历思路和子问题思路
遍历思路:
List<Character> ret = new ArrayList<>();
//数组放外面,是为了防止每次递归都重新开辟空间
public List<Character> postOrderTraversal(TreeNode root) {
if(root == null) return ret;
ret.add(root.val);
postOrderTraversal(root.left);
postOrderTraversal(root.right);
return ret;
}
子问题思路:
public List<Character> postOrderTraversal(TreeNode root) {
List<Character> ret = new ArrayList<>();
//数组如果放在里边,就需要接收返回值了,否则每次都重新创建了一个新的数组
if(root == null) return ret;
List<Character> leftTree = postOrderTraversal(root.left);
ret.addAll(leftTree);
List<Character> rightTree = postOrderTraversal(root.right);
ret.addAll(rightTree);
ret.add(root.val);
return ret;
}
2.2.二叉树的基本操作
public static int nodeSize = 0;
public int size(TreeNode root) {
if(root == null) return 0;
nodeSize++;//把每一颗子树的根都加起来
size(root.left);
size(root.right);
return nodeSize;
}
2.2.1.获取树中节点的个数---子问题思路
public int size2(TreeNode root) {
if(root == null) return 0;
//树的结点个数 = 左子树结点数 + 右子树结点数 + 根(1)
return size2(root.left) + size2(root.right) + 1;
}
2.2.2.获取叶子节点的个数---遍历思路
public static int leafSize = 0;
public int getLeafNodeCount(TreeNode root) {
if(root == null) return 0;
//叶子结点:左孩子和右孩子都为null
if(root.left == null && root. right == null) {
leafSize++;
}
getLeafNodeCount(root.left);
getLeafNodeCount(root.right);
return leafSize;
}
2.2.2.获取叶子节点的个数---子问题思路
public int getLeafNodeCount2(TreeNode root) {
if(root == null) return 0;
if(root.left == null && root.right == null) {
return 1;
}
return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
}
public int getKLevelNodeCount(TreeNode root, int k) {
if(root == null) return 0;
if(k == 1) return 1;
//第 k 层结点个数等于 以root的左子树为根结点的这棵树的第 k-1 层 + 以root的右子树为根结点的这棵树的第 k-1 层
// 依次类推,k == 1 的时候,这棵树只有根,左右子树都为空,那么就返回 1
return getKLevelNodeCount(root.left, k - 1) + getKLevelNodeCount(root.right, k - 1);
}
2.2.4获取二叉树的高度
public int getHeight(TreeNode root) {
if(root == null) return 0;
if(root.left == null && root.right == null) {
return 1;
}
int leftH = getHeight(root.left);
int rightH = getHeight(root.right);
//二叉树的高度 = 左子树高度 和 右子树高度 中较大的一个 + 1
return leftH > rightH ? leftH + 1 : rightH + 1;
}
这里的求左右子树的高度时,建议接收值,不然在三木操作符中会重复计算,导致时间浪费,在oj上可能就会跑不过。
2.2.5.检测值为value的元素是否存在
public TreeNode find(TreeNode root, char val) {
if(root == null) return null;
if(root.val == val) return root;
//left和right只有 root 和 null两种可能,先把左边找完,如果不为空,就返回
TreeNode left = find(root.left, val);
if(left != null) {
return left;
}
//右边和左边一样
TreeNode right = find(root.right, val);
if(right != null) {
return right;
}
return null;
}
2.2.6.层序遍历---简易版
public void levelOrder(TreeNode root) {
if(root == null) return;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
//队列不为空,就弹出一个元素,并输出
while(!queue.isEmpty()) {
TreeNode cur = queue.poll();
System.out.print(cur.val + " ");
//弹出的元素左右子树不为空,就依次入队
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
}
层序遍历需要用到队列,每一次出队后,就把出队元素的左右元素入队(不为空就入)
2.2.7.层序遍历---提高难度
public List<List<Character>> levelOrder(TreeNode root) {
List<List<Character>> ret = new ArrayList<>();
if(root == null) return ret;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()) {
//二叉树当前层的元素集合
List<Character> list = new ArrayList<>();
int size = queue.size();
//size就是二叉树当前层的元素个数
while(size != 0) {
TreeNode cur = queue.poll();
list.add(cur.val);
size--;
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
ret.add(list);
}
return ret;
}
当求一棵二叉树的层序遍历时,返回值改为二维数组,则每一层都可以看成是一个一维数组,这样的话,我们就可以在队列不为空这个循环里嵌套一个循环,用每一层的元素个数来控制循环;
2.2.8.判断一棵树是不是完全二叉树
public boolean isCompleteTree(TreeNode root) {
if(root == null) return false;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if(cur != null) {
queue.offer(cur.left);
queue.offer(cur.right);
}else {
break;
}
}
//第2次遍历队列 判断队列当中 是否存在非空的元素
while (!queue.isEmpty()) {
TreeNode cur = queue.peek();
if(cur == null) {
queue.poll();
}else {
return false;
}
}
return true;
}
分析:判断一棵树是不是完全二叉树,也是要用到队列,类似于层序遍历,但是这里不同的是,左右子树为空也入队,null也是可以入队的;当弹出一个元素是 null 时,如果队里的元素全为 null,则说明它是一颗完全二叉树,,当然我们没有这种判断条件,所以这样做,当弹出的元素为 null 时,我们继续弹元素,如果为 null ,一直弹,直到队空;否则,就像图中右边的情况,弹出一个元素为空时,队里还有一个不为空,此时一定不是完全二叉树,,,
谢谢观看!!!