数据结构和算法-结构-树结构

二叉树

又叫二叉排序树。

节点是数量为,2^{n}-1,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 是下标

堆排序:数据结构和算法——排序算法-CSDN博客

赫夫曼树

路径长度等于层数-1;

节点的权就是节点的值;

节点的带权路径长度为:从根节点到该节点之间的路径长度与该节点的权的乘积;

树的带权路径长度(WPL)为:所有叶子节点的带权路径长度之和;

赫夫曼树是带权路径长度(WPL)最短的树,权值越大的节点离根节点越近;

这不是赫夫曼树WPL=13*2+7*2+8*2+3*2=62
这是赫夫曼树WPL=13*1+2*2+7*3+3*3=59

如何构建一棵赫夫曼树?

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+树的所有数据都放在叶子节点;

B*树

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋劲豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值