1.判断是否是完全二叉树
- 思路:
- 确定一个队列,只要树的根非空就进队列;
- 队列只要非空,就弹出对头元素给cur节点;
- cur非空就将cur的左子树节点和右边节点入栈;
- 只要cur为空,直接跳出循环;
- 接着判断队列是否为空,非空就弹出对头元素;
弹出队头只会出现俩种情况:- 对于完全二叉树,队列所有元素都是null;
- 对于不是完全二叉树,队列里面的元素就是非null的元素;
- 因此接下来的条件就是只要对头弹出非null的元素,之间返回false;
- 否则就一直到队列为空,说明弹出的队列元素就是null,就返回true;
- 完全二叉树的情况,队列,最后全是null
2.非完全二叉树,队列里面出现了非null的元素
- 完全二叉树的情况,队列,最后全是null
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(Node root) {
if(root == null) {
return true;
}
Queue<Node> queue = new LinkedList<>();//队列
queue.offer(root);
while (!queue.isEmpty()) {
Node cur = queue.poll();
if(cur != null){
queue.offer(cur.left);
queue.offer(cur.right);
}else{
break;
}
}
//判断队列剩下的元素 是否有非空的元素
while(!queue.isEmpty()){
Node cur = queue.peek();
if(cur != null) {
return false;
}else {
queue.poll();
}
}
return true;
}
//完整测试代码
import java.lang.Math;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/*
孩子表示法
*/
class Node {
char val;
Node left;
Node right;
//构造方法
public Node(char val) {
this.val = val;
}
}
public class BinaryTree {
//构造一颗树
public Node createTree(){
Node A = new Node('A');
Node B = new Node('B');
Node C = new Node('C');
Node D = new Node('D');
Node E = new Node('E');
Node F = new Node('F');
Node G = new Node('G');
//Node H = new Node('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
//E.right = H;
C.left = F;
C.right = G;
return A;
}
// 判断一棵树是不是完全二叉树
boolean isCompleteTree(Node root) {
if(root == null) {
return true;
}
Queue<Node> queue = new LinkedList<>();//队列
queue.offer(root);
while (!queue.isEmpty()) {
Node cur = queue.poll();
if(cur != null){
queue.offer(cur.left);
queue.offer(cur.right);
}else{
break;
}
}
//判断队列剩下的元素 是否有非空的元素
while(!queue.isEmpty()){
Node cur = queue.peek();
if(cur != null) {
return false;
}else {
queue.poll();
}
}
return true;
}
}
//测试类
class TestDemo{
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();//创建对象
//构造一棵树,通过调用BinaryTree类里面的createTree()方法
Node root = binaryTree.createTree();
System.out.println("是否是完全二叉树: "+binaryTree.isCompleteTree(root));
}
}
2.二叉树的最近公共祖先
-
公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
-
思路:
1.对于一棵二叉树,如果它的根都为空,那么也就不存在什么公共祖先的说法,直接返回null;
2.如果p,q都是指向根节点(同一个节点),那么祖先就是这个根节点;
3.那么接下来根的情况已经考虑完,就利用子问题的思想,同样遍历根的左子树和右子树;
4.那么就会有下面图片的几种情况- p,q一个在左子树,一个在右边子树,他们的公共祖先肯定是root;
- p,q都在左子树,祖先就是左节点它本身§,也就是图片第二种情况,返回root.eft;
- 同样它俩全在右子树,也就是第三种情况返回root.right;
- 最后遍历完毕,都有没有找到p,q,直接返回null;
-
p,q可能的情况
//二叉树的最近公共祖先
public TreeNode lowestCommonAncestor(Node root, Node p, Node q) {
if(root == null){
return null;
}
if(root == p || root == q){
return root;
}
Node l = lowestCommonAncestor(root.left,p,q);
Node r = lowestCommonAncestor(root.right,p,q);
if (l != null && r != null){
return root;
}
if (l != null){
return l; }
if (r != null){
return r;
}
return null;
}
3.二叉搜索树转为双向链表(中序遍历的方式)
//二叉搜索树转为双向链表
public Node prev = null;
public void ConvertChild(Node root) {
if(root == null) return ;
ConvertChild(root.left);
root.left = prev;
if(prev != null) {
prev.right = root;
}
prev = root;
ConvertChild(root.right);
}
public Node Convert(Node pRootOfTree) {
if(pRootOfTree == null) return null;
ConvertChild(pRootOfTree);
Node head = pRootOfTree;
while (head.left != null) {
head = head.left;
}
return head;
}
4.二叉树创建字符串
//前序遍历的方式,二叉树转字符串
//被调用方法
public void tree2strChild(Node t,StringBuilder sb) {
if(t == null) return;
sb.append(t.val);
if(t.left == null) {
if(t.right == null) {
return;
}else {
sb.append("()");
}
}else {
sb.append("(");
tree2strChild(t.left,sb);
sb.append(")");
}
if(t.right == null) {
return;
}else {
sb.append("(");
tree2strChild(t.right,sb);
sb.append(")");
}
}
//主调方法
public String tree2str(Node t) {
if(t == null) return null;
StringBuilder sb = new StringBuilder();
tree2strChild(t,sb);
return sb.toString();
}
5.根据前序遍历和中序遍历确定二叉树
//根据前序遍历和中序遍历确定二叉树
public int preIndex = 0;
public Node buildTreeChild(char[] preorder,
char[] inorder,int inbegin,int inend) {
if(inbegin > inend) {
return null;
}
Node root = new Node(preorder[preIndex]);
int inorderIndex = findInoderIndexOfRoot(inorder,
inbegin,inend,preorder[preIndex]);
preIndex++;
root.left = buildTreeChild(preorder,
inorder,inbegin,inorderIndex-1);
root.right = buildTreeChild(preorder,
inorder,inorderIndex+1,inend);
return root;
}
public int findInoderIndexOfRoot(char[] inorder,
int inbegin,int inend,char val) {
for (int j = inbegin; j <= inend; j++) {
if(inorder[j] == val) {
return j;
}
}
return -1;
}
public Node buildTree(char[] preorder, char[] inorder) {
if(preorder == null || inorder == null) {
return null;
}
if(preorder.length == 0 || inorder.length == 0) {
return null;
}
return buildTreeChild(preorder,inorder,
0,inorder.length-1);
}
7.层次遍历二叉树
- 思路
1.首先用一个队列,来保证数据的顺序性;
2.判断树是否为空
3.如果非空,将根就入队,接着判断队列是否为空,非空,就弹出队头元素,赋值给cur节点,然后判断左子树和右边子树,非空就入栈, 并且将cur节点打印;
4.然后直到队列为空,就层次遍历完成;
//层次遍历(加入一个队列)
void levelOrderTraversal(Node root) {
if(root == null) {
return;
}
Queue<Node> queue = new LinkedList<>();//队列对象
queue.offer(root);
while (!queue.isEmpty()) {
Node cur = queue.poll();
System.out.print(cur.val);
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
}
}
- 第二种方法思路
1.首先创建一个List<List> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character),见下图;
2.然后在建立一个rowlist,相当于每一行,这样树的每一层就会对应一行,这样就可以按层打印;
//第二种方法,进行层次遍历
//二叉树的层次遍历(输出是按照一层一层输出的)
public List<List<Character>> levelOrder(Node root) {
List<List<Character>> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character)
if(root == null) {
return ret;
}
//队列用来保证遍历当前顺序性
Queue<Node> queue = new LinkedList<>();//队列
queue.offer(root);
while (!queue.isEmpty()){
//用来存放每一层的数据
List<Character> rowlist = new ArrayList<>();
int count = queue.size();
while(count != 0){
//出栈,cur存放出栈的节点,并且判断后续是否有其他树节点需要进队列
Node cur = queue.poll();//出栈
rowlist.add(cur.val);
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
count--;//用来分层,当一层弹出完毕,count就变为0
}
ret.add(rowlist);
}
return ret;
}
8.输出二叉树最多的层的节点个数
//第一种情况,宽度不包括null节点
- 思路:
1.上面第7题第二种情况我们已经进行了层次遍历,
2.我们将每一层都放在了一个rowlist里面,那么我们现在要找节点最多的,不就相当于只要比较出来,rowlist长度最大的,不就是我们需要的结果吗,因此我们在上面代码的基础上,增加一个max变量,每次得到一个rowlist就计算出来长度和max比较,然后比max大,则将它的长度作为新的max;
3.直到每一次都比较完毕,这个时候我们max里面放的就是最大的;
重:题的扩展,1.那么我们想要知道每一次最左边的是不是也很容易,直接输出每一个rowlist的第一个元素即可;
2.同理,最右边的就是最后一个元素;(大家有兴趣,可以尝试一下,加油!)
- 对于下图的的二叉树来说,最多的层的节点个数是4,因为第一层是节点是1个,第二层节点数是2个,第三层是4个,第四层只有一个;
import java.lang.Math;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/*
孩子表示法
*/
class Node {
char val;
Node left;
Node right;
//构造方法
public Node(char val) {
this.val = val;
}
}
public class BinaryTree {
//构造一颗树
public Node createTree(){
Node A = new Node('A');
Node B = new Node('B');
Node C = new Node('C');
Node D = new Node('D');
Node E = new Node('E');
Node F = new Node('F');
Node G = new Node('G');
Node H = new Node('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
E.right = H;
C.left = F;
C.right = G;
return A;
}
//二叉树的层次遍历(输出是按照一层一层输出的)https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
public List<List<Character>> levelOrder(Node root) {
List<List<Character>> ret = new ArrayList<>();//相当于二维数组,里面放的是字符型数据(Character)
if(root == null) {
return ret;
}
//队列用来保证遍历当前顺序性
Queue<Node> queue = new LinkedList<>();//队列
queue.offer(root);
int max = 0;//用来实现另外一个功能,求出哪层所拥有的节点最多少;
while (!queue.isEmpty()){
//用来存放每一层的数据
List<Character> rowlist = new ArrayList<>();
int count = queue.size();
while(count != 0){
//出栈,cur存放出栈的节点,并且判断后续是否有其他树节点需要进队列
Node cur = queue.poll();//出栈
rowlist.add(cur.val);
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
count--;//用来分层,当一层弹出完毕,count就变为0
}
ret.add(rowlist);
if (rowlist.size() > max){
max = rowlist.size();
}
}
System.out.println("层的最多节点数目: "+max);
return ret;
}}
//测试类
class TestDemo{
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();//创建对象
//构造一棵树,通过调用BinaryTree类里面的createTree()方法
Node root = binaryTree.createTree();
binaryTree.levelOrder(root);//调用求最大节点的方法
}
}
//第二种情况,宽度包括null节点
- 例如下面这颗树,它的层上面节点最多的是在第三层,有四个节点[5,3,null,9];
- 思路:将根节点的的下标定为i,那么它的左节点的下标就是2i,右节点的下标就是2i+1;那么最后的宽度,就是一层最右边的下标减去一层最左边的下标,再加1;
int max = 0;
public int widthOfBinaryTree(Node root) {
isCompleteTree(root);
return max;
}
public void isCompleteTree(Node root) {
if(root == null) {
return;
}
Queue<Node > q =new LinkedList<>();//队列用来保证数据存放的顺序性
LinkedList<Integer> list = new LinkedList<>();//链表用来存储层次遍历过后的二叉树
q.offer(root);//如果根非空,直接入栈
list.add(1);//并且将根的下标弄为1,并且进入链表中
int res = 1;
//开始进入队列,只要队列非空,就弹出队头元素
while (!q.isEmpty()) {
//为了能够将一层数据弹出,因此设置一个count来存储一层的个数
int count = q.size();
//进入循环
for(int i =0;i < count;i++){
Node cur = q.poll();//开始弹出队头元素
Integer curIndex = list.removeFirst();//因为之前已经直接将根的下标放为1,加入到了链表里面,将它移出来,用来标记它子树的下标
if(cur.left != null){
q.offer(cur.left);
list.add(curIndex * 2);
}
if(cur.right != null) {
q.offer(cur.right);
list.add(curIndex * 2 +1);
}
}
//从for中出来,一层已经遍历完毕
//然后计算这一层的宽度,就是链表的最后一个元素减去第一个元素
max = res;
if(list.size() >= 2) {
res = Math.max(res, list.getLast() - list.getFirst() + 1);
max = res;
}
}
}