目录
1、100. 相同的树 - 力扣(LeetCode)
2、572. 另一棵树的子树 - 力扣(LeetCode)
3、110. 平衡二叉树 - 力扣(LeetCode)
4、101. 对称二叉树 - 力扣(LeetCode)
5、102. 二叉树的层序遍历 - 力扣(LeetCode)
?6、判断该树是否为完全二叉树
七、二叉树遍历_牛客题霸_牛客网 (nowcoder.com)
?八、236. 二叉树的最近公共祖先 - 力扣(LeetCode)
九、二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)
十、105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
十一、106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
十二、非递归实现二叉树的前中后序遍历

?我们考虑它相同和不相同的情况,再用递归遍历:

完整代码:
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);
}


这道题,我们可以直接用上一道题的代码,其思路如下:
- 我们首先判断root和subRoot是不是两颗相同的树,是的话就返回true?
- 不是的话,就继续判断subRoot是不是root.left的左子树的子树或者root.left是不是和和subRoot为相同的树
- 同理再判断subRoot和root.right;
完整代码:
class Solution {
//时间复杂度为:n * m
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;
}
if(isSubtree(root.left,subRoot)){
return true;
}
if(isSubtree(root.right,subRoot)){
return true;
}
return false;
}
}

思路:
- ?首先这道题判断平衡也就是左右子树的高度差,那就需要求出左右子树的高度,,进行比较
- 然后还需要求出左子树的子树的高度差,和右子树的子树的高度差,来判断左子树和右子树是否平衡。
- 所以需要遍历树的每一个结点

完整代码:
class Solution {
//时间复杂度为:n * n
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;
}
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.abs(leftHeight-rightHeight) <= 1
&& isBalanced(root.left)
&& isBalanced(root.right);
}
}
?上面的这种解法的时间复杂度是O(n^2),会出现大量的的重复计算,当计算根结点的左子树高度时,递归到数字九时返回一个2就已经说明了该树不平衡,所以我们从下向上计算高度时顺便判断一下,这样的话时间复杂度就是O(n):
class Solution {
int getHeight(TreeNode root){
if (root == null){
return 0;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
if(leftHeight >= 0 && rightHeight >= 0 && Math.abs(leftHeight - rightHeight) <= 1){
return Math.max(leftHeight,rightHeight) + 1;
}else{
//返回-1 就说明已经出现了不平衡
return -1;
}
}
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
return getHeight(root) >= 0;
}
}

?思路就是判断root的左右子树是否对称:左子树的右树和右子树的左树是否相同:
class Solution {
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;
}
//判断val值
if (leftTree.val != rightTree.val){
return false;
}
//进行递归左子树的右树和右子树的左树
return isSymmetricChild(leftTree.left, rightTree.right) &&
isSymmetricChild(leftTree.right, rightTree.left);
}
}

思路:
- 首先要创建两个顺序表来分层存储二叉树
- 创建一个队列,先把根结点入对,再把top弹出的同时并赋值给cur并打印
- 并且把top的左右子树入队,直到树为空

?完整代码:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<List<Integer>>();
if(root == null){
return ret;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> leve = new ArrayList<Integer>();
int queue_size = queue.size();
for(int i = 0; i < queue_size; i++){
TreeNode node = queue.poll();
leve.add(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
ret.add(leve);
}
return ret;
}
}
?6、判断该树是否为完全二叉树
思路:
- 将null也入队,如果队列中只剩下null,说明就为完全二叉树
- 当非完全二叉树入队时,出队的时候会发现队列中还剩下null和结点

?
?完整代码:
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 cur = queue.peek();
if (cur != null){
//不是完全二叉树
return false;
}else {
queue.poll();
}
}
return true;
}

import java.util.*;
public class Main {
class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val = val;
}
}
public int i = 0;
//创建树
public TreeNode createTree(String s){
TreeNode root = null;
if (s.charAt(i) != '#'){
root = new TreeNode(s.charAt(i));
i++;
root.left = createTree(s);
root.right = createTree(s);
}else {
i++;
}
return root;
}
public void inorder(TreeNode root){
if (root == null){
return;
}
inorder(root.left);
System.out.print(root.val+" ");
inorder(root.right);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String s = scanner.nextLine();
Main m = new Main();
TreeNode root = m.createTree(s);
m.inorder(root);
}
}
}

?先列出求公共祖先会出现的情况:

分析:
- 假设这是一颗二叉搜索树(中序遍历是有序的),p == root?|| q == root,?此时公共祖先就是root
- 当p和q刚好在左右子树中,公共祖先就是根结点
- 当p.val和q.val都小于root.val,此时p和q都在root的左子树中,此时就在左子树中找,右子树同理;
完整代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
if(root == null){
return null;
}
if(root == p || root == q){
return root;
}
TreeNode retleft = lowestCommonAncestor(root.left,p,q);
TreeNode retright = lowestCommonAncestor(root.right,p,q);
if(retleft != null && retright != null){
return root;
}else if(retleft != null){//都在左树
return retleft;
}else{
return retright;
}
}
}
当我们用栈的思想来解决这道题时,可以有:

