模拟实现:
首先创建一个树:
由于每一个TreeNode都是一个结点,所以创建为内部类;
public class BinaryTree {
static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode creatTree(){
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');
TreeNode H = new TreeNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
E.right = H;
C.left = F;
C.right = G;
return A;
}
}
不管是哪种方法遍历,可以想象在最后一个结点的时候代码会打印哪个字母,并且return后打印哪个,这样想思考的更清楚
每种方法都要画图
前序遍历:
// 前序遍历
void preOrder(TreeNode root){
if (root == null){
return;
}
System.out.println(root.val);
postOrder(root.left);
postOrder(root.right);
return;
}
中序遍历:
// 中序遍历
void inOrder(TreeNode root){
if (root == null){
return;
}
postOrder(root.left);
System.out.println(root.val);
postOrder(root.right);
return;
}
后序遍历:
// 后序遍历
void postOrder(TreeNode root){
if (root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.println(root.val);
return;
}
二叉树的基本操作:
获取树中结点个数:
关键在于只要不是空就++
第一种解法:
public int nodeSize;
int size(TreeNode root){
if(root == null){
return 0;
}
//关键:只要不是空就++
nodeSize++;
size(root.left);
size(root.right);
return nodeSize;
}
子问题解法:
树的结点个数 = 左子树的结点个数 + 右子树的结点个数 + 1;
当拿只有三个结点的二叉树去理解很清楚,当结点很多,同样适用:
int size(TreeNode root){
if(root == null){
return 0;
}
//子问题
return size(root.left)+
size(root.right)+1;
}
获取叶子结点的个数:
遍历思路:
按照某种遍历方式(四种遍历方法),遍历到某个结点判断式是叶子结点,计数器++
public int leafNodeCount;
int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null&&root.right == null){
leafNodeCount++;
}
getLeafNodeCount(root.left);
getLeafNodeCount(root.right);
return leafNodeCount;
}
子问题解法:
叶子个数 = 左子树子叶个数 + 右子树子叶个数
int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null&&root.right == null){
return 1;
}
return getLeafNodeCount(root.left)+
getLeafNodeCount(root.right);
}
获取第K层结点的个数:
子问题方法:
整棵树的第K层节点个数 = 左子树第 K -1 层结点个数 + 右子树第 K - 1 层结点个数
int getKLevelNodeCount(TreeNode root,int k){
if(root == null){
return 0;
}
if(k == 1){
return 1;
}
return getKLevelNodeCount(root.left,k-1)
+ getKLevelNodeCount(root.right, k-1);
}
获取二叉树的高度:
二叉树的高度 = 左子树的高度 和 右子树高度 的最大的值 + 1;
加的1 是根结点,对于叶子结点,左右树都是0 返回的就是自己单独一个结点的高度
// 获取二叉树的高度
int getHeight(TreeNode root){
if(root == null){
return 0;
}
int leftTree = getHeight(root.left);
int rightTree = getHeight(root.right);
return leftTree > rightTree ?
leftTree + 1 : rightTree + 1;
}
检测值为value的值是否存在:
思路:
1.先判断是否为null
2.判断根结点的val是否等于 val
3.遍历左子树
4.遍历右子树
从3 .4 开始递归
// 检测值为value的元素是否存在
TreeNode find(TreeNode root, int val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
TreeNode ret1 = find(root.left,val);
if (ret1 != null){
return ret1;
}
TreeNode ret2 = find(root.right,val);
if(ret2 != null){
return ret2;
}
return null;
}
层序遍历:
使用队列去实现:
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);
}
}
}
判断一棵树是不是完全二叉树:
思路:
boolean isCompleteTree(TreeNode root){
if(root == null){
return true;
}
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;
}
}
//出现空 判断队列中是否还有非空
while(!queue.isEmpty()){
TreeNode tmp = queue.peek();
if(tmp == null){
queue.poll();
}else {
return false;
}
}
return true;
}
LeetCode题目:
144.二叉树的前序遍历
遍历思维做法:
List<Integer> List = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if (root == null){
return List;
}
List.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return List;
}
利用好返回值:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null){
return list;
}
list.add(root.val);
List<Integer> leftTree = preorderTraversal(root.left);
list.addAll(leftTree);
List<Integer> rightTree = preorderTraversal(root.right);
list.addAll(rightTree);
return list;
}
非递归:
void preorderTraversal(TreeNode root) {
if(root == null){
return ;
}
Stack<TreeNode> stack = new Stack();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
System.out.print(cur.val+ " ");
cur = cur.left;
}
TreeNode top = stack.pop();
cur = top.right;
}
}
}
94. 二叉树的中序遍历
非递归打印:
思路:
void inorderTraversal(TreeNode root) {
if(root == null){
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
TreeNode top = stack.pop();
System.out.print(top.val + " ");
cur = top.right;
}
}
145. 二叉树的后序遍历
博哥代码:
void postorderTraversal(TreeNode root) {
if(root == null){
return;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode prev = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
TreeNode top = stack.peek();
if(top.right == null || top.right == prev) {
System.out.print(top.val+" ");
stack.pop();
prev = top;
}else {
cur = top.right;
}
}
}
我的思路:
void postorderTraversal2(TreeNode root) {
if(root == null){
return;
}
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2= new Stack<>();
TreeNode cur = root;
while(cur != null || !stack1.isEmpty()){
while(cur != null){
stack1.push(cur);
cur = cur.left;
}
TreeNode top = stack1.peek();
stack2.push(top);
cur = top.right;
if(cur == null){
while(!stack1.isEmpty()&&!stack2.isEmpty()&&
stack2.peek()==stack1.peek() ){
System.out.print(stack1.peek().val+" ");
stack1.pop();
stack2.pop();
}
if(stack1.isEmpty())
{
break;
}
top = stack1.peek();
stack2.push(top);
cur = top.right;
}
}
}
100. 相同的树
思路:
public boolean isSameTree(TreeNode p, TreeNode q) {
//先从结构上,再从数值上
//结构上
//一个为空一个不为空 肯定不相同
if (p == null && q != null || p != null && q == null){
return false;
}
//两个都为空
if(p == null && q == null){
return true
}
//数值上
//值不相等 不相同{根节点的判断}
if(p.val != q.val){
return false;
}
//左右树的值进行以上 结构 和 值上的判断
return isSameTree(p.left,q.left)&&
isSameTree(p.right,q.right);
}
心得:
在二叉树的操作或者题目上,都是针对结点进行各种情况的判断(一般是先判断结构 再 判断数值),然后再递归,这样递归到任何结点,进行的还是以上红色字体的操作
本题再return之前的操作都针对具体结点的分析 先结构 再 数值 然后递归 重复以上循环
572. 另一棵树的子树
正确代码:
错误代码,还没有解决
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q != null || p != null && q == null){
return false;
}
if(p == null && q == null){
return true;
}
if(p.val != q.val){
return false;
}
return isSameTree(p.left,q.left)&&
isSameTree(p.right,q.right);
}
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null || subRoot == null){
return false;
}
if(isSameTree(root,subRoot)){
return true;
}
return isSameTree(root.left,subRoot) ||
isSameTree(root.right,subRoot);
}
226. 翻转二叉树
思路:
抽象到只有三个节点的满二叉树,那么只需要根节点的左右子树进行交换就可以实现
所以具体到结点,交换根结点的左右子树,抽象到整体使用前序遍历对所有的结点进行上面红色的操作
其中,判断结点左右子树是否都为空,这样最起码会减少一般的遍历次数
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
//如果左右子树都为空就不再交换
if (root.left == null && root.right == null){
return root;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
110. 平衡二叉树
子问题:
当左右子树都为平衡二叉树,整棵树才是平衡二叉树
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
return getHeight(root) >= 0;
}
int getHeight(TreeNode root){
if(root == null){
return 0;
}
int leftTree = getHeight(root.left);
if(leftTree < 0){
return -1;
}
int rightTree = getHeight(root.right);
if(leftTree >= 0 && rightTree >= 0 && Math.abs(leftTree - rightTree) <= 1){
return Math.max(leftTree,rightTree)+1;
}else{
return -1;
}
}
时间复杂度O(N);
101. 对称二叉树
思路:
关键:判断2.3同时为真时才是对称的
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isSymmetricChild(root.left,root.right);
}
private boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
if(leftTree == null && rightTree != null || leftTree != null && rightTree == null){
return false;
}
if(leftTree == null && rightTree == null){
return true;
}
if(leftTree.val != rightTree.val){
return false;
}
return isSymmetricChild(leftTree.left,rightTree.right)&&isSymmetricChild(leftTree.right,rightTree.left);
}
注意:
两个值相同时,return ture ; 局部的正确, 当该结点下面的不相同时就是错的
KY11 二叉树遍历
注意:
i被static修饰的原因:
class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val = val;
}
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static int i;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 注意 while 处理多个 case
String str = in.nextLine();
TreeNode root = creatTree(str);
inorder(root);
}
}
public static TreeNode creatTree(String str){
TreeNode root = null;
if(str.charAt(i) != '#'){
root = new TreeNode(str.charAt(i));
i++;
root.left = creatTree(str);
root.right = creatTree(str);
}else{
i++;
}
return root;
}
public static void inorder(TreeNode root){
if(root == null){
return ;
}
inorder(root.left);
System.out.print(root.val+" ");
inorder(root.right);
}
}
102. 二叉树的层序遍历
思路:
public List<List<Integer>> levelOrder(TreeNode root) {
//创建个二维数组
List<List<Integer>> ret = new ArrayList<>();
if(root == null){
return ret;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
//每一层数值 封装在一个新的tmp数组内
List<Integer> tmp = new ArrayList<>();
int size = queue.size();
while(size != 0) {
TreeNode cur = queue.poll();
//将本层的数值 添加到tmp中去
tmp.add(cur.val);
size--;
System.out.print(cur.val + " ");
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
ret.add(tmp);
}
return ret;
}
236. 二叉树的最近公共祖先
任何叶子结点的.left .right 都是 null ,当遍历到时 返回 null , 像leftTree 接收的 会有 null 的情形
使用递归:
思路:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;
}
if(root == q || root == p){
return root;
}
TreeNode leftTree = lowestCommonAncestor(root.left,p,q);
TreeNode rightTree = lowestCommonAncestor(root.right,p,q);
if(leftTree != null && rightTree != null){
return root;
}else if(leftTree != null){
return leftTree;
}else{
return rightTree;
}
}
使用栈:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;
}
Stack<TreeNode> stackP = new Stack<>();
Stack<TreeNode> stackQ = new Stack<>();
getPath(root,p,stackP);
getPath(root,q,stackQ);
int sizeP = stackP.size();
int sizeQ = stackQ.size();
if(sizeP > sizeQ){
int size = sizeP - sizeQ;
while(size != 0){
stackP.pop();
size--;
}
}else{
int size = sizeQ - sizeP;
while(size != 0){
stackQ.pop();
size--;
}
}
while(!stackP.isEmpty() && !stackQ.isEmpty()){
if(stackP.peek() == stackQ.peek()){
return stackP.peek();
}else{
stackP.pop();
stackQ.pop();
}
}
return null;
}
public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode>stack){
if(root == null){
return false;
}
stack.push(root);
if(root == node){
return true;
}
boolean leftTree = getPath(root.left,node,stack);
if(leftTree == true){
return true;
}
boolean rightTree = getPath(root.right,node,stack);
if(rightTree == true){
return true;
}
stack.pop();
return false;
}
}
105. 从前序与中序遍历序列构造二叉树
思路:
先序遍历找根节点,中序遍历找根节点左子树和右子树
class Solution {
public int preIndex;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeChilde(preorder,inorder,0,inorder.length - 1);
}
public TreeNode buildTreeChilde(int[] preorder, int[] inorder,int inbegin,int inend){
if(inbegin > inend){
return null;
}
TreeNode root = new TreeNode(preorder[preIndex]);
int rootIndex = findRootIndex(inorder,inbegin,inend,preorder[preIndex]);
//找到root的下标
if(rootIndex == -1){
return null;
}
preIndex++;
root.left =buildTreeChilde(preorder,inorder,inbegin,rootIndex-1);
root.right =buildTreeChilde(preorder,inorder,rootIndex + 1,inend);
return root;
}
public int findRootIndex(int[] inorder,int inbegin,int inend,int key){
for(int i = inbegin; i <= inend; i++){
if(inorder[i] == key){
return i;
}
}
return -1;
}
}
106. 从中序与后序遍历序列构造二叉树
后序遍历找根结点,创建二叉树的时候先创建右树,因为后序遍历时左右根,倒着遍历时先遍历到右根
class Solution {
public int postIndex;
public TreeNode buildTree(int[] inorder, int[] postorder) {
postIndex = postorder.length - 1;
return buildTreeChilde(postorder,inorder,0,inorder.length - 1);
}
public TreeNode buildTreeChilde(int[] postorder, int[] inorder,int inbegin,int inend){
if(inbegin > inend){
return null;
}
TreeNode root = new TreeNode(postorder[postIndex]);
int rootIndex = findRootIndex(inorder,inbegin,inend,postorder[postIndex]);
//找到root的下标
if(rootIndex == -1){
return null;
}
postIndex--;
root.right =buildTreeChilde(postorder,inorder,rootIndex + 1,inend);
root.left =buildTreeChilde(postorder,inorder,inbegin,rootIndex-1);
return root;
}
public int findRootIndex(int[] inorder,int inbegin,int inend,int key){
for(int i = inbegin; i <= inend; i++){
if(inorder[i] == key){
return i;
}
}
return -1;
}
}
606. 根据二叉树创建字符串
思路:
理解一下,当右边为空,回溯后在上次层的递归添加小括号
class Solution {
public String tree2str(TreeNode root) {
StringBuilder stringbuilder = new StringBuilder();
tree2strChiled(root,stringbuilder);
return stringbuilder.toString();
}
public void tree2strChiled(TreeNode t,StringBuilder stringbuilder) {
if(t == null){
return;
}
stringbuilder.append(t.val);
if(t.left != null){
stringbuilder.append("(");
tree2strChiled(t.left,stringbuilder);
stringbuilder.append(")");
}else{
//右边为空
if(t.right == null){
return;
}else{
//右边不为空
stringbuilder.append("()");
}
}
if(t.right != null){
stringbuilder.append("(");
tree2strChiled(t.right,stringbuilder);
stringbuilder.append(")");
}else{
return;
}
}
}