二叉树篇

二叉树理论基础

树的基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
节点的度: 一个节点含有的子树的个数称为该节点的度。如上图,T 节点的度为4
树的度: 一颗树中,最大的节点的度称为树的度。如上图,该树的度为4
叶子节点或终端节点: 度为0的节点称为叶子节点。如上图,T4、T11、T21、T22、T31、T32、T33为叶子节点
双亲节点或父节点: 若一个节点含有子节点,则这个节点称为其子节点的 父节点。如上图,T 节点是 T4 节点的父节点
孩子节点或子节点: 一个节点含有的子树的根节点称为该节点的子节点。如上图,T4 节点是 T 节点的子节点
根节点: 一颗树中,没有双亲节点的节点称为根节点。如上图,T 节点为根节点
节点的层次: 从根开始定义,根为第1层,根的子节点为第二层,以此类推。如上图,该树有3层
节点的深度: 某节点层次是第几层,则它的深度是多少。如上图,T 节点深度为1,T1 节点深度为2
树的高度: 树中节点的最大层次。如上图,树的高度为3
非终端节点或分支节点: 度不为0的节点。如上图,T、T1、T2、T3 为分支节点
兄弟节点: 父亲节点相同的节点互称为兄弟节点。如上图,T1、T2、T3、T4 互称为兄弟节点
堂兄弟节点: 双亲在同一层次的节点互称为堂兄弟节点。如上图,T11、T21 互称为堂兄弟节点
节点的祖先: 从根节点到该节点所经过分支上的所有节点都称为该节点的祖先。如上图,T、T1 节点都为 T11 节点的祖先
子孙: 以某节点为根的子树中,任意节点都称为该节点的子孙。如上图,该树中除 T 节点其它节点都是 T 节点的子孙
森林: 由 m(m>=0)棵互不相交的树的集合称为森林。

二叉树:

1
2
3
4
当集合为空时,该二叉树称为空二叉树。
在二叉树中,一个元素也称为一个结点。
每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
二叉树的子树有左右之分,其次子树的次序不能颠倒,因此二叉树是有序树。

二叉树的种类:

满二叉树: 一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树。性质: 如果一个二叉树的层数是k,且节点数是 2k-1,则它就是满二叉树。

完全二叉树:在完全二叉树中,除了最后一层的节点没填满以外,其余每层节点数都达到最大值,并且下面一层的节点数都集中在最左边的若干位置,

二叉搜索树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

平衡二叉搜索树:空树,或者它的左右两个树的高度差的绝对值不超过1,并且左右子树都是平衡二叉树

二叉树的存储方式:

链式存储:用指针,(节点元素,左指针 ,右指针)

顺序存储:用数组,用数组下标记录左孩子右孩子的位置

二叉树的遍历方式:

深度优先遍历:先往深处走,一层一层遍历

  • 前序遍历:中左右
  • 中序遍历:左中右
  • 后序遍历:左右中

广度优先遍历:

  • 层次遍历

java链式存储二叉树节点的定义方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TreeNode {//定义了一个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;
}
}

144.二叉树的前序,中序,后续遍历(递归)

145.后序遍历 94.中序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
recursive(root, res);
return res;
}

public void recursive(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
res.add(root.val);
recursive(root.left, res);
recursive(root.right, res);
}
}
//前序遍历是,中,左,右
//中序遍历是,左,中,右
//后序遍历是,左,右,中
//只需要在递归函数中 把递归调用的顺序改变就行了

144,二叉树的前序,中序,后序遍历(迭代法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//先序遍历,中左右
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<TreeNode> s = new Stack<>();
s.push(root);
while (!s.isEmpty()) {
TreeNode n = s.pop();
res.add(n.val);
if (n.right != null) {
s.push(n.right);
}
if (n.left != null) {
s.push(n.left);
}
}
return res;
}
}
//判断栈空使用isEmpty()函数,不能用!=null来判断
//用栈模拟,进栈顺序为先右后左

//后续遍历,左右中
//需要在先序遍历级基础上,生成中右左,再反转为左右中
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<TreeNode> s = new Stack<>();
s.push(root);
while (!s.isEmpty()) {
TreeNode n = s.pop();
res.add(n.val);
if (n.left != null) {
s.push(n.left);
}
if (n.right != null) {
s.push(n.right);
}
}
Collections.reverse(res);
return res;
}
}
//集合的反转不能直接调用reverse();
//Collections.reverse();

//中序遍历,左中右
//思路就是,用临时变量temp记录当前的节点
//不为空入栈,左移,到最左边的节点后
//为空弹出栈,右移
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<TreeNode> s = new Stack<>();
TreeNode temp = root;
while (temp != null || !s.isEmpty()) {
if (temp != null) {
s.push(temp);
temp = temp.left;
}
if (temp == null) {
TreeNode n = s.pop();
res.add(n.val);
temp = n.right;
}
}
return res;
}
}

102.二叉树的层序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);// 入队
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode n = queue.poll();
list.add(n.val);
if (n.left != null) {
queue.offer(n.left);
}
if (n.right != null) {
queue.offer(n.right);
}
}
res.add(list);
}
return res;

}
}
//队列模拟,入队,确定队长,循环出队和遍历当前节点的左右子树即可

//递归的方法,还不是很懂
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
levelOrderHelper(root, 0, result);
return result;
}

