树结构-基础部分


介绍:
在这里插入图片描述
在这里插入图片描述
数组的特点总结一句话就是: 查快增慢

插播:ArrayList源码分析


通过名字我们就可以发现其实是数组集合,首先有两种构造器,两种容器都是创建一个数组,只不过一个长度为0,一个长度为,这是在jdk8中的情况,jdk7种elementdata默认的是10,当我们再次添加的时候,首先有个判断,也就是grow方法,如果需要扩容就扩容到十倍,如果没有添加过一次数组那就先设置elementdata为10。

链表特点一句话:增快查慢

而树这种存储结构可以帮我们解决这些问题,他是 查快增快

树的介绍

在这里插入图片描述

二叉树

在这里插入图片描述
在这里插入图片描述
满二叉树就是说,节点总数是2的n次方-1,叶子节点都在最后一层;完全二叉树就是说,除了最后一层其他的度都为2,然后连续的最后一层在左边,右边是倒数第二层。

需求:
在这里插入图片描述
在这里插入图片描述

二叉树数据结构

谁掉用this,this就只想谁,下面开始代码演示:

public class BinaryTree {
    public static void main(String[] args) {
        TreeNode root = new TreeNode("1");
        root.setLeft(new TreeNode("2"));
        root.setRight(new TreeNode("3"));
        root.qianxu();
    }
}

class TreeNode{
    private String no;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(String no) {
        this.no = no;
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "no='" + no + '\'' +
                '}';
    }

    public void qianxu(){
        //输出父节点
        System.out.println(this);
        if(this==null){
            System.out.println("此节点为空");
        }
        if(this.left!=null){
            this.left.qianxu();
        }
        if(this.right!=null){
            this.right.qianxu();
        }
    }
}

这里就写了一个前序遍历,其他两个都是一个道理哈, 记住一句话this的调用着就是操作者
这一点跟线程的道理是一样的,线程的调用者也就是操作者

查找指定节点
在这里插入图片描述
在这里插入图片描述

public class BinaryTree {
    public static void main(String[] args) {
        TreeNode root = new TreeNode("1");
        root.setLeft(new TreeNode("2"));
        root.setRight(new TreeNode("3"));
        root.qianxu();
        TreeNode result = root.search("3");
        System.out.println("数据前序查找的结果为"+result);
        TreeNode result2 = root.searchMid("3");
        System.out.println("数据中序查找的结果为"+result2);
    }
}

class TreeNode{
    private String no;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(String no) {
        this.no = no;
    }

    public String getNo() {
        return no;
    }

    public void setNo(String no) {
        this.no = no;
    }

    public TreeNode getLeft() {
        return left;
    }

    public void setLeft(TreeNode left) {
        this.left = left;
    }

    public TreeNode getRight() {
        return right;
    }

    public void setRight(TreeNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "TreeNode{" +
                "no='" + no + '\'' +
                '}';
    }
    //前序查找
    public TreeNode search(String no){
        if (this.no==no){
            return this;
        }
        TreeNode node = null;
        if (this.left!=null){
            node = this.left.search(no);
        }
        //判断是否是左节点
        if (node!=null){
            return node;
        }
        if (this.right!=null){
            node=this.right.search(no);
        }
        return node;
    }
	//中序查找
    public TreeNode searchMid(String no){
        TreeNode node=null;
        if (this.left!=null){
             node = this.left.searchMid(no);
        }
        if (node!=null){
            return node;
        }
        //判断中间节点
        if (this.no==no){
            return this;
        }
        if (this.right!=null){
             node = this.right.searchMid(no);
        }
        return node;
    }
}

删除节点
在这里插入图片描述
在这里插入图片描述

public void delete(String no){
        //上来先判断左右子树是否为要删除的数据,如果是就删除不是就继续递归下去
        if (this.left!=null&&this.left.no==no){
            this.left=null;
            return ;
        }
        if (this.right!=null&&this.right.no==no){
            this.right=null;
            return ;
        }
        if (this.left!=null){
            this.left.delete(no);
        }
        if (this.right!=null){
            this.right.delete(no);
        }
    }

顺序存储二叉树

在这里插入图片描述
在这里插入图片描述
例子:
在这里插入图片描述

