DS期末复习 二叉树
二叉树
DS期末复习之二叉树
二叉树类的定义
-
二叉树肯定有根节点,就像链表一样,必须有头结点。
class BinaryTree { Node root; public void setRoot(Node root) { this.root = root; } } //结点类 class Node { String name; Node left; Node right; public Node(String name) { this.name = name; } @Override public String toString() { return "Node{" + "name='" + name + '\'' + '}'; } }
二叉树的遍历
-
前序遍历
import java.util.ArrayList; import java.util.List; public class Solution { public List
preorderTraversal(TreeNode root) { List res = new ArrayList (); inorder(root, res); return res; } public void inorder(TreeNode root, List res) { if (root == null) { return; } res.add(root.val); inorder(root.left, res); inorder(root.right, res); } } -
中序遍历
import java.util.ArrayList; import java.util.List; public class Main { public List
inorderTraversal(TreeNode root) { List res = new ArrayList (); inorder(root, res); return res; } public void inorder(TreeNode root, List res) { if (root == null) { return; } inorder(root.left, res); res.add(root.val); inorder(root.right, res); } } -
后序遍历
import java.util.ArrayList; import java.util.List; public class Solution { public List
postorderTraversal(TreeNode root) { List res = new ArrayList (); inorder(root, res); return res; } public void inorder(TreeNode root, List res) { if (root == null) { return; } inorder(root.left, res); inorder(root.right, res); res.add(root.val); } -
层序遍历
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; public class Solution { public ArrayList
PrintFromTopToBottom(TreeNode root) { ArrayList resultList = new ArrayList<>(); if (root == null) { return resultList; } Queue q = new LinkedList<>(); q.add(root); while (!q.isEmpty()) { TreeNode nowNode = q.peek(); q.poll(); resultList.add(nowNode.val); if (nowNode.left != null) { q.add(nowNode.left); } if (nowNode.right != null) { q.add(nowNode.right); } } return resultList; } }
最重要的应该就是层序遍历,用到了广度优先搜索,其实就是每次访问完一个结点,将其子节点都入队,当队列空的时候结束。
二叉树的一些题目
-
二叉树相似判断
最重要的是找出递归终止条件
public boolean Like(Node Aroot, Node Broot) { if (Aroot == null && Broot == null) { return true; } if (Aroot == null || Broot == null){ return false; } return Like(Aroot.left,Broot.left) && Like(Aroot.right,Broot.right); }
-
计算叶子结点个数
还是递归
public int getLeafSize(Node root) { if (root == null) { return 0; } if (root.left == null && root.right == null) { return 1; } else { return getLeafSize(root.left) + getLeafSize(root.right); } }
-
二叉树的最大深度
递归。。。
public int maxDepth(TreeNode root) { if (root == null) { return 0; } return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); }
-
二叉树的最小深度
递归。。。
public int minDepth(TreeNode root) { if (root == null) { return 0; } return Math.min(minDepth(root.left), minDepth(root.right)); }
-
对称二叉树
第一步是终止条件,然后要想对称,必须值val相等,然后递归检查p树的左子树和q树的右子树,p树的右子树和q树的左子树。
public boolean check(TreeNode p, TreeNode q) { if (p == null && q == null) { return true; } if (p == null || q == null) { return false; } return p.val == q.val && check(p.left, q.right) && check(p.right, q.left); }
二叉树的题目很多,而我觉得大部分都能用递归解决,时间紧,先做一些基础的应付考试,等期末结束后刷题的时候再深入学习二叉树。
哈夫曼树
-
哈夫曼树,也就是最优二叉树
哈夫曼树相关的几个名词
路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径。图 1 中,从根结点到结点 a 之间的通路就是一条路径。
路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。图 1 中从根结点到结点 c 的路径长度为 3。
结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权。例如,图 1 中结点 a 的权为 7,结点 b 的权为 5。
结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。例如,图 1 中结点 b 的带权路径长度为 2 * 5 = 10 。
? 树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL” 。
例如图 1 中所示的这颗树的带权路径长度为:
WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3
? 图1 哈夫曼树
- 什么是哈夫曼树?
当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。在图 1 中,因为结点 a 的权值最大,所以理应直接作为根结点的孩子结点。
- 构建哈夫曼树的过程
对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:
- 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
- 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
- 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
?
图 2 哈夫曼树的构建过程
图 2 中,(A)给定了四个结点a,b,c,d,权值分别为7,5,2,4;第一步如(B)所示,找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入;进入(C),重复之前的步骤。直到(D)中,所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。
下面是构建的代码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {5, 3, 8, 2, 9};
Node root = createHuffmanTree(arr);
//测试
preOrder(root);
}
//创建哈夫曼树的方法
public static Node createHuffmanTree(int[] arr) {
//first step
ArrayList nodes = new ArrayList<>();
for (int a : arr) {
nodes.add(new Node(a));
}
while (nodes.size() > 1) {
Collections.sort(nodes);
System.out.println("nodes=" + nodes);
//取出权值最小的两颗二叉树
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//变成一颗新的二叉树
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
//删除刚才两个二叉树
nodes.remove(leftNode);
nodes.remove(rightNode);
//把parent加入到nodes
nodes.add(parent);
}
return nodes.get(0);
}
//前序遍历
public static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("空树无法遍历");
}
}
}
//结点类
class Node implements Comparable {
int value;
Node left;
Node right;
//前序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return value + " ";
}
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
}
跟着韩顺平老师学的时候敲的。主要就是结点类Node实现了Comparable接口,用来比较结点值大小。然后就是算法逻辑,在一个循环中完成哈夫曼树的创建,不算难,只要了解了哈夫曼树构建的原理。