二叉树
又叫二叉排序树。
节点是数量为,,n为层数。
满二叉树:所有的叶子节点都在最后一层。
完全二叉树:如果所有叶子节点都在最后一层和倒数第二层,而且每个叶子节点都有左右子节点。
前序遍历
1、先输出当前节点(初始是root节点)。
2、如果左子节点不为空,则递归继续前序遍历。
3、如果右子节点不为空,则递归继续前序遍历。
class HeroNode {
private int no;
private String name;
private HeroNode left, right;
}
public HeroNode preOrderSearch(int no) {
if (this.no == no) {
return this;
}
HeroNode resNode;
if (this.left != null) {
resNode = this.left.preOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
if (this.right != null) {
resNode = this.right.preOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
return null;
}
中序遍历
1、如果当前节点的左子节点不为空,则递归中序遍历。
2、输出当前节点。
3、如果当前节点的右子节点不为空,则递归中序遍历。
public HeroNode infixOrderSearch(int no) {
HeroNode resNode;
if (this.left != null) {
resNode = this.left.infixOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
if (this.no == no) {
return this;
}
if (this.right != null) {
resNode = this.right.infixOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
return null;
}
后序遍历
左右中
1、如果当前节点的左子节点不为空,则递归后序遍历。
2、如果当前节点的右子节点不为空,则递归后序遍历。
3、输出当前节点。
public HeroNode postOrderSearch(int no) {
HeroNode resNode;
if (this.left != null) {
resNode = this.left.postOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
if (this.right != null) {
resNode = this.right.postOrderSearch(no);
if (resNode != null) {
return resNode;
}
}
if (this.no == no) {
return this;
}
return null;
}
二叉树节点的删除,如果是中间节点,则整个中间节点都删除。
public void delNode(int no) {
if (this.no == no) {
return;
}
if (this.left != null) {
if (this.left.no == no) {
this.left = null;
} else {
this.left.delNode(no);
}
}
if (this.right != null) {
if (this.right.no == no) {
this.right = null;
} else {
this.right.delNode(no);
}
}
}
二叉树的层序遍历
//todo
顺序存储二叉树
顺序二叉树通常是完全二叉树。
第n(n是下标)个元素的左子节点为 2 * n + 1
第n个元素的右子节点为 2 * n + 2
第n个元素的父节点为 (n - 1) / 2
堆排序用到顺序存储二叉树的结构。
线索化二叉树
充分的利用到了叶子节点的空指针。
class HeroNode {
private int no;
private String name;
private HeroNode left, right;
private int leftType; // 0 指向的是左子树 1指向前驱节点
private int rightType; // 0 指向的是右子树 1指向后继节点
}
对线索二叉树进行中序线索化的方法
public void threadedNodes(HeroNode node) {
if (node == null) {
return;
}
threadedNodes(node.getLeft()); // 先线索化左子树
// 再线索化当前节点
if (node.getLeft() == null) { // 前驱
node.setLeft(pre);
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null) { // 后继
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
threadedNodes(node.getRight()); // 最后线索化右子树
}
线索化二叉树的中序遍历
class ThreadedBinaryTree {
private HeroNode root;
private HeroNode pre = null; // 指向前驱节点
public void threadedList() {
HeroNode node = root;
while (node != null) {
while (node.getLeftType() == 0) { // 到左下角
node = node.getLeft();
}
System.out.println(node);
while (node.getRightType() == 1) {
node = node.getRight();
System.out.println(node);
}
node = node.getRight();
}
}
}
什么是大顶堆,什么是小顶堆?
每个节点的值都小于等于其左右孩子节点的值,称为小顶堆。反之是大顶堆。
大顶堆的特点:arr[i] >= arr[2 * i + 1] && arr[i] >= arr[2 * i + 2]
小顶堆的特点:arr[i] <= arr[2 * i + 1] && arr[i] <= arr[2 * i + 2]
i 是下标
赫夫曼树
路径长度等于层数-1;
节点的权就是节点的值;
节点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积;
树的带权路径长度(WPL)为:所有叶子节点的带权路径长度之和;
赫夫曼树是带权路径长度(WPL)最短的树,权值越大的节点离根节点越近;
如何构建一棵赫夫曼树?
1)将数组从小到大排序,每个数据都是一个节点,每个节点都是一棵二叉树树;
2)取出根节点权值最小的两棵二叉树;
3)组成一棵新的二叉树,新二叉树的根节点的权值是前两棵二叉树根节点权值的和。
4)再将新二叉树放到数组中。不断重复1-2-3-4的步骤,直到数组只剩下最后一个元素。
通过数组构建赫夫曼树
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3, 29, 6, 1}; // {1, 3, 6, 7, 8, 13, 29}
Node root = createHuffmanTree(arr);
preOrder(root);
}
// 数组 to 赫夫曼树
public static Node createHuffmanTree(int[] arr) {
List<Node> list = new ArrayList<>();
for (int value : arr) { // int[] to ArrayList
list.add(new Node(value));
}
while (list.size() > 1) {
Collections.sort(list);
// 1、取出最小的两个数
Node leftNode = list.get(0); // 取出权值最小的二叉树
Node rightNode = list.get(1); // 取出权值第二小的二叉树
// 2、构建一棵新树
Node parent = new Node(leftNode.value + rightNode.value);
parent.left = leftNode;
parent.right = rightNode;
// 3、从ArrayList里删掉掉leftNode和rightNode
list.remove(leftNode);
list.remove(rightNode);
// 4、新树加到原数组中
list.add(parent);
}
return list.get(0);
}
public static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("空树不能遍历");
}
}
}
class Node implements Comparable<Node> {
int value; // 节点的权值
Node left, right;
public Node(int value) {
this.value = value;
}
// 前序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
@Override
public int compareTo(Node node) {
return this.value - node.value;
}
}
赫夫曼编码
也称为可变字长编码(每个字符用来表示的二进制位长度不确定),把字符串发放到赫夫曼树里,可以对数据进行压缩和解压,压缩率可以达到20%~90%。是无损压缩,每次构建的赫夫曼树有可能不同;
public class HuffmanCode {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
// 10100
byte[] contentBytes = content.getBytes(); // oldString to ascii 例如:171717
// 1、压缩
byte[] huffmanZip = huffmanZip(contentBytes);
// 2、解压
byte[] sourceBytes = decode(huffmanCodes, huffmanZip);
String oldString = new String(sourceBytes);
}
/**
* 将byte转成二进制的字符串
*
* @param flag 是否需要删除高位
* @param b 十进制
* @return 二进制
*/
private static String bytesToBitString(boolean flag, byte b) {
int temp = b;
if (flag) {
temp |= 256;
String str = Integer.toBinaryString(temp);
return str.substring(str.length() - 8);
} else {
String str = Integer.toBinaryString(temp);
return str;
}
}
/**
* 解压
*
* @param huffmanCodes 赫夫曼对应的编码表
* @param huffmanBytes 压缩赫夫曼码
* @return oldString
*/
private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
// 1、转成101格式
StringBuilder stringBuilder1 = new StringBuilder();
for (int i = 0; i < huffmanBytes.length - 1; i++) {
stringBuilder1.append(bytesToBitString(true, huffmanBytes[i]));
} // 最后一个不需要补0
stringBuilder1.append(bytesToBitString(false, huffmanBytes[huffmanBytes.length - 1]));
// 2、对照赫夫曼编码表转成oldString
HashMap<String, Byte> map = new HashMap<>(); // 反转map
for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
List<Byte> list = new ArrayList<>();
for (int i = 0; i < stringBuilder1.length(); i++) {
int count = 1;
boolean flag = true;
Byte b = null;
while (flag) {
String key = stringBuilder1.substring(i, i + count);
b = map.get(key);
if (b == null) { // 没有找到
count++;
} else {
flag = false;
}
}
list.add(b);
i += count - 1;
}
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* oldString to huffmanZip
*
* @param bytes 原始数组
* @return 压缩后的数组
*/
private static byte[] huffmanZip(byte[] bytes) {
List<Node> nodes = getNodes(bytes); // 每个字符各出现了多少次 例如:105->9次
Node root = createHuffmanTree(nodes); // 生成赫夫曼树
Map<Byte, String> huffmanCodes = getCodes(root); // 生成对应赫夫曼<key,val>编码 例如:32->01
byte[] huffmanCodeBytes = zip(bytes, huffmanCodes); // 压缩 例如:-88,-65,-56
return huffmanCodeBytes;
}
/**
* 压缩,将字符串对应的byte[]数组,转换成压缩后的byte[]
*
* @param bytes 原始的byte[]数组
* @param huffmanCodes 赫夫曼编码表
* @return 返回压缩后的byte[]数组
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
StringBuilder stringBuilder1 = new StringBuilder();
for (byte b : bytes) {
stringBuilder1.append(huffmanCodes.get(b));
}
int len;
if (stringBuilder1.length() % 8 == 0) {
len = stringBuilder1.length() / 8;
} else {
len = stringBuilder1.length() / 8 + 1;
}
byte[] huffmanCodeBytes = new byte[len];
int index = 0; // 第几个byte
for (int i = 0; i < stringBuilder1.length(); i += 8) { // 每8位对应一个byte,所以步长是8
String strByte;
if (i + 8 < stringBuilder1.length()) {
strByte = stringBuilder1.substring(i, i + 8);
} else { // 最末尾的
strByte = stringBuilder1.substring(i);
}
huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2);
index++;
}
return huffmanCodeBytes;
}
// 赫夫曼编码表Map<Byte,String> 32(空格)->01
static Map<Byte, String> huffmanCodes = new HashMap<>();
static StringBuilder stringBuilder = new StringBuilder();
private static Map<Byte, String> getCodes(Node root) {
if (root == null) {
return null;
}
getCodes(root.left, "0", stringBuilder); // 生成赫夫曼编码
getCodes(root.right, "1", stringBuilder); // 生成赫夫曼编码
return huffmanCodes;
}
/**
* 将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入到huffmanCodes集合
*
* @param node 根节点
* @param code 路径 左子节点0 右子节点1
* @param stringBuilder 用于拼接路径
*/
private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
stringBuilder2.append(code);
if (node != null) {
if (node.data == null) { // 非叶子节点,没有内容,只有权值
getCodes(node.left, "0", stringBuilder2);
getCodes(node.right, "1", stringBuilder2);
} else { // 叶子节点
huffmanCodes.put(node.data, stringBuilder2.toString());
}
}
}
private static void preOrder(Node root) {
if (root != null) {
root.preOrder();
} else {
System.out.println("赫夫曼树为空");
}
}
private static List<Node> getNodes(byte[] bytes) {
ArrayList<Node> nodes = new ArrayList<>();
HashMap<Byte, Integer> counts = new HashMap<>();
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
Node leftNode = nodes.get(0);
Node righNode = nodes.get(1);
Node parentNode = new Node(null, leftNode.value + righNode.value);
parentNode.left = leftNode;
parentNode.right = righNode;
nodes.add(parentNode);
nodes.remove(leftNode);
nodes.remove(righNode);
}
return nodes.get(0);
}
}
class Node implements Comparable<Node> {
Byte data;
int value; // 权值
Node left, right;
public Node(Byte key, Integer value) {
this.data = key;
this.value = value;
}
void preOrder() {
}
@Override
public int compareTo(Node node) {
return this.value - node.value;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", value=" + value +
'}';
}
}
如何用赫夫曼树压缩文件?
import java.io.*;
import java.util.*;
public class HuffmanCode2 {
public static void main(String[] args) {
String srcFile = "d:\\Users\\JJH\\Desktop\\1.jpg";
String descFile = "d:\\Users\\JJH\\Desktop\\download.zip";
// 1、文件的压缩,写入目标路径
zipFile(srcFile, descFile);
String descFile2 = "d:\\Users\\JJH\\Desktop\\2.jpg";
// 2、文件的解压
unZipFile(descFile, descFile2);
}
public static void unZipFile(String zipFile, String destFile) {
// 文件输入流
InputStream is = null;
// 对象输入流
ObjectInputStream ois = null;
// 文件输出流
OutputStream os = null;
try {
// 文件输入流
is = new FileInputStream(zipFile);
ois = new ObjectInputStream(is);
// 文件读取出来的二进制数
byte[] huffmanBytes = (byte[]) ois.readObject();
// 文件读取出来的赫夫曼编码表
Map<Byte, String> huffmanCodes = (Map<Byte, String>) ois.readObject();
// 定义文件输出流
os = new FileOutputStream(destFile);
// 1、解压
byte[] decode = decode(huffmanCodes, huffmanBytes);
// 2、写入文件
os.write(decode);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (ois != null) {
ois.close();
}
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* 解压
*
* @param huffmanCodes 赫夫曼对应的编码表
* @param huffmanBytes 压缩赫夫曼码
* @return oldString
*/
private static byte[] decode(Map<Byte, String> huffmanCodes, byte[] huffmanBytes) {
// 1、转成101格式
StringBuilder stringBuilder1 = new StringBuilder();
for (int i = 0; i < huffmanBytes.length - 1; i++) {
stringBuilder1.append(bytesToBitString(true, huffmanBytes[i]));
} // 最后一个需要补0,例如最后一个数是113的情况
stringBuilder1.append(bytesToBitString(false, huffmanBytes[huffmanBytes.length - 1]));
// 2、对照赫夫曼编码表转成oldString
HashMap<String, Byte> map = new HashMap<>(); // 反转map
for (Map.Entry<Byte, String> entry : huffmanCodes.entrySet()) {
map.put(entry.getValue(), entry.getKey());
}
List<Byte> list = new ArrayList<>();
for (int i = 0; i < stringBuilder1.length(); ) {
int count = 1;
boolean flag = true;
Byte b = null;
while (flag) {
String key = stringBuilder1.substring(i, i + count);
b = map.get(key);
if (b == null) { // 没有找到
count++;
} else {
flag = false;
}
}
list.add(b);
i += count;
}
byte[] b = new byte[list.size()];
for (int i = 0; i < b.length; i++) {
b[i] = list.get(i);
}
return b;
}
/**
* 将byte转成二进制的字符串
*
* @param flag 是否需要删除高位
* @param b 十进制
* @return 二进制
*/
private static String bytesToBitString(boolean flag, byte b) {
int temp = b;
if (flag) { // 不要补0的情况
/**
* 00001111 和
* 1111
*/
temp |= 256;
String str = Integer.toBinaryString(temp);
return str.substring(str.length() - 8);
} else { // 要补零的情况
temp |= 256;
String str = Integer.toBinaryString(temp);
return str.substring(str.length() - 8);
}
}
/**
* 文件的压缩
*
* @param srcFile 源路径
* @param destFile 目标路径
*/
public static void zipFile(String srcFile, String destFile) {
FileInputStream is = null;
FileOutputStream os = null;
ObjectOutputStream oos = null;
try {
// 1、创建输入流
is = new FileInputStream(srcFile);
// 创建一个和源文件大小一样的byte[]
byte[] b = new byte[is.available()];
is.read(b);
byte[] huffmanZip = huffmanZip(b);
// 2、创建输出流
os = new FileOutputStream(destFile);
oos = new ObjectOutputStream(os); // 对象输出流
oos.writeObject(huffmanZip);
// 还要把赫夫曼编码写入压缩文件
oos.writeObject(huffmanCodes);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (oos != null) {
oos.close();
}
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* oldString to huffmanZip
*
* @param bytes 原始数组
* @return 压缩后的数组
*/
private static byte[] huffmanZip(byte[] bytes) {
List<Node> nodes = getNodes(bytes); // 每个字符各出现了多少次 例如:105->9次
Node root = createHuffmanTree(nodes); // 生成赫夫曼树
Map<Byte, String> huffmanCodes = getCodes(root); // 生成对应赫夫曼<key,val>编码 例如:32->01
byte[] huffmanCodeBytes = zip(bytes, huffmanCodes); // 压缩 例如:-88,-65,-56
return huffmanCodeBytes;
}
private static List<Node> getNodes(byte[] bytes) {
ArrayList<Node> nodes = new ArrayList<>();
HashMap<Byte, Integer> counts = new HashMap<>();
for (byte b : bytes) {
Integer count = counts.get(b);
if (count == null) {
counts.put(b, 1);
} else {
counts.put(b, count + 1);
}
}
for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
private static Node createHuffmanTree(List<Node> nodes) {
while (nodes.size() > 1) {
Collections.sort(nodes);
Node leftNode = nodes.get(0);
Node righNode = nodes.get(1);
Node parentNode = new Node(null, leftNode.value + righNode.value);
parentNode.left = leftNode;
parentNode.right = righNode;
nodes.add(parentNode);
nodes.remove(leftNode);
nodes.remove(righNode);
}
return nodes.get(0);
}
static StringBuilder stringBuilder = new StringBuilder();
static Map<Byte, String> huffmanCodes = new HashMap<>();
private static Map<Byte, String> getCodes(Node root) {
if (root == null) {
return null;
}
getCodes(root.left, "0", stringBuilder); // 生成赫夫曼编码
getCodes(root.right, "1", stringBuilder); // 生成赫夫曼编码
return huffmanCodes;
}
/**
* 将传入的node节点的所有叶子节点的赫夫曼编码得到,并放入到huffmanCodes集合
*
* @param node 根节点
* @param code 路径 左子节点0 右子节点1
* @param stringBuilder 用于拼接路径
*/
private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
StringBuilder stringBuilder2 = new StringBuilder(stringBuilder);
stringBuilder2.append(code);
if (node != null) {
if (node.data == null) { // 非叶子节点,没有内容,只有权值
getCodes(node.left, "0", stringBuilder2);
getCodes(node.right, "1", stringBuilder2);
} else { // 叶子节点
huffmanCodes.put(node.data, stringBuilder2.toString());
}
}
}
/**
* 压缩,将字符串对应的byte[]数组,转换成压缩后的byte[]
*
* @param bytes 原始的byte[]数组
* @param huffmanCodes 赫夫曼编码表
* @return 返回压缩后的byte[]数组
*/
private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
// 1001
StringBuilder stringBuilder1 = new StringBuilder();
for (byte b : bytes) {
stringBuilder1.append(huffmanCodes.get(b));
}
int len;
if (stringBuilder1.length() % 8 == 0) {
len = stringBuilder1.length() / 8;
} else {
len = stringBuilder1.length() / 8 + 1;
}
byte[] huffmanCodeBytes = new byte[len];
int index = 0; // 第几个byte
for (int i = 0; i < stringBuilder1.length(); i += 8) { // 每8位对应一个byte,所以步长是8
String strByte;
if (i + 8 < stringBuilder1.length()) {
strByte = stringBuilder1.substring(i, i + 8);
} else { // 最末尾的
strByte = stringBuilder1.substring(i);
}
huffmanCodeBytes[index] = (byte) Integer.parseInt(strByte, 2);
index++;
}
return huffmanCodeBytes;
}
}
二叉排序树
1、左子节点小于当前节点,右子节点大于当前节点;
2、尽量避免相同节点,相同节点可以放在左边或者右边;
如果树不平衡怎么办?
平衡二叉树就是对此的优化;
平衡二叉树
一棵树的左右两棵子树的高度差的绝对值不超过1;
左旋转
1、创建一个新节点newNode,值等于当前节点的值;
2、把新节点的左子树设置为当前节点的左子树;
3、把新节点的右子树设置为当前节点右子树的左子树;
4、把当前节点(root)的值换为右子节点的值;
5、把当前节点的右子树设置为右子树的右子树;
6、把当前节点的左子树设置为新节点;
右旋转
1、创建新节点,以当前根节点的值
2、把新节点的右子节点设置为当前节点的右子节点
3、把新节点的左子节点设置为当前节点的左子节点的右子节点
4、把当前节点的值设置为左子节点的值
5、当前节点左子节点设置位当前节点左子节点的左子节点
6、当前节点的右子节点设置为新节点
在添加新节点时旋转
package org.example.tree.avl;
public class AVLTreeDemo {
public static void main(String[] args) {
// int[] arr = {4, 3, 6, 5, 7, 8};
// int[] arr = {10,11,7,6,8,9};
int[] arr = {2, 1, 6, 5, 7, 3};
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
}
}
class AVLTree {
private Node root;
public Node getRoot() {
return root;
}
// 查找要删除的节点
public Node search(int val) {
if (root == null) {
return null;
} else {
return root.search(val);
}
}
// 查找要删除的节点的父节点
public Node searchParent(int val) {
if (root == null) {
return null;
} else {
return root.searchParent(val);
}
}
/**
* 1、删除叶子节点
* 2、删除有一棵子树的节点
* 左子节点还是右子节点?
* 3、删除有两课子树的节点
* 右子节点的最小左子节点
*/
public void delNode(int val) {
if (root == null) {
return;
} else {
Node targetNode = search(val);
if (targetNode == null) {
return; //没找到
}
if (root.left == null && root.right == null) {
// 树只有一个节点
root = null;
return;
}
Node parent = searchParent(val);
// 1、删除叶子节点
if (targetNode.left == null && targetNode.right == null) {
// 判断ta rgetNode是parent的左子节点还是父子节点
if (parent.left != null && parent.left == targetNode) {
// 删除的是左子节点
parent.left = null;
} else if (parent.right != null && parent.right == targetNode) {
// 删除的是右子节点
parent.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {
// 2、删除有两棵子节点的节点
// 从target的右子节点找最小的节点
int min = delRightTreeMin(targetNode.right);
targetNode.val = min;
} else {
// 3、删除只有一个子节点的节点
if (targetNode.left != null) {
// target只有左子节点
if (parent != null) {
if (parent.left.val == val) {
// target是父节点的左子节点
parent.left = targetNode.left;
} else if (parent.right.val == val) {
// target是父节点的右子节点
parent.right = targetNode.left;
}
} else {
// target是根节点
root = targetNode.left;
}
} else {
if (parent != null) {
// target只有右子节点
if (parent.left.val == val) {
// target是父节点的左子节点
parent.left = targetNode.right;
} else {
// target是父节点的右子节点
parent.right = targetNode.right;
}
} else {
// target是根节点
root = targetNode.right;
}
}
}
}
}
// 删除右子节点的最左子节点并返回
public int delRightTreeMin(Node node) {
Node target = node;
while (target.left != null) {
target = target.left;
}
int val = target.val;
delNode(val);
return val;
}
public void add(Node node) {
if (root == null) {
root = node;
} else {
root.add(node);
}
}
/*public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉排序树为空,不能遍历");
}
}*/
}
class Node {
int val;
Node left, right;
public Node(int val) {
this.val = val;
}
public int leftHeight() {
if (left == null) {
return 0;
}
return left.height();
}
public int rightHeight() {
if (right == null) {
return 0;
}
return right.height();
}
// 以该节点为根节点的树的高度
public int height() {
return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
}
// 查找要删除的节点
public Node search(int val) {
if (val == this.val) {
return this;
} else if (val < this.val) {
if (this.left == null) {
return null; // 找不到
} else {
return this.left.search(val);
}
} else {
if (this.right == null) {
return null; // 找不到
} else {
return this.right.search(val);
}
}
}
// 查找要删除的节点的父节点
public Node searchParent(int val) {
// 如果当前节点是要删除的节点的父节点
if (this.left != null && this.left.val == val ||
this.right != null && this.right.val == val) {
return this;
} else {
if (val < this.val && this.left != null) {
return this.left.searchParent(val);
} else if (val > this.val && this.right != null) {
return this.right.searchParent(val);
}
return null;
}
}
public void add(Node node) {
if (node == null) {
return;
}
if (node.val < this.val) {
if (this.left == null) {
this.left = node;
} else {
this.left.add(node);
}
}
if (node.val > this.val) {
if (this.right == null) {
this.right = node;
} else {
this.right.add(node);
}
}
// 添加完节点后,是否需要旋转
if (rightHeight() - leftHeight() > 1) {
if (right != null && right.leftHeight() > right.rightHeight()) {
// 左旋转
right.rightRotate();
}
// 左旋转
leftRotate();
return;
}
if (leftHeight() - rightHeight() > 1) {
if (left != null && left.leftHeight() < left.rightHeight()) {
// 左旋转
left.leftRotate();
}
// 右旋转
rightRotate();
}
}
private void leftRotate() {
// 创建新节点,以当前根节点的值
Node newNode = new Node(val);
// 把新节点的左子节点设置为当前节点的左子节点
newNode.left = left;
// 把新节点的右子节点设置为当前节点的右子节点的左子节点
newNode.right = right.left;
// 把当前节点的值设置为右子节点的值
val = right.val;
// 把当前节点的右子节点设置为当前节点的右子节点的右子节点
right = right.right;
// 把当前节点的左子节点设置为新节点
left = newNode;
}
private void rightRotate() {
// 创建新节点,以当前根节点的值
Node newNode = new Node(val);
// 把新节点的右子节点设置为当前节点的右子节点
newNode.right = right;
// 把新节点的左子节点设置为当前节点的左子节点的右子节点
newNode.left = left.right;
// 把当前节点的值设置为左子节点的值
val = left.val;
// 把当前节点的左子节点设置为当前节点的左子节点的左子节点
left = left.left;
// 把当前节点的右子节点设置为新节点
right = newNode;
}
/*public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}*/
@Override
public String toString() {
return "Node{" +
"val=" + val +
'}';
}
}
多叉树
多叉树的存在是为了减少树的高度,以减少IO的次数;
B树
一个节点的大小通常是4K,这样每个节点只需要一次IO就可以完全载入;
B树的阶:节点的最多子节点的个数,例如23树的阶就是3;
文件系统和和数据库系统的设计者采用预读原理,将一个节点的大小设置为一个页(一个页通常是4K),这样每个节点就只需要1次IO就可以完全载入;
将树的度M设置为1024;
2-3树
2-3树是最简单的B树;
2-3树的所有叶子节点都在同一层;
三个子节点的节点叫三节点,三节点要么没有子节点,要么有三个子节点,二节点同理;
2-3树是由二节点和三节点构成的树;
2-3-4树
B+树
B+树和B树的区别:B+树的所有数据都放在叶子节点;