一. 二叉树
节点的度: 一个节点含有的子树的个数称为该节点的度;
树的度: 一棵树中,最大的节点的度称为树的度;
叶子节点或终端节点: 度为0的节点称为叶节点;
双亲节点或父节点: 若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点: 一个节点含有的子树的根节点称为该节点的子节点;
根结点: 一棵树中,没有双亲结点的结点;
节点的层次: 从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度: 树中节点的最大层次;
完全二叉树:节点从左到右依次遍历没有间隔。
满二叉树:对于一个层数为K的满二叉树,节点总数为 (2^k)-1
对于一个有n个节点的满二叉树:其深度(层数)K可表示为:
k
=
l
o
g
2
n
+
1
.
\ k = \ log _2^{n+1}\,.
k= log2n+1.
注意:
- 二叉树的结点个数=分支数+1;
- 分支数=度为2的节点数*2+度为1的节点数*1;
- 总节点数=度为2的节点数+度为1的节点数+度为0的节点数;
- 在完全二叉树中,度为2的节点数=度为0的节点数(叶子节点)-1;且度为1的节点数只能为0或者1
二叉树系列习题
1 二叉树的遍历:前中后序遍历
(1)二叉树的前序遍历
根左右
链接:前序遍历
1)递归:
void preOrder(TreeNode root){
if(root == null) {
return;
}
System.out.print(root.value+" ");
preOrder(root.left);
preOrder(root.right);
}
2)非递归:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
List<Integer> list = new ArrayList<>();
TreeNode cur=root;
while(cur!=null || !stack.empty()){
while(cur!=null){
stack.push(cur);
list.add(cur.val);
cur=cur.left;
}
cur = stack.pop();
cur = cur.right;
}
return list;
}
}
(2)二叉树的中序遍历
左根右
链接:中序遍历
1)递归:
void inOrder(TreeNode root){
if(root==null){
return;
}
inOrder(root.left);
System.out.print(root.value+" ");
inOrder(root.right);
}
2)非递归:
public List<Character> inorder(TreeNode root) {
List<Character> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur!=null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
list.add(cur.value);
cur = cur.right;
}
return list;
}
(3)二叉树的后序遍历
左右根
链接:后序遍历
递归:
void postOrder(TreeNode root){
if(root==null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.value+" ");
}
非递归:
public List<Character> postorder(TreeNode root) {
List<Character> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode prev = null;
while(cur!=null || !stack.empty()){
while(cur!=null){
stack.push(cur);
cur=cur.left;
}
cur = stack.peek();
//prev记录节点,是为了防止重复打印
//cur.left cur.right都为null时 cur才可以出栈
if(cur.right==null || cur.right==prev){
stack.pop();
System.out.print(cur.value+" ");
list.add(cur.value);
prev=cur;
cur=null;
}else{
cur = cur.right;
}
}
return list;
}
设置一个prev是为了防止重复打印,因为当右节点不为空时,当前节点cur是不能出栈的,此时需要跳到右节点,当右边的节点全部打印完时,通过stack.peek()又取到了cur,而如果不加 cur.right==prev这条判断,那么又会对cur的右节点打印一遍,所以我们需要用prev记录上一次打印的节点,来防止重复打印。
2 构造二叉树
(1)简单粗暴构造
class TreeNode {
char value;
TreeNode left;
TreeNode right;
public TreeNode(char value) {
this.value = value;
}
}
public TreeNode buildTree() {
TreeNode root = 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');
root.left = B;
root.right = C;
B.left = D;
B.right = E;
E.right = H;
C.left = F;
C.right = G;
return root;
}
(2)通过前序遍历字符串构造二叉树
先序遍历字符串:ABC##DE#G##F###
其中“#”表示的是空格,
空格字符代表空树,即遇到#时跳过创建节点就可以了。每个节点都有两个分支。
链接:字符串构造二叉树并遍历
import java.util.Scanner;
public class Main{
static class TreeNode{
TreeNode left;
TreeNode right;
char val;
public TreeNode(char val){
this.val = val;
}
}
static int i=0;
public static TreeNode buildTree(String str){
TreeNode root = null;
if(str.charAt(i)!='#'){
root = new TreeNode( str.charAt(i++));
root.left = buildTree(str);
root.right = buildTree(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);
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String str = sc.nextLine();
TreeNode root = buildTree(str);
inOrder(root);
}
}
}
注意:
如果遇到#号需要创建空树时,由TreeNode root = null;
就直接赋值为null了,且该节点也不会执行到root.left,root.right.相当于一个递归的返回条件。
(3)根据一棵树的前序遍历与中序遍历构造二叉树
根据前序遍历找每棵树的头节点,然后在中序遍历中找到头节点的位置,然后划分区间。
链接:前序中序遍历创建二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder==null || inorder==null){
return null;
}
return buildTreeChild(preorder,inorder,0,inorder.length-1);
}
int i=0;
public TreeNode buildTreeChild(int[] preorder, int[] inorder,int inbegin,int inend){
if(inbegin>inend){
return null;
}
TreeNode root = new TreeNode(preorder[i]);
int index = findIndexOfInorder(inorder,inbegin,inend,preorder[i]);
i++;
root.left = buildTreeChild(preorder,inorder,inbegin,index-1);
root.right = buildTreeChild(preorder,inorder,index+1,inend);
return root;
}
public int findIndexOfInorder(int[] inorder,int inbegin,int inend,int val){
for(int i=0;i<inorder.length;i++){
if(inorder[i]==val){
return i;
}
}
return -1;
}
}
(4)根据一棵树的中序和后序遍历,创建一颗二叉树
链接:中序和后序遍历创建二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int i =0;
public TreeNode buildTree(int[] inorder, int[] postorder) {
i = postorder.length-1;
return buildTreeChild(inorder,postorder,0,inorder.length-1);
}
public TreeNode buildTreeChild(int[] inorder,int[] postorder,int inbegin,int inend){
if(inbegin>inend){
return null;
}
TreeNode root = new TreeNode(postorder[i]);
int index = findIndexOfInorder(inorder,inbegin,inend,postorder[i]);
i--;
//中后序遍历要先遍历右树
root.right = buildTreeChild(inorder,postorder,index+1,inend);
root.left = buildTreeChild(inorder,postorder,inbegin,index-1);
return root;
}
public int findIndexOfInorder(int[] inorder,int inbegin,int inend,int val){
for(int i=0;i<inorder.length;i++){
if(inorder[i]==val){
return i;
}
}
return -1;
}
}
3 返回节点的总个数
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
if(root==null){
return 0;
}
return countNodes(root.left)+countNodes(root.right)+1;
}
}
4 返回叶子节点个数
int getLeafSize(TreeNode root){
if(root==null){
return 0;
}else if(root.left==null&&root.right==null){
return 1;
}
return getLeafSize(root.left)+getLeafSize(root.right);
}
5 返回第K层节点的个数
int getKLevelSize(TreeNode root,int k){
if(root==null){
return 0;
}
if(k==1){
return 1;
}
return getKLevelSize(root.left,k-1)+
getKLevelSize(root.right,k-1);
}
6 前序遍历的形式去查找val值
TreeNode find(TreeNode root, int val){
if(root==null){
return null;
}
if(root.value==val){
return root;
}
TreeNode ret = find(root.left,val);
if(ret!=null){
return ret;
}
ret = find(root.right,val);
if(ret!=null){
return ret;
}
return null;
}
7 判断树t是否为树s的子树
判断树t是否为树s的子树需要判断两棵树是否相同
链接:两棵树是否相同
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
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);
}
}
链接:另一颗树的子树
boolean isSubtree(TreeNode s, TreeNode t){
if(s==null || t == null){
return false;
}
if(isSameTree(s,t)){
return true;
}
return isSubtree(s.left,t)||isSubtree(s.right,t);
}
8 求一棵树的最大深度
总深度=左子树高度+1或右子树高度+1
求一棵树的最大深度,递归,将具体问题抽象化,表述清楚思路
链接:最大深度
int maxDepth(TreeNode root){
if(root==null){
return 0;
}
int leftHight = maxDepth(root.left);
int rightHight = maxDepth(root.right);
return leftHight>rightHight?leftHight+1:rightHight+1;
}
9 判断一棵树是否为平衡二叉树
判断一棵树是否为平衡二叉树需要用到一棵树的最大深度。
平衡二叉树:两颗子树高度相差不大于1
链接:平衡二叉树
boolean isBalanced(TreeNode root){
if(root==null){
return true;
}
int a=maxDepth(root.left);
int b=maxDepth(root.right);
if(Math.abs(a-b)>1){
return false;
}
return isBalanced(root.left)&&isBalanced(root.right);
}
10 判断两棵树是否为对称二叉树
判断两棵树是否为对称二叉树,即判断它得两个子树是否为对称的。
链接:对称二叉树
public 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.value!=rightTree.value){
return false;
}
return isSymmetricChild(leftTree.left,rightTree.right)&&
isSymmetricChild(leftTree.right,rightTree.left);
}
public boolean isSymmetric(TreeNode root){
if(root==null){
return true;
}
return isSymmetricChild(root.left,root.right);
}
11 层序遍历
二叉树的层序遍历:从根节点出发,从左到右,依次遍历每一层。用到了队列(先进先出)来记录节点。
(1)层序打印遍历
public void binaryTreeLevelOreder(TreeNode root){
Queue<TreeNode> queue = new LinkedList<>();
if(root!=null){
queue.offer(root);
}
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.print(cur.value+" ");
if(cur.left!=null){
queue.offer(cur.left);
}
if(cur.right!=null){
queue.offer(cur.right);
}
}
}
(2)层序遍历并将每一层保存到List中
链接:层序遍历
public List<List<Character>> levelOrder(TreeNode root) {
List<List<Character>> ret = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
if(root != null) {
queue.offer(root);
}
while (!queue.isEmpty()) {
int size = queue.size();//4
List<Character> list = new ArrayList<>();
while (size > 0 ) {
TreeNode cur = queue.poll();
System.out.print(cur.value+" ");
list.add(cur.value);
size--;//0
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
ret.add(list);
}
return ret;
}
12 判断一棵树是不是完全二叉树
链接:完全二叉树验证
public boolean isCompleteTree(TreeNode root){
Queue<TreeNode> queue = new LinkedList<>();
if(root!=null){
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()){
if(queue.poll()!=null){
return false;
}
}
return true;
}
13 二叉搜索树转换为排序双向链表
二叉搜索树:左节点<根节点<右节点
将二叉搜索树用中序遍历正好能得到一个有序的结构,此处left相当于链表中的prev,right相当于链表中的next,prev在双向链表中的第一个为null,next在最后一个为null (默认不赋值就是为null)。
题目链接: 转换为排序双向链表
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeNode prev=null;//prev记录上一个节点
public void ConvertChild(TreeNode root){
if(root==null){
return;
}
ConvertChild(root.left);
root.left=prev;
if(prev!=null){
//上一个节点的next为当前节点
prev.right=root;
}
prev = root;
ConvertChild(root.right);
}
public TreeNode Convert(TreeNode root){
//放入ConvertChild中完成双向链表的转化
ConvertChild(root);
//寻找链表的头节点,由原来的root根节点向左寻找双向链表的头节点
TreeNode head = root;
while(head!=null &&head.left!=null){
head = head.left;
}
return head;
}
}
14 给定一个二叉树,找到该树中两个节点的最近公共祖先
一个节点也可以是它自己的祖先
分三种情况,1:两个节点在两边 2:都在左边 3:都在右边
链接:公共祖先
public TreeNode findCommonAncestor(TreeNode root,TreeNode p,TreeNode q){
if(root==null){
return null;
}
if(root==p || root==q){
return root;
}
TreeNode leftTree =findCommonAncestor(root.left,p,q);
TreeNode rightTree = findCommonAncestor(root.right,p,q);
if(leftTree!=null&&rightTree!=null){
return root;
}
if(leftTree!=null){
return leftTree;
}
if(rightTree!=null){
return rightTree;
}
return null;
}
15 采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串
空节点则用一对空括号 “()” 表示。而且你需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
输入: 二叉树: [1,2,3,null,4]
输出:“1(2()(4))(3)”
题目链接:转换成一个由括号和整数组成的字符串
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public String tree2str(TreeNode t) {
StringBuilder sb = new StringBuilder();
convert(sb,t);
return sb.toString();
}
public void convert(StringBuilder sb, TreeNode t){
if(t==null){
return;
}
sb.append(t.val);
if(t.left==null){
if(t.right!=null){
sb.append("()");
}
}else{
sb.append("(");
convert(sb,t.left);
sb.append(")");
}
if(t.right!=null){
sb.append("(");
convert(sb,t.right);
sb.append(")");
}
}
}