?完整代码:
class Solution {
//找到根结点到指定节点node之间路径上的所有结点,存储到栈中
private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
if (root == null || node == null) {
return false;
}
stack.push(root);
if (root == node) {
return true;
}
boolean ret1 = getPath(root.left, node, stack);
if (ret1) {
return true;
}
boolean ret2 = getPath(root.right, node, stack);
if (ret2) {
return true;
}
//当前根结点不是,根的左右子树都不是
stack.pop();
return false;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){
Stack<TreeNode> stack1 = new Stack<>();
getPath(root,p,stack1);
Stack<TreeNode> stack2 = new Stack<>();
getPath(root,q,stack2);
int size1 = stack1.size();
int size2 = stack2.size();
if(size1 > size2){
int tmp = size1 - size2;
while(tmp != 0){
stack1.pop();
tmp--;
}
}else{
int tmp = size2 - size1;
while(tmp != 0){
stack2.pop();
tmp--;
}
}
while(!stack1.empty() && !stack2.empty()){
if(stack1.peek() == stack2.peek()){
return stack1.peek();
}else{
stack1.pop();
stack2.pop();
}
}
return null;
}
}

思路:
- 题目要求转换成一个排序的双向链表,并且是一颗二叉搜索树,所以我们要使用中序遍历,遍历树
- 然后利用每个节点的left和right作为前驱后继进行连接?
?
public class Solution {
TreeNode prev = null;
public void ConvertChild(TreeNode root){
if (root == null){
return;
}
ConvertChild(root.left);
root.left = prev;
if (prev != null){
prev.right = root;
}
prev = root;
ConvertChild(root.right);
}
public TreeNode Convert(TreeNode pRootOfTree){
if (pRootOfTree == null){
return null;
}
ConvertChild(pRootOfTree);
TreeNode head = pRootOfTree;
while(head.left != null){
head = head.left;
}
return head;
}
}

思路:

?完整代码:
class Solution {
public int preIndex = 0;
public TreeNode buildTreeChild(int[] preorder, int[] inorder,int inBegin,int inEnd){
if(inBegin > inEnd){
return null;
}
TreeNode root = new TreeNode(preorder[preIndex]);
int rootIndex = findInorderIndex(inorder,preorder[preIndex],inBegin,inEnd);
preIndex++;
root.left = buildTreeChild(preorder,inorder, inBegin, rootIndex-1);
root.right = buildTreeChild(preorder,inorder,rootIndex+1, inEnd);
return root;
}
//找到根结点的位置
private int findInorderIndex(int[] inorder,int val,int inBegin,int inEnd){
for(int i = inBegin; i <= inEnd; i++){
if(inorder[i] == val){
return i;
}
}
return -1;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeChild(preorder,inorder,0,inorder.length-1);
}
}
同理这道题也是一样的道理:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
只不过变成了后序序列倒着是根结点:
class Solution {
public int postIndex;
public TreeNode buildTreeChild(int[] postorder, int[] inorder,int inBegin,int inEnd){
if(inBegin > inEnd){
return null;
}
TreeNode root = new TreeNode(postorder[postIndex]);
int rootIndex = findInorderIndex(inorder,postorder[postIndex],inBegin,inEnd);
postIndex--;
root.right = buildTreeChild(postorder,inorder,rootIndex+1, inEnd);
root.left = buildTreeChild(postorder,inorder, inBegin, rootIndex-1);
return root;
}
private int findInorderIndex(int[] inorder,int val,int inBegin,int inEnd){
for(int i = inBegin; i <= inEnd; i++){
if(inorder[i] == val){
return i;
}
}
return -1;
}
public TreeNode buildTree(int[] inorder,int[] postorder) {
postIndex = postorder.length-1;
return buildTreeChild(postorder,inorder,0,inorder.length-1);
}
}

?
?思路:
每颗小子树走完都会加一个右括号,当然除了根结点剩下的结点都先加一个左括号,变美丽树,并将其打印出来:
class Solution {
public String tree2str(TreeNode root) {
//通过StringBuilder修改字符串
StringBuilder sb = new StringBuilder();
tree2strChild(root,sb);
return sb.toString();
}
private void tree2strChild(TreeNode t,StringBuilder sb){
if(t == null){
return;
}
sb.append(t.val);
//左子树不为空就加个左括号
if(t.left != null){
sb.append("(");
tree2strChild(t.left,sb);
sb.append(")");
}else{
if(t.right == null){
return;
}else{
sb.append("()");
}
}
if(t.right == null){
return;
}else{
sb.append("(");
tree2strChild(t.right,sb);
sb.append(")");
}
}
}
十二、非递归实现二叉树的前中后序遍
前序遍历思路:
创建一个栈,将根结点入栈并打印,再遍历root.left,当结点左右子树为空时就出栈并赋值给top记录下来,再继续遍历root.right:
class Solution {
public List<Integer> preorderTraversal(TreeNode root){
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
System.out.println(cur.val + " ");
ret.add(cur.val);
cur = cur.left;
}
TreeNode top = stack.pop();
cur = top.right;
}
return ret;
}
}
中序遍历同理:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
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.println(cur.val + " ");
list.add(top.val);
cur = top.right;
}
return list;
}
}
后序遍历:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode prev = null;
TreeNode cur = root;
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){
stack.pop();
list.add(top.val);
prev = top;
}else{
cur = top.right;
}
}
return list;
}
}
|