public class ArrayBinaryTree {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7};
        new ArrayTree(arr).list(0);
    }
}
class ArrayTree{
    private int[] arr;
    public ArrayTree(int[] arr) {
        this.arr = arr;
    }
    public void list(int index){
        if (index<0||index>=arr.length){
            System.out.println("不行,index越界");
        }
        System.out.print(arr[index]+"\t");
        //分别再去比较左右节点
        if ((index*2+1)<arr.length){
            list(index*2+1);
        }
        if ((index*2+2)<arr.length){
            list(index*2+2);
        }
    }
}

线索化二叉树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二叉排序树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class BinarySortTree {
    public static void main(String[] args) {
        int[] arr={7,3,10,12,5,3,9};
        Nodes head = new Nodes(0);
        for (int data : arr) {
            head.add(new Nodes(data));
        }
        head.zhongxu();
    }
}
class Nodes{
    int value;
    Nodes left;
    Nodes right;

    public Nodes(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Nodes{" +
                "value=" + value +
                '}';
    }

    public void add(Nodes node){
        if(node==null){
            return ;
        }
        if(node.value<this.value){
            if(this.left==null){
                this.left=node;
            }else {
                this.left.add(node);
            }
        }else {
            if(this.right==null){
                this.right=node;
            }else {
                this.right.add(node);
            }
        }
    }

    public void zhongxu(){
        if (this.left!=null){
            this.left.zhongxu();
        }
        System.out.print(this.value+"\t");
        if (this.right!=null){
            this.right.zhongxu();
        }
    }
}

思想就是每次进去的时候判断,如果小就继续往左边地推,如果大就继续往右边地推,然后找到位置了之后程序就回溯执行完毕!无返回值!
在这里插入图片描述

 //查找待删除节点
    public Nodes search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left != null) {
                //这里注意查找到的值一定要回溯回去
                return this.left.search(value);
            } else {
                return null;
            }
        } else {
            if (this.right != null) {
                return this.right.search(value);
            } else {
                return null;
            }
        }
    }

    //查找待删除节点父节点
    public Nodes searchParent(int value) {
        //这里注意顺序一定是先判断是否为空,再判断是否等于value,不然就会报空指针异常
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            } else {
                return null;
            }
        }
    }

    public int delMinNodes(Nodes nodes) {
        Nodes target = nodes;
        while (target.left != null) {
            target = target.left;
        }
        deleteNodes(target.value);
        //这里的target是一个辅助指针,所以实际上删除的是真实树的节点
        //这里可以直接返回
        return target.value;
    }

    public void deleteNodes(int value) {
        Nodes targetNode = search(value);
        Nodes parentNode = searchParent(value);
        if (targetNode == null) {
            return;
        }
        if (parentNode.left == null && parentNode.right == null) {
            return;
        }
        //如果是叶子节点的情况
        if (targetNode.left == null && targetNode.right == null) {
            if (parentNode.left != null && parentNode.left.value == value) {
                parentNode.left = null;
            } else if (parentNode.right != null && parentNode.right.value == value) {
                parentNode.right = null;
            }
        } else if (targetNode.left != null && targetNode.right != null) {//有两颗子树的情况
            //这里解释一下,节点的右子树不管还有多少课子树,右子树所有节点的value都要小于该节点的value
            int minValue = delMinNodes(targetNode.right);
            targetNode.value = minValue;
        } else {//只有一棵子树的情况
            if (targetNode.left != null) {
                if (parentNode!=null){
                    if (parentNode.left.value == value) {
                        parentNode.left = targetNode.left;
                    } else {
                        parentNode.right = targetNode.left;
                    }
                }else{
                    targetNode = targetNode.left;
                }
            } else {
                if (parentNode!=null) {
                    if (parentNode.left.value == value) {
                        parentNode.left = targetNode.right;
                    } else {
                        parentNode.right = targetNode.right;
                    }
                }else{
                    targetNode=targetNode.right;
                }
            }
        }
    }

平衡二叉树(AVL树)

二叉排序树存在的问题:在这里插入图片描述

左旋转
在这里插入图片描述
右旋转
在这里插入图片描述

package datastructres.tree;

/**
 * @author :Yan Guang
 * @date :Created in 2021/2/9 10:11
 * @description:
 */
public class AVLTree {
    public static void main(String[] args) {
        int[] arr={3,6,5,7,8};
        Nodess nodess = new Nodess(4);
        for (int data : arr) {
            nodess.add(new Nodess(data));
        }
        nodess.zhongxu();
        System.out.println();
        System.out.println(nodess.height());
        System.out.println(nodess.leftHeight());
        System.out.println(nodess.rightHeight());
    }
}

