前言
本篇博客是参考 学习视频 的笔记
1.树
1.什么是树?
树: 由n(n>=1)个有限结点组成一个具有层次关系的集合。
特点:
- 每个结点有零个或多个子结点;
- 没有父结点的结点为根结点;
- 每一个非根结点只有一个父结点;
- 每个结点及其后代结点整体上可以看做是一棵树,称为当前结点的父结点的一个子树;
相关术语:
- 结点的度:
一个结点含有的子树的个数
A结点的度为6 - 叶结点(终端结点):
度为0的结点
如:B、H、I、J、K、N、O、M - 分支结点(非终端结点):
度不为0的结点 - 结点的层次:
从根结点开始,根结点的层次为1,之后每一层+1
如:A的层次为1,N的层次为4 - 结点的层序编号:
将树中的结点,按照从上层到下层,同层从左到右的次序排成一个线性序列,把他们编成连续的自然数
如:A:0、B:1、H:8 - 树的度:
树中所有结点的度的最大值
此树中A结点的度最大为6,故树的度为6 - 树的高度(深度):
树中结点的最大层次
此树中N、O结点层次最大,为4,故树的高度为4 - 森林:
多个互不相交的树的集合。将一个非空树的根节点去掉就形成了森林,给一个森林加一个统一的根节点即形成树
- 孩子结点:
一个结点的直接后继结点
如:I、H都是C的孩子结点 - 双亲结点(父结点):
一个结点的直接前驱
如:C是H、I的双亲结点 - 兄弟结点:
同一双亲结点的孩子结点
如:I、H是兄弟结点
树的表示:
双亲表示法:
孩子表示法1: 不管树的度为多少,都用最大的度来表示(浪费空间)
孩子表示法2: degree表示当前节点的度,有几个度就有几个孩子节点(结构较为复杂)
孩子表示法3: 将所有结点存入顺序链表
孩子兄弟表示法:
2.二叉树
二叉树: 度不超过2的树
满二叉树: 一个二叉树,如果每一个层的结点的度都达到最大值,则这个二叉树就是满二叉树
完全二叉树: 叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
完全二叉树的数组表示法:
A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
推论1: 对于位置为 k 的结点,左子结点 = 2*k + 1,右子节点 = 2*(k+1)
推论2: 最后一个非叶子结点的位置为 (N/2) - 1 , N 为数组长度
注意: 只有完全二叉树会用数组表示法,一般的二叉树都是用下列表示方法:
data | leftchild | rightchild |
---|
3.二叉树的遍历:
前序遍历: 先访问根结点,然后再访问左子树,最后访问右子树
遍历结果:A B D H I E J C F G
中序遍历: 先访问左子树,再访问根节点,最后访问右子树
遍历结果:H D I B J E A F C G
后序遍历: 先访问左子树,再访问右子树,最后访问根节点
遍历结果: H I D J E B F G C A
层序遍历: 从根节点(第一层)开始,依次向下,获取每一层所有结点的值
遍历结果:A B C D E F G H I J
2.习题
二叉树的中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
思路1——递归实现
左——根——右
先访问玩左子树,再访问根节点,最后访问右子树(很符合递归的思想)。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//返回结果
List<Integer> res = new ArrayList<>();
//递归入口
accessTree(root,res);
return res;
}
public void accessTree(TreeNode root,List<Integer> res){
//终止条件
if (root == null) return;
//中序遍历: 左-根-右
accessTree(root.left,res);
res.add(root.val);
accessTree(root.right,res);
}
}
思路2——循环迭代(栈)
内部 while 第一次循环完
将栈内元素存入列表
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//存储遍历后的数字
List<Integer> res = new ArrayList<>();
//用来辅助中序遍历
Deque<TreeNode> stack = new LinkedList<>();
while (root != null || !stack.isEmpty()){
//先将左子树入栈,然后出栈交给list(已经反序)
while (root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
//开始遍历右子树(为空的时候,就继续弹出,不然就入栈)
root = root.right;
}
return res;
}
}
二叉树前序遍历
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
思路1——递归实现
根——左——右
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
accessTree(root,res);
return res;
}
public void accessTree(TreeNode root,List<Integer> res){
if (root == null) return;
//中序遍历: 根-左-右
res.add(root.val);
accessTree(root.left,res);
accessTree(root.right,res);
}
}
思路2——循环迭代
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
//存储遍历后的数字
List<Integer> res = new ArrayList<>();
//用来辅助中序遍历
Deque<TreeNode> stack = new LinkedList<>();
while (root != null || !stack.isEmpty()){
//先将根节点存入链表,再将左子树入栈,然后出栈交给list(已经反序)
while (root != null){
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
//开始遍历右子树
root = root.right;
}
return res;
}
}
二叉树的后序遍历
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
思路1——递归
左——右——根
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
/**
递归
*/
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
accessTree(root,res);
return res;
}
private void accessTree(TreeNode root, List<Integer> res){
if (root == null) return;
accessTree(root.left,res);
accessTree(root.right,res);
res.add(root.val);
}
}
思路2——循环迭代
与前中序遍历有所不同:访问完左子树后,要访问右子树,但是左子树没有指针直接连接右子树,需要先经过最近的连接着右子树的根节点,再去访问右子树。
有右子树的节点将被反复压栈
因此代码有所不同:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode prevAccess = null;//指向前一个节点,判定当前结点右子树是否遍历过了
while (root != null || !stack.isEmpty()){
while (root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
//新增判断,若当前节点没有右节点 或 当前右节点是否已经遍历过了,如果都没有 就 将结点值加入链表
if (root.right == null || root.right == prevAccess){
res.add(root.val);
prevAccess = root;
root = null;//root置空
}else {//否则就继续压栈,遍历右子树
stack.push(root);
root = root.right;
}
}
return res;
}
}
对称二叉树
给你一个二叉树的根节点 root , 检查它是否轴对称。
思路1——递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) return true;
//递归入口
return deepCheck(root.left, root.right);
}
private boolean deepCheck(TreeNode left, TreeNode right){
if (left == null && right == null){
return true;
}
if (left == null || right == null){
return false;
}
if (left.val != right.val){
return false;
}
return deepCheck(left.left, right.right) && deepCheck(left.right, right.left);
}
}
思路2——循环迭代(队列)
先入队
再取出对比:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
TreeNode u = root.left;
TreeNode v = root.right;
if (root == null || (u == null && v == null)){
return true;
}
//入队
q.offer(u);
q.offer(v);
while (!q.isEmpty()){
//出队
u = q.poll();
v = q.poll();
if (u == null && v == null){
continue;
}
if ((u == null || v == null) || (u.val != v.val)){
return false;
}
//在将该节点的左右子节点入队
q.offer(u.left);
q.offer(v.right);
q.offer(u.right);
q.offer(v.left);
}
return true;
}
}
二叉树最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数
思路——递归
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if (root == null){
return 0;
}else {
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;//深度
while (!queue.isEmpty()){
//用来记录本层级的节点是否已经处理完
int size = queue.size();
while (size > 0){
TreeNode node = queue.poll();
if (node.left != null){
queue.offer(node.left);
}
if (node.right != null){
queue.offer(node.right);
}
size--;
}
depth++;
}
return depth;
}
}
平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
return helper(root) != -1;
}
public int helper(TreeNode root){
if (root == null) return 0;
//统计左树和右树的高度
int left = helper(root.left);
int right = helper(root.right);
if (left == -1 || right == -1 || Math.abs(left - right) > 1){
return -1;
}
return Math.max(left,right) + 1;
}
}
反转二叉树
思路1——递归调用
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
invertTree(root.left);
invertTree(root.right);
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return root;
}
}