又叫二叉排序树。
节点是数量为,
,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);
- }
- }
- }
顺序二叉树通常是完全二叉树。
第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
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
{ - 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
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
map = new HashMap<>(); // 反转map - for (Map.Entry
entry : huffmanCodes.entrySet()) { - map.put(entry.getValue(), entry.getKey());
- }
- List
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
nodes = getNodes(bytes); // 每个字符各出现了多少次 例如:105->9次 - Node root = createHuffmanTree(nodes); // 生成赫夫曼树
- Map
huffmanCodes = getCodes(root); // 生成对应赫夫曼编码 例如: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
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
32(空格)->01 - static Map
huffmanCodes = new HashMap<>(); - static StringBuilder stringBuilder = new StringBuilder();
-
- private static Map
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
getNodes(byte[] bytes) { - ArrayList
nodes = new ArrayList<>(); - HashMap
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
entry : counts.entrySet()) { - nodes.add(new Node(entry.getKey(), entry.getValue()));
- }
- return nodes;
- }
-
- private static Node createHuffmanTree(List
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
{ - 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
huffmanCodes = (Map) 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
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
map = new HashMap<>(); // 反转map - for (Map.Entry
entry : huffmanCodes.entrySet()) { - map.put(entry.getValue(), entry.getKey());
- }
- List
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
nodes = getNodes(bytes); // 每个字符各出现了多少次 例如:105->9次 - Node root = createHuffmanTree(nodes); // 生成赫夫曼树
- Map
huffmanCodes = getCodes(root); // 生成对应赫夫曼编码 例如:32->01 - byte[] huffmanCodeBytes = zip(bytes, huffmanCodes); // 压缩 例如:-88,-65,-56
- return huffmanCodeBytes;
- }
-
- private static List
getNodes(byte[] bytes) { - ArrayList
nodes = new ArrayList<>(); - HashMap
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
entry : counts.entrySet()) { - nodes.add(new Node(entry.getKey(), entry.getValue()));
- }
- return nodes;
- }
-
- private static Node createHuffmanTree(List
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
huffmanCodes = new HashMap<>(); -
- private static Map
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
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的次数;
一个节点的大小通常是4K,这样每个节点只需要一次IO就可以完全载入;
B树的阶:节点的最多子节点的个数,例如23树的阶就是3;
文件系统和和数据库系统的设计者采用预读原理,将一个节点的大小设置为一个页(一个页通常是4K),这样每个节点就只需要1次IO就可以完全载入;
将树的度M设置为1024;

2-3树是最简单的B树;
2-3树的所有叶子节点都在同一层;
三个子节点的节点叫三节点,三节点要么没有子节点,要么有三个子节点,二节点同理;
2-3树是由二节点和三节点构成的树;

B+树和B树的区别:B+树的所有数据都放在叶子节点;