class Nodess {
    int value;
    Nodess left;
    Nodess right;

    public Nodess(int value) {
        this.value = value;
    }

    public Nodess() {
    }

    @Override
    public String toString() {
        return "Nodess{" +
                "value=" + value +
                '}';
    }

    //返回左子树的高度
    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;
    }

    //左旋转
    private void leftRotate(){
        Nodess newNode = new Nodess(value);
        newNode.left=left;
        newNode.right=right.left;
        value=right.value;
        right=right.right;
        left=newNode;
    }

    //右旋转
    private void rightRotate(){
        Nodess newNode =new Nodess(value);
        newNode.right=right;
        newNode.left=left.right;
        value=left.value;
        left=left.left;
        right=newNode;
    }

    //查找待删除节点
    public Nodess search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left != null) {
                //这里注意查找到的值一定要回溯回去
                return this.left.search(value);
            } else {
                return null;
            }
        } else {
            if (this.right != null) {
                return this.right.search(value);
            } else {
                return null;
            }
        }
    }

    //查找待删除节点父节点
    public Nodess searchParent(int value) {
        //这里注意顺序一定是先判断是否为空,再判断是否等于value,不然就会报空指针异常
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);
            } else {
                return null;
            }
        }
    }

    public int delMinNodess(Nodess Nodess) {
        Nodess target = Nodess;
        while (target.left != null) {
            target = target.left;
        }
        deleteNodess(target.value);
        //这里的target是一个辅助指针,所以实际上删除的是真实树的节点
        //这里可以直接返回
        return target.value;
    }

    public void deleteNodess(int value) {
        Nodess targetNode = search(value);
        Nodess parentNode = searchParent(value);
        if (targetNode == null) {
            return;
        }
        if (parentNode.left == null && parentNode.right == null) {
            return;
        }
        //如果是叶子节点的情况
        if (targetNode.left == null && targetNode.right == null) {
            if (parentNode.left != null && parentNode.left.value == value) {
                parentNode.left = null;
            } else if (parentNode.right != null && parentNode.right.value == value) {
                parentNode.right = null;
            }
        } else if (targetNode.left != null && targetNode.right != null) {//有两颗子树的情况
            //这里解释一下,节点的右子树不管还有多少课子树,右子树所有节点的value都要小于该节点的value
            int minValue = delMinNodess(targetNode.right);
            targetNode.value = minValue;
        } else {//只有一棵子树的情况
            if (targetNode.left != null) {
                if (parentNode != null) {
                    if (parentNode.left.value == value) {
                        parentNode.left = targetNode.left;
                    } else {
                        parentNode.right = targetNode.left;
                    }
                } else {
                    targetNode = targetNode.left;
                }
            } else {
                if (parentNode != null) {
                    if (parentNode.left.value == value) {
                        parentNode.left = targetNode.right;
                    } else {
                        parentNode.right = targetNode.right;
                    }
                } else {
                    targetNode = targetNode.right;
                }
            }
        }
    }

    public void add(Nodess node) {
        if (node == null) {
            return;
        }
        if (node.value < this.value) {
            if (this.left == null) {
                this.left = node;
            } else {
                this.left.add(node);
            }
        } else {
            if (this.right == null) {
                this.right = node;
            } else {
                this.right.add(node);
            }
        }
        //右边高,左旋转,左边高,右旋转
        if ((rightHeight()-leftHeight())>1){
            leftRotate();
        }
        if ((leftHeight()-rightHeight())>1){
            rightRotate();
        }
    }

    public void zhongxu() {
        if (this.left != null) {
            this.left.zhongxu();
        }
        System.out.print(this.value + "\t");
        if (this.right != null) {
            this.right.zhongxu();
        }
    }
}

在这里插入图片描述

//右边高,左旋转,左边高,右旋转
        //左旋转
        if ((rightHeight()-leftHeight())>1){
            if (right!=null&&right.leftHeight()>right.rightHeight()){
                right.rightRotate();
                leftRotate();
            }else {
                leftRotate();
            }
        }
        //右旋转
        if ((leftHeight()-rightHeight())>1){
            if (left!=null&&left.rightHeight()>left.leftHeight()){
                left.leftRotate();
                rightRotate();
            }else {
                rightRotate();
            }
        }

