9.2 二叉树的遍历
-
preOrder、inOrder、postOrder都由DFS实现,依赖递归
-
layerOrder由BFS实现,依赖队列
-
已知先序/后序/层序+中序构建唯一的二叉树
package 第九章.已知先序或者后序或者层序和中序求二叉树;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
public class PAT_A1020 {
static int[] postOrder;
static int[] inOrder;
public static void main(String[] args) throws IOException {
InputStreamReader ir = new InputStreamReader(System.in);
BufferedReader bf = new BufferedReader(ir);
String line = bf.readLine();
int total = Integer.parseInt(line);
postOrder=new int[total];
inOrder=new int[total];
String line1 = bf.readLine();
String[] s = line1.split(" ");
String line2 = bf.readLine();
String[] s1 = line2.split(" ");
for(int i=0;i<total;i++){
postOrder[i]=Integer.parseInt(s[i]);
}
for(int i=0;i<total;i++){
inOrder[i]=Integer.parseInt(s1[i]);
}
Node tree = createTree(0, total-1, 0, total-1);
layerOrder(tree,total);
}
/**
* post:左右根 postL postR
* in:左根右 inL inR
* postR为当前根节点,在in中寻找postR对应的下标index,则根节点左边元素个数为index-inL,在post中对应的是[postL,postL+index-inL-1],根节点右边元素在post中对应的是[postL+index-inL,postR-1]
*/
public static Node createTree(int postL,int postR,int inL,int inR){
if(postL>postR){
return null;
}
Node node = new Node();
node.data=postOrder[postR];
int index=0;
for(int i=0;i<inOrder.length;i++){
if(inOrder[i]==postOrder[postR]){
index=i;
break;
}
}
node.lChild=createTree(postL,postL+index-inL-1,inL,index-1);
node.rChild=createTree(postL+index-inL,postR-1,index+1,inR);
return node;
}
public static void layerOrder(Node tree,int total){
Queue<Node> queue=new LinkedList<>();
queue.offer(tree);
int sum=0;
while (!queue.isEmpty()){
Node tempNode = queue.poll();
sum++;
if(sum==total){
System.out.print(tempNode.data);
}else{
System.out.print(tempNode.data+" ");
}
if(tempNode.lChild!=null){
queue.offer(tempNode.lChild);
}
if(tempNode.rChild!=null){
queue.offer(tempNode.rChild);
}
}
}
}
class Node{
int data;
Node lChild;
Node rChild;
}
9.3 树的遍历
9.4 BST
利用BST实现数据查询优化
BST中比结点权值小的最大节点称为该节点的前驱,而把比节点权值大的最小节点称为该节点的后继。
显然,节点的前驱就是该节点左子树的最右节点,后继就是该节点右子树的最左节点
BST的删除值得关注:递归思想
假设决定用节点N的前驱P来替换N,于是就把问题转换为在N的左子树中删除节点P,就可以递归下去了,直到递归到一个叶子节点,就可以直接把它删除
BST的性质:对BST进行中序遍历,遍历的结果是有序的
例题:
1043 Is It a Binary Search Tree
分数 25
作者 CHEN, Yue
单位 浙江大学
A Binary Search Tree (BST) is recursively defined as a binary tree which has the following properties:
- The left subtree of a node contains only nodes with keys less than the node's key.
- The right subtree of a node contains only nodes with keys greater than or equal to the node's key.
- Both the left and right subtrees must also be binary search trees.
If we swap the left and right subtrees of every node, then the resulting tree is called the Mirror Image of a BST.
Now given a sequence of integer keys, you are supposed to tell if it is the preorder traversal sequence of a BST or the mirror image of a BST.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤1000). Then N integer keys are given in the next line. All the numbers in a line are separated by a space.
Output Specification:
For each test case, first print in a line YES if the sequence is the preorder traversal sequence of a BST or the mirror image of a BST, or NO if not. Then if the answer is YES, print in the next line the postorder traversal sequence of that tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line.
Sample Input 1:
7
8 6 5 7 10 8 11
Sample Output 1:
YES
5 7 6 8 11 10 8
Sample Input 2:
7
8 10 11 8 6 7 5
Sample Output 2:
YES
11 8 10 7 5 6 8
Sample Input 3:
7
8 6 8 5 10 9 11
Sample Output 3:
NO
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
思路:
-
题目会给出树的先序遍历序列,要我们判断是否是BST或者是镜像BST的先序序列
-
我们知道一个序列中元素顺序不同,构建出来的BST可能是不同的(即便是一组相同的数字,如果它们插入BST的顺序不同,最后生成的BST也可能不同)
3.所以可以就按照题目给出的先序遍历序列中元素的顺序,构建一个BST。(BST的insert()与create())
4.构建完成之后,对该BST进行preOrder(),并对镜像BST进行preOrderMirror(),判断两个遍历结果之一有无与题目给出的先序序列完全一致的
5.如果有,则postOrder()或者postOrderMirror()
Tip:镜像BST的先序遍历只需要在原树的先序遍历时交换左右子树的访问顺序即可,后序遍历同理
代码:
import java.util.Scanner;
import java.util.Vector;
public class PAT_A1043 {
static int n;
static int[] weights;
static Vector<Node> nodes=new Vector<>();
static Vector<Integer> pres1=new Vector<>();
static Vector<Integer> pres2=new Vector<>();
static Vector<Integer> results=new Vector<>();
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n= scanner.nextInt();
weights=new int[n];
for(int i=0;i<n;i++){
weights[i]= scanner.nextInt();
Node node = new Node();
node.weight=weights[i];
nodes.add(node);
}
//根据所给节点建立BST
Node root = create();
//前序遍历BST
preOrder(root);
//先序访问镜像BST;
preOrderMirror(root);
boolean flag1=true;
boolean flag2=true;
for(int i=0;i<pres1.size();i++){
if(weights[i]!=pres1.get(i)){
flag1=false;
break;
}
}
for(int i=0;i<pres2.size();i++){
if(weights[i]!=pres2.get(i)){
flag2=false;
break;
}
}
if(flag1||flag2){
System.out.println("YES");
if(flag1){
postOrder(root);
}else{
postOrderMirror(root);
}
for(int i=0;i<results.size();i++){
if(i== results.size()-1){
System.out.print(results.get(i));
}else {
System.out.print(results.get(i)+" ");
}
}
}else{
System.out.println("NO");
}
}
public static void postOrderMirror(Node root){
if(root==null){
return;
}
postOrderMirror(root.rChild);
postOrderMirror(root.lChild);
results.add(root.weight);
}
public static void postOrder(Node root){
if(root==null){
return;
}
postOrder(root.lChild);
postOrder(root.rChild);
results.add(root.weight);
}
public static void preOrderMirror(Node root) {
if(root==null){
return ;
}
pres2.add(root.weight);
preOrderMirror(root.rChild);
preOrderMirror(root.lChild);
}
// public static void change(int indexL,int indexR){//将BST的先序遍历序列转化为镜像BST的先序遍历序列 //好难转换......
// // 实际上要获取镜像BST的先序序列只需要在原BST先序遍历的基础上交换左右子树访问顺序即可
// if(indexR-indexL==1){
// pres2.add(indexL);
// pres2.add(indexR);
// }else{
// pres2.add(indexL);
// }
// int index=0;
// for(int i=indexL+1;i<=indexR;i++){
// if(pres1.get(i)>=pres1.get(indexL)){
// index=i;
// change(indexL+1,index-1);
// change(index,indexR);
// break;
// }
// }
//
// }
public static void preOrder(Node root){
if(root==null){
return ;
}
pres1.add(root.weight);
preOrder(root.lChild);
preOrder(root.rChild);
}
public static Node insert(Node root,int x){
if(root==null){
Node node = new Node();
node.weight=x;
return node;
}
if(x<root.weight){
root.lChild=insert(root.lChild,x);
}else{
root.rChild=insert(root.rChild,x);
}
return root;
}
public static Node create(){
Node root = nodes.get(0);
for(int i=1;i<nodes.size();i++){
root=insert(root,nodes.get(i).weight);
}
return root;
}
}
class Node{
int weight;
Node lChild;
Node rChild;
}
9.5 AVL树
AVL仍然是BST,只是在其基础上增加了"平衡"的要求,平衡指对AVL树任何节点来说,左子树与右子树高度差的绝对值不超过1
getHeight(Node root)
getBalanceFactor(Node root)
updateHeight(Node root)
AVL树的insert操作值得关注:
-
旋转
Left Rotation
Right Rotation
-
插入--->会导致失衡(某节点平衡因子为2或者-2)----->需要调整
LL型--->右旋调整
LR型--->左旋调整为LL型--->右旋调整
RR型--->左旋调整
RL型--->右旋调整为RR型--->左旋调整
AVL数的插入代码是在BST的插入代码基础上增加平衡操作,也就是需要在每个insert函数之后更新当前子树的高度,并在这之后根据树型是LL型、LR型、RR型、RL型之一来进行平衡调整
9.6 并查集
Union Find Set
并查集的初始操作:
-
初始化
-
查找
-
合并
对并查集查找函数的优化:路径压缩
9.7 堆
堆是一颗完全二叉树--->使用数组来表示
堆分为大顶堆与小顶堆--->堆是完全二叉树形式,但是其中元素是按秩序的,这也是与完全二叉树不同之处
堆一般用于优先队列的实现,优先队列默认情况下使用的是大顶堆
根据完全二叉树(一个数组)来建堆:从非叶子节点倒着枚举每个节点,然后对它进行向下调整
如果要删除堆中的最大元素(也就是堆顶元素),并让其仍然保持堆的结构,那么只需要最后一个元素覆盖堆顶元素,然后对根节点进行向下调整
如果要往堆中添加一个元素:可以把想要添加的元素放在数组最后(也就是完全二叉树的最有一个节点后面),然后进行向上调整
堆排序:使用堆结构对一个序列排序
为什么需要堆排序?堆结构不已经有秩序了吗?
因为秩序只存在于根节点与子节点之间,而左右节点之间是无序的
堆排序思路:取出堆顶元素,然后将堆的最后一个元素替换到堆顶,再进行一次针对堆顶的向下调整----如此重复,直到堆中只有一个元素为止
9.8 哈夫曼树
很多实际场景并不需要真的去构建一颗哈夫曼树,只需要能得到最终的带权路径长度即可,重要的是掌握哈夫曼树的构建思想:也就是反复选择两个最小的元素,合并,直到只剩下一个元素。一般使用**优先队列(注意为小顶堆)**来执行这种策略
哈夫曼编码:前缀编码
哈夫曼编码是针对确定的字符串来讲的