目录
4.4 Java8中HashMap链表使用红黑树而不是AVL树
4.5 既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于8个的时候才转换红黑树
1.先序序列建立一颗二叉树
输入(在空子树处添加空格字符或“*”的二叉树,本例中以“*”判断空子树)先序序列(设每个元素是一个字符),按先序遍历的顺序,建立二叉链表,并将该二叉链表根结点指针赋给root 。
如果输入字符不是“*”,则建立一个新结点,然后建立其左子树和右子树;如果是空格则返回,继续进行下一次操作。
输入:一个包含空子树符号的先序序列,例如:序列ABC**DE**F**G**
输出:建立一颗二叉链表表示的二叉树T,根指针为root,结果输出先序遍历二叉树后的序列A B C D E F G
import java.util.Scanner;
public class InOrder {
static int count = 0;
// 创建节点
public static class Node {
private char data;
private Node left = null;
private Node right = null;
public Node(char data) {
this.data = data;
}
}
// 先序创建二叉树
public static Node create(String datas) {
Node root = null;
char c = datas.charAt(count);
if (c != '*') {
root = new Node(c);
count++;
root.left = create(datas);
count++;
root.right = create(datas);
}
return root;
}
// 输出二叉树
public static void prePrint(Node root) {
System.out.print(root.data);
if (root.left != null) {
prePrint(root.left);
}
if (root.right != null) {
prePrint(root.right);
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入先序序列:");
String datas = scanner.next();
Node root = create(datas);
prePrint(root);
}
}
2.哈夫曼树的创建与打印
根据给定的字符及其权值建立一棵哈夫曼树,并输出已建好的哈夫曼树的各结点的编码。
public class HuffmanTree {
/*
* 哈夫曼树的结点
*/
public static class Node {
private char data;
private int weight;
private int parent;
public Node left = null;
public Node right = null;
public Node(int data, int weight, int parent) {
this.data = (char) (data + 'A');
this.weight = weight;
this.parent = parent;
}
}
/*
* 非递归创建哈夫曼树,返回权值最大的节点
*/
public Node create(Stack<Integer> stack, int[][] a) { // 创建哈夫曼树
Queue<Node> queue = new LinkedList<>();
int weight, data, parent;
Node temp = null;
while (!stack.isEmpty()) {
Node root = null;
data = -1;// 设置默认符号为@
parent = 0;
/*
* 创建或选择根节点
*/
if (!queue.isEmpty()) {
// 取队头元素为根节点
root = queue.poll();
} else { // 创建根节点,仅执行一次
weight = stack.pop();
for (int i = 0; i < a.length; i++) {
if (a[i][1] == weight) {
data = a[i][0];
}
}
root = new Node(data, weight, parent);
temp = root;
}
/*
* 创建左子树
*/
if (!stack.isEmpty()) {
parent = root.weight;
weight = stack.pop();
for (int i = 0; i < a.length; i++) {
if (a[i][1] == weight) {
data = a[i][0];
}
}
root.left = new Node(data, weight, parent);
queue.add(root.left);
}
/*
* 创建右子树
*/
if (!stack.isEmpty()) {
parent = root.weight;
weight = stack.pop();
for (int i = 0; i < a.length; i++) {
if (a[i][1] == weight) {
data = a[i][0];
}
}
root.right = new Node(data, weight, parent);
queue.add(root.right);
}
}
return temp;
}
/*
* 递归打印哈夫曼树
*/
public static void printTree(Node root) {
if (root.left != null && root.right != null) {// 哈夫曼树没有度为1的点
System.out.println(root.data + "\t" + root.weight + "\t"
+ root.parent + "\t" + root.left.data + "\t"
+ root.right.data);
} else {
// -1代表无子节点
System.out.println(root.data + "\t" + root.weight + "\t"
+ root.parent + "\t" + "-1" + "\t" + "-1");
}
if (root.left != null) {
printTree(root.left);
}
if (root.right != null) {
printTree(root.right);
}
}
/*
* 处理输入的数组,返回存储权值的栈
*/
public Stack<Integer> dealArray(int[][] a) {
Stack<Integer> stack = new Stack<>();
List<Integer> array = new ArrayList<>();
for (int i = 0; i < a.length; i++) {
array.add(a[i][1]);
}
Collections.sort(array);// 排序
int min1;
for (int i = 0; i < array.size() - 1; ) {
stack.add(array.get(i));
stack.add(array.get(i + 1));
int sum = array.get(i) + array.get(i + 1);
array.add(sum);
array.remove(array.get(i));
array.remove(array.get(i)); // 一定要为array.get(i),不能为array.get(i+1)!
Collections.sort(array);
i = 0;
}
stack.add(array.get(0));
return stack;
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("请输入需哈夫曼编码的字符及其权值:\n输入规则如(A1 B2 C3)");
String s = in.nextLine();
s = s.replace(" ", "");
int[][] a = new int[s.length() / 2][2];
int j = 0;
for (int i = 0; i < s.length(); i++) {// 用二维数组存储字符与权值
if (i % 2 == 0) {
a[j][0] = s.charAt(i) - 'A';
} else {
a[j][1] = s.charAt(i) - '0';
j++;
}
}
// C3 B4 A5
HuffmanTree tree = new HuffmanTree();
Stack<Integer> stack = tree.dealArray(a);
Node root = tree.create(stack, a);
System.out.println("所带字符\t权值\t双亲节点\t左孩子\t右孩子");
printTree(root);
System.out.println("备注:\n字符默认为@\n0代表无双亲节点\n-1代表无孩子");
}
}
3.素数解决之树筛法
算法训练 Torry的困惑(基本型)
时间限制:1.0s 内存限制:512.0MB
问题描述
Torry从小喜爱数学。一天,老师告诉他,像2、3、5、7……这样的数叫做质数。Torry突然想到一个问题,前10、100、1000、10000……个质数的乘积是多少呢?他把这个问题告诉老师。老师愣住了,一时回答不出来。于是Torry求助于会编程的你,请你算出前n个质数的乘积。不过,考虑到你才接触编程不久,Torry只要你算出这个数模上50000的值。
输入格式
仅包含一个正整数n,其中n<=100000。
输出格式
输出一行,即前n个质数的乘积模50000的值。
样例输入
1
样例输出
2
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
System.out.println(calPrimeNumber(n));
}
private static int calPrimeNumber(int n) {
boolean[] prime=new boolean[10*n];
for(int i=2;i<prime.length;i++){
if(i%2==0)
prime[i]=false;
else
prime[i]=true;
}
for(int i=3;i<=Math.sqrt(prime.length);i+=2){
for(int j=i+i;j<prime.length;j+=i){//数筛法
prime[j]=false;
}
}
prime[2]=true;
int k=0,ans=1;
for(int i=2;i<prime.length;i++){
if(k==n)
break;
if(prime[i]){
ans*=i;
ans%=50000;
k++;
}
}
return ans;
}
}
4.红黑树
4.1 简介
红黑树是一种自平衡二叉搜索树,不能保证非常严格的平衡性,但是其平衡性仍然足以确保以O(logN)的时间复杂度进行插入、删除和检索操作。
它需要更少的内存,并且可以更快的进行再平衡,所以它常在树需要被频繁修改的情况下使用。
4.2 红黑树性质
1)每个节点要么是红色节点,要么是黑色节点
2)根节点是黑色节点
3)叶节点是空节点,也称为黑色节点
4)每个红色节点必须有两个黑色子节点,也就是说,一个红色节点不可能有红色子节点(虽然黑色节点可以有黑色子节点)
5)每一条从某一节点至叶节点的路径必须包含相同数量的黑色子节点
4.3 为什么这样的树是平衡的
根据性质5)假设从某节点(根节点)到叶节点有路径1与路径2,每条路径均有b个黑色节点,最坏情况下,
+ 红色节点最少数量为0,则路径1共有b个节点
+ 红色节点最大数量为b(性质4决定红色节点数不会超过一半), 路径2共有2b个节点
即使在最极端的情况下,两条路径的长度相差也不会超过一倍,这足以确保在O(logN)的时间复杂度内完成查找和插入操作
4.4 Java8中HashMap链表使用红黑树而不是AVL树
在ConcurrentHashMap中是加了锁的,实际上是读写锁,如果写冲突就会等待,如果插入时间过长必然等待时间更长,而红黑树相对AVL树插入更快
4.5 既然红黑树那么好,为啥hashmap不直接采用红黑树,而是当大于8个的时候才转换红黑树
因为红黑树需要左旋、右旋操作,而单链表不需要
以下都是单链表与红黑树结构对比
如果元素小于8个,查询成本高,新增成本低
如果元素大于8个,查询成本低,新增成本高
4.6 红黑树应用实例
JAVA: java.util.TreeMap、java.utils.TreeSet
C++ STL: map、multimap、multiset
Linux内核: 完全公平的调度程序, linux/rbtree.h
欢迎关注公众号算法小生第一时间获取更多精彩内容