private void levelOrderHelper(TreeNode node, int level, List<List<Integer>> result) {
if (node == null) {
return;
}

if (level == result.size()) {
result.add(new ArrayList<>());
}
//如果当前层级等于列表结果的大小,则说明当前层级的列表还不存在,需要创建一个新的列表,并且将其添加到结果列表当中
result.get(level).add(node.val);
//获取当前层级的列表,并且添加对应的元素值
levelOrderHelper(node.left, level + 1, result);
levelOrderHelper(node.right, level + 1, result);
}
}

107.二叉树的层序遍历II

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}

Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
int size = q.size();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode n = q.poll();
list.add(n.val);
if (n.left != null) {
q.offer(n.left);
}
if (n.right != null) {
q.offer(n.right);
}
}
res.add(list);
}
List<List<Integer>> ans = new ArrayList<>();
for (int i = res.size() - 1; i >= 0; i--) {
ans.add(res.get(i));
}
return ans;
}
}
//层序遍历再反转即可

199.二叉树的右视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//层序遍历的基础上,记录循环出队的最后一个元素即可
for (int i = 0; i < size; i++) {
TreeNode n = queue.poll();
list.add(n.val);
if (n.left != null) {
queue.offer(n.left);
}
if (n.right != null) {
queue.offer(n.right);
}
if (i == size - 1) {
ans.add(n.val);
}
}

637.二叉树的层平均值

1
//在层遍历的基础上,统计和在平均一下,没啥好说的

429.N叉树的层序遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> children;

public Node() {}

public Node(int _val) {
val = _val;
}

public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
};
*/

class Solution {
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) {
return res;
}
Queue<Node> queue = new LinkedList<>();
queue.offer(root);// 入队
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
Node n = queue.poll();
list.add(n.val);
for (Node c : n.children) {
queue.offer(c);
}
//二叉树这里是遍历左右子树,N叉树这里直接循环遍历所有的子树
}
res.add(list);
}
return res;
}
}
//

515.在每个树中找出最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) {
return ans;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
int size = q.size();
int max = Integer.MIN_VALUE;
for (int i = 0; i < size; i++) {
TreeNode n = q.poll();
max = n.val > max ? n.val : max;
if (n.left != null) {
q.offer(n.left);
}
if (n.right != null) {
q.offer(n.right);
}
}
ans.add(max);
}
return ans;
}
}

116.(117)填充每个节点的下一个右侧节点指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public Node connect(Node root) {
if (root == null) {
return null;
}
Queue<Node> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
Node n = q.poll();
if (i < size - 1) {//最右边不连接
n.next = q.peek();
}
if (n.left != null) {
q.offer(n.left);
}
if (n.right != null) {
q.offer(n.right);
}
}
}
return root;
}
}
//队列的peek()方法,获得队首的值,但是不出队
//最末尾的不连接,链接前size-1个元素的值即可

104.二叉树的最大深度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//层序遍历,len值++
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
int len = 0;
while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode n = q.poll();
if (n.left != null) {
q.offer(n.left);
}
if (n.right != null) {
q.offer(n.right);
}
}
len++;
}
return len;
}
}

111.二叉树的最小深度

1
2
3
4
//层序遍历,当同时满足,节点的左右节点都为null时候,返回此时的len值
if (n.left == null && n.right == null) {
return len;
}

226.翻转二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//递归,翻转左右子节点
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null)
return root;
invertTree(root.left);
invertTree(root.right);
swap(root);
return root;
}
public void swap(TreeNode node) {
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
}
//递归我还是很不熟练,自己调用自己

101.对称二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return compare(root.left,root.right);
}
public boolean compare(TreeNode left,TreeNode right){
if(left == null && right!=null) return false;
if(left != null && right == null) return false;
if(left == null && right == null) return true;
if(left.val != right.val) return false;
return compare(left.left,right.right) && compare(left.right,right.left);
}
}
//递归判断,判断左右节点非空,再判断左对右,右对左的数值是否相等

104.完全二叉树的节点个数

1
2
3
4
5
6
7
//层序遍历,记录count值,或者递归
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
return countNodes(root.left) + countNodes(root.right) + 1;
}
}

110.平衡二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
int left = getHeight(root.left);
int right = getHeight(root.right);
if(Math.abs(left-right)>1) {
return false;
}
return isBalanced(root.left) && isBalanced(root.right);

}
public int getHeight(TreeNode node){
if(node == null) return 0;
int left = getHeight(node.left);
int right = getHeight(node.right);
return Math.max(left,right) +1;
}
}
//定义一个计算当前二叉树高度的函数getheight
//满足平衡二叉树的定义:
//1.左右节点的高度差不超过1
//2.当前节点的左右节点都是为平衡二叉树
//知道这两个原理了,就很好写代码,不知道的话,就写不出来

257.二叉树的所有路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
if (root == null) {
return res;
}
dfs(root, "", res);
return res;
}

public void dfs(TreeNode node, String path, List<String> res) {
if (node != null) {
// 当前节点不为空,拼接节点到当前路劲下
path += Integer.toString(node.val);
// 如果节点到为叶子结点,添加到当前路径中
if (node.left == null && node.right == null) {
res.add(path);
} else {// 非叶子节点,添加路径符号,继续遍历左右子树
path += "->";
dfs(node.left, path, res);
dfs(node.right, path, res);
}
}
}
}
//重要点是记录当前的路径