多路查找树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B树

在这里插入图片描述

2-3树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里比较繁琐,但是其实就是需要满足2-3树的要求即可叶子节点的高度必须相同,而且节点必须要满度

2-3-4树

在这里插入图片描述

B+树

在这里插入图片描述

B*树

在这里插入图片描述

树结构的应用

堆排序

在这里插入图片描述
在这里插入图片描述
思想:
在这里插入图片描述
在这里插入图片描述

public class HeapSort {
    public static void main(String[] args) {
        int arr[] = {4,6,8,5,9};
        //1、我们首先把普通数组转换成为一个大顶堆
        for (int i=arr.length/2-1;i>=0;i--){
            adjustHeap(arr,i,arr.length);
        }
        //2、每次将大顶堆元素放到数组最后,放完之后重新把剩下的元素调为大顶堆
        //这里因为每次调整之后其实只是把最大的放到数组尾部就可以了,所以其实并不用每次都转换成一个大顶堆
        //只用把最大的数放到数组的末部就可以了,可以画草图理解
        for (int i=arr.length-1;i>0;i--){
            int temp = arr[i];
            arr[i]=arr[0];
            arr[0]=temp;
            adjustHeap(arr,0,i);
        }
        System.out.println(Arrays.toString(arr));
    }
    public static void adjustHeap(int arr[], int i, int length) {
        int temp = arr[i];
        //把数据最大的值放到堆顶
        for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
            if (arr[k] < arr[k + 1] && k + 1 < length) {
                k++;
            }
            if (arr[k] > temp) {
                arr[i] = arr[k];
                i = k;
            } else {
                break;
            }
            arr[k] = temp;
        }
    }
}

堆排序分两种,如果是要改为升序就用大顶堆,降序都用小顶堆,思路都差不多,这里比方是用升序,就一开始把最大的数放到堆顶,然后每次再交换到数组末端就可以了!

堆排序 速度极快!!!

哈夫曼树/赫夫曼树

在这里插入图片描述
例子:
在这里插入图片描述
在这里插入图片描述
WLP的值越小的就是哈夫曼树

在这里插入图片描述
创建结果如下,就是每次把组合先求和,小的放左边,大的放右边
在这里插入图片描述

public class Huffman {
    public static void main(String[] args) {
        int[] arr = {13,7,8,3,29,6,1};
        Node head = createHuffman(arr);
        head.preOrder();
    }

    //创建哈夫曼树
    public static Node createHuffman(int[] arr){
        ArrayList<Node> list = new ArrayList<>();
        for (int data : arr) {
            list.add(new Node(data));
        }
        //因为当最后一个数存入list的时候其实就已经结束了
        while(list.size()>1) {
            //将list进行排序
            Collections.sort(list);
            //开始构建新的树
            Node liftNode = list.get(0);
            Node rightNode = list.get(1);
            Node parent = new Node(liftNode.val + rightNode.val);
            parent.left = liftNode;
            parent.right = rightNode;
            //删除list中已经用过的节点
            list.remove(liftNode);
            list.remove(rightNode);
            //将parent加入节点
            list.add(parent);
        }
        //因为此时list中只存在最后一个最大的权的头结点了,于是返回就可以了
        return list.get(0);
    }
}

class Node implements Comparable<Node>{
     int val;
     Node left;
     Node right;

    public Node(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return "Node{" +
                "val=" + val +
                '}';
    }

    @Override
    public int compareTo(Node o) {
        return this.val-o.val;
    }

    //前序遍历
    public void preOrder(){
        System.out.print(this.val+"\t");
        if (this.left!=null){
            this.left.preOrder();
        }
        if (this.right!=null){
            this.right.preOrder();
        }
    }
}

哈夫曼树的一个创建可以说是十分简单的啊,就是一个写一个节点,然后自己递归随便先写一个遍历的方式,然后可以开始写代码了,总过就是用一个while循环就可以搞定了啊,一开始先把数组封装成一个Node并且按照降序排列的list,然后每次就把list最前面的0和1取出然后再新创建一个parent节点,然后删除两个利用过得节点再list中,最后再把创建的节点直接压入list就可以了!!!记得最后list中会留一个头节点,也是我们退出循环的条件!!!

哈夫曼编码

在这里插入图片描述
原理:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
连接在这里朋友们,因为个人原因,我先跳了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值