数据结构与算法

**数据结构的分类 **

线性结构和非线性结构

在这里插入图片描述

二叉树数据结构

二叉树

在这里插入图片描述

二叉树(Binary Tree)是一种树形结构,它的特点是每个节点最多只有两个分支节点,一棵二叉树通常由根节点,分支节点,叶子节点组成。而每个分支节点也常常被称作为一棵子树。

在这里插入图片描述

根节点:二叉树最顶层的节点分支节点:除了根节点以外且拥有叶子节点叶子节点:除了自身,没有其他子节点
常用术语在二叉树中,我们常常还会用父节点和子节点来描述,比如图中2为6和3的父节点,反之6和3是2子节点

2、二叉树的性质

性质1:二叉树第i层上的结点数目最多为2^(i-1)(i>=1)

性质2:深度为k的二叉树至多有2^k -1个结点(k>=1)

性质3:包含n个结点的二叉树的高度至少为(log2^n)+1

性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

3、性质4的证明

性质4:在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

证明:因为二叉树中所有结点的度数均不大于2,不妨设n0表示度为0的结点个数,n1表示度为1的结点个数,n2表示度为2的结点个数。三类结点加起来为总结点个数,于是便可得到:n=n0+n1+n2 (1)

由度之间的关系可得第二个等式:n=n0x0+n1+n2x2+1即n=n1+2nx2+1 (2) (节点数等于度加1)

将(1)(2)组合在一起可得到n0=n2+1

三、满二叉树、完全二叉树和二叉查找树

1、满二叉树

定义:高度为h,并且由2^h-1个结点组成的二叉树,称为满二叉树

img

2、完全二叉树

定义:一棵二叉树中,只有最下面两层结点的度可以小于2,并且最下层的叶结点集中在靠左()的若干位置上,这样的二叉树称为完全二叉树。

完全二叉树,可以看做是满二叉树在最后一层从右往左砍掉一些节点。

如果从满二叉树中在最后一层自左向右砍掉的节点数是偶数,那么该完全二叉树中度为1的节点数就是0。

如果砍掉的节点数是奇数,那么该完全二叉树中就有且仅有一个节点的度为1.

img

面试题:如果一个完全二叉树的结点总数为768个,求叶子结点的个数。

由二叉树的性质知:n0=n2+1,将之带入768=n0+n1+n2中得:768=n1+2n2+1,因为完全二叉树度为1的结点个数要么为0,要么为1,那么就把n1=0或者1都代入公式中,很容易发现n1=1才符合条件。所以算出来n2=383,所以叶子结点个数n0=n2+1=384。

总结规律:如果一棵完全二叉树的结点总数为n,那么叶子结点等于n/2(当n为偶数时)或者(n+1)/2(当n为奇数时)

3、二叉查找树

定义:二叉查找树又被称为二叉搜索树。设x为二叉查找树中的一个结点,x结点包含关键字key,结点x的key值计为key[x]。如果y是x的左子树中的一个结点,则key[y]<=key[x];如果y是x的右子树的一个结点,则key[y]>=key[x]

img

在二叉查找树中:

(1)若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值。

(2)任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值。

(3)任意结点的左、右子树也分别为二叉查找树。

(4)没有键值相等的结点。

二叉树的遍历

在这里插入图片描述

在这里插入图片描述

根据两个遍历写二叉树(前加中或者后加中才可以写出二叉树)
在这里插入图片描述

//因为根据前序遍历可以得到A是根节点
在这里插入图片描述

将a的左子树单独拿出来分析该子树
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

用程序实现二叉树遍历的步骤

在这里插入图片描述

package 算法;
//二叉树的遍历、节点、二叉树的定义
public class BinaryTreeDemo {
    public static void main(String[] args) {
      //测试,先创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的节点
        HeroNode node1 =new HeroNode(1,"jack");
        HeroNode node2 =new HeroNode(2,"lucy");
        HeroNode node3 =new HeroNode(3,"tom");
        HeroNode node4 =new HeroNode(4,"gogo");
        HeroNode node5 =new HeroNode(5,"dd");

        //说明,先手动创建二叉树,以后再学用递归的方法创建二叉树
        binaryTree.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        //测试前序遍历
        System.out.println("前序遍历");
        binaryTree.preOrder();// 1 2 3 5 4
        System.out.println("中序遍历");
        binaryTree.midOrder(); //2 1 5 3 4
        System.out.println("后序遍历");
        binaryTree.postOrder();// 2 5 4 3 1
    }

}

//先创建 HeroNode节点
class  HeroNode{
     private int no;
     private  String name;
     private  HeroNode left;
     private  HeroNode right;
     public HeroNode(int no,String name){
         this.no = no;
         this.name = name;
     }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }
    //重写toString()方法,用于输出
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
    //编写前序遍历的方法
    public void preOrder(){
        System.out.println(this);//先输出父节点
        //递归向左子树递归前序遍历
        if(this.left !=null){
             this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
             this.right.preOrder();
        }
    }
    //编写中序遍历
    public void midOrder(){
         //递归向左子树中序遍历
         if(this.left!=null){
              this.left.midOrder();
         }
         //输出父节点
        System.out.println(this);
         //递归向右子树中序遍历
        if(this.right!=null){
             this.right.midOrder();
        }
    }
    // 编写后序遍历
    public  void postOrder(){
          if (this.left!=null){
              this.left.postOrder();
          }
          if(this.right!=null){
               this.right.postOrder();
          }
        System.out.println(this);
    }

}

//定义BinaryTree 二叉树
class BinaryTree{
    private  HeroNode root;
    //设置根节点,通过根节点调用节点的方法可以增加节点
    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //树的前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空无法遍历");
        }
    }
    //书的中序遍历
    public void midOrder(){
        if(this.root!=null){
            this.root.midOrder();
        }else {
            System.out.println("二叉树为空无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if(this.root!=null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空无法遍历");
        }
    }
}

在这里插入图片描述

二叉树的前序查找 中序查找 后序查找

package 算法;

public class BinaryTreeDemo {
    public static void main(String[] args) {
      //测试,先创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的节点
        HeroNode node1 =new HeroNode(1,"jack");
        HeroNode node2 =new HeroNode(2,"lucy");
        HeroNode node3 =new HeroNode(3,"tom");
        HeroNode node4 =new HeroNode(4,"gogo");
        HeroNode node5 =new HeroNode(5,"dd");

        //说明,先手动创建二叉树,以后用递归的方法创建二叉树
        binaryTree.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        //测试前序遍历
        System.out.println("前序遍历");
        binaryTree.preOrder();// 1 2 3 5 4
        System.out.println("中序遍历");
        binaryTree.midOrder(); //2 1 5 3 4
        System.out.println("后序遍历");
        binaryTree.postOrder();// 2 5 4 3 1

        //测试前序查找
        System.out.println("前序查找");
        System.out.println(binaryTree.preOrderSearch(3));
        //测试中序查找
        System.out.println("中序查找");
        System.out.println(binaryTree.midOrderSearch(3));
        //测试后序查找
        System.out.println("后序查找");
        System.out.println(binaryTree.postOrderSearch(3));
    }

}

//先创建 HeroNode节点
class  HeroNode{
     private int no;
     private  String name;
     private  HeroNode left;
     private  HeroNode right;
     public HeroNode(int no,String name){
         this.no = no;
         this.name = name;
     }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }
    //重写toString()方法,用于输出
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
    //编写前序遍历的方法
    public void preOrder(){
        System.out.println(this);//先输出父节点
        //递归向左子树递归前序遍历
        if(this.left !=null){
             this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
             this.right.preOrder();
        }
    }
    //编写中序遍历
    public void midOrder(){
         //递归向左子树中序遍历
         if(this.left!=null){
              this.left.midOrder();
         }
         //输出父节点
        System.out.println(this);
         //递归向右子树中序遍历
        if(this.right!=null){
             this.right.midOrder();
        }
    }
    // 编写后序遍历
    public  void postOrder(){
          if (this.left!=null){
              this.left.postOrder();
          }
          if(this.right!=null){
               this.right.postOrder();
          }
        System.out.println(this);
    }
    //前序遍历查找
    /*no 查找no
    如果找到就返回Node,如果没有就返回null
    * */
    public HeroNode preOrderSearch(int no){
        //比较当前节点是不是要查找的元素
        System.out.println("前序遍历查找");//可以通过该语句判断查找的次数,对于不同顺序的查找法,该语句位置不同,应该放在根节点判断上一句
        if(this.no==no){
            return this;
        }
        //判断当前节点的左子树是否为空,如果不为空,执行前序查找
        // 2如果左递归前序查找,找到结点,就返回
        //创建一个节点接收查找到的结点
        HeroNode resNode = null;
        if(this.left!=null){
            resNode=this.left.preOrderSearch(no);
        }
        if(resNode !=null){
            //说明左子树找到
            return resNode;
        }
        //如果左递归前序查找没找到,就判断当前节点的
        //右节点是否为空,如果不空,则向右递归前序查找
        if(this.right!=null){
            resNode= this.right.preOrderSearch(no);
        }
        return resNode;
    }

    //中序查找遍历
    public  HeroNode midOrderSearch(int no){
         HeroNode resNode = null;
        if(this.left!=null){
            resNode=this.left.midOrderSearch(no);
        }
        //如果左节点的递归中序遍历没有找到
        if(resNode !=null){
            return resNode;
        }
        System.out.println("进入中序1查找");
        if(this.no==no){
            return  this;
        }
        if(this.right!=null){
            resNode=this.right.midOrderSearch(no);
        }
        return resNode;
    }
    //后序查找
    public HeroNode postOrderSearch(int no){
        HeroNode resNode = null;
        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;
        }
        //如果左右子树都没有
        System.out.println("进入后序查找");
        if(this.no==no){
            return this;
        }
        return resNode;
    }

}

//定义BinaryTree 二叉树
class BinaryTree{
    private  HeroNode root;
    //设置根节点,通过根节点调用节点的方法可以增加节点
    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //树的前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else{
            System.out.println("二叉树为空无法遍历");
        }
    }
    //树的中序遍历
    public void midOrder(){
        if(this.root!=null){
            this.root.midOrder();
        }else {
            System.out.println("二叉树为空无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if(this.root!=null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空无法遍历");
        }
    }
    //前序查找
    public HeroNode preOrderSearch(int no){
        if(root!=null){
        return root.preOrderSearch(no);
    }else {
            return null;
          }
    }
    //中序查找
    public HeroNode midOrderSearch(int no){
        if(root!=null){
            return root.midOrderSearch(no);
        }else {
            return null;
        }
    }
    //后序查找
    public HeroNode postOrderSearch(int no){
        if(root!=null){
            return root.postOrderSearch(no);
        }else {
            return null;
        }
    }
}

二叉树删除节点

在这里插入图片描述

在这里插入图片描述

//二叉树删除指定元素

package 算法;

public class BinaryTreeDemo {
    public static void main(String[] args) {
      //测试,先创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创建需要的节点
        HeroNode node1 =new HeroNode(1,"jack");
        HeroNode node2 =new HeroNode(2,"lucy");
        HeroNode node3 =new HeroNode(3,"tom");
        HeroNode node4 =new HeroNode(4,"gogo");
        HeroNode node5 =new HeroNode(5,"dd");

        //说明,先手动创建二叉树,以后用递归的方法创建二叉树
        binaryTree.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
    

        //测试删除
        System.out.println("删除前的前序遍历");
        binaryTree.preOrder();
        System.out.println("删除后的前序遍历");
        binaryTree.delNode(3);
        binaryTree.preOrder();
    }

}

//先创建 HeroNode节点
class  HeroNode{
     private int no;
     private  String name;
     private  HeroNode left;
     private  HeroNode right;
     public HeroNode(int no,String name){
         this.no = no;
         this.name = name;
     }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }
    //重写toString()方法,用于输出
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
    //编写前序遍历的方法
    public void preOrder(){
        System.out.println(this);//先输出父节点
        //递归向左子树递归前序遍历
        if(this.left !=null){
             this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
             this.right.preOrder();
        }
    }
    //编写中序遍历
    public void midOrder(){
         //递归向左子树中序遍历
         if(this.left!=null){
              this.left.midOrder();
         }
         //输出父节点
        System.out.println(this);
         //递归向右子树中序遍历
        if(this.right!=null){
             this.right.midOrder();
        }
    }
    // 编写后序遍历
    public  void postOrder(){
          if (this.left!=null){
              this.left.postOrder();
          }
          if(this.right!=null){
               this.right.postOrder();
          }
        System.out.println(this);
    }
  。9k
    //删除指定节点
    //如果要删除的节点是叶子结点,就直接删除
    //如果要删除的节点是非叶子节点,就删除该子树
    //这里只进行下面5点,提前操作放在二叉树执行
    /*
    *  预处理:考虑如果删除后是空树,则等价将二叉树置空
    *
    *    //然后进行下面步骤
    *    1:因为二叉树是单向的,所以我们是判断当前节点的子节点是否为要删除的节点
    *    而不能去判断当前结点是不是要删除的结点(this=null会有编译错误)
    *    2:如果当前结点的子节点不为空,并且左子节点是要删除的结点,就将this.left=null,
    *    并且就返回(结束递归删除)
    *    3如果当前结点的子节点不为空,并且右子节点是要删除的结点,就将this.right=null,
    *    并且就返回(结束递归删除)
    *    4如果第2步和第三步没有删除结点,需要向左子树递归删除
    *    5如果第4步没有删除结点,则应该向右子树删除结点
    * */
    public void delNode(int no){
        //1先要判断左子节点是否为空,防止空指针异常,在判断左子节点是否为要删除的元素
       if(this.left!=null&&this.left.no==no){
           this.left=null;
           return;
       }
       //2判断右子子节点是否为空。在判断右子节点是否为要删除的节点
        if(this.right!=null&&this.right.no==no){
            this.right=null;
            return;
        }
        //3如果上面两步都没有成功删除指定的节点,向左子树进行递归删除
        if(this.left!=null){
             this.left.delNode(no);
        }
        //如果第三步也没有删除指定的元素,那就进行右子树递归删除
        if(this.right!=null){
            this.right.delNode(no);
        }

    }

}

//定义BinaryTree 二叉树
class BinaryTree{
    private  HeroNode root;
    //设置根节点,通过根节点调用节点的方法可以增加节点
    public void setRoot(HeroNode root) {
        this.root = root;
    }
 
    //删除节点
    public void delNode(int no){

        //如果root是要删除的节点

        if(root!=null) {
            //判断根节点是否为空
            if (root.getNo() == no) {
                root = null;
            } else {
                root.delNode(no);
            }

        }else {
            System.out.println("空树,不能删除");
        }
    }
}

顺序存储二叉树 数组和二叉树的相互转换

在这里插入图片描述

顺序二叉树的特点

​ n是从0开始的,表示二叉树中的第几个元素,而且在做第四点运算时去掉小数部分取整

在这里插入图片描述

在这里插入图片描述

用数组实现顺序存储二叉树,二叉树以不同遍历方式进行遍历,前序遍历的结果是 1 2 4 5 3 6 7

 package 算法;

public class ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int[]arr = {1,2,3,4,5,6,7};
        ArrBinaryTree arrTree = new ArrBinaryTree(arr);
        //前序遍历
        arrTree.preOrder();
        System.out.println("中序遍历");
        arrTree.midOrder();
        System.out.println("后序遍历");
        arrTree.postOrder();
    }
}

class ArrBinaryTree{
    private int []arr; //存储数据的数组

    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }
    //方法重载
    public  void  preOrder(){
        this.preOrder(0);
    }

    //编写一个顺序存储二叉树的前序遍历 index 是数组的下标
    public void preOrder(int index){
        //如果数组为空,或者arr.length=0(数组为空是指向空,长度为0是数组没有数据)
       if(arr== null ||arr.length==0){
           System.out.println("数组为空,不能按照二叉树的前序遍历");
       }
       //输出当前的这个元素
        System.out.println(arr[index]);
       //向左递归遍历
        //判断左子节点的索引是否大于数组长度
        if(index*2+1<arr.length){
             preOrder(2*index+1);
        }
        //向右递归遍历
        if(index*2+2<arr.length){
            preOrder(2*index+2);
        }

    }
     //中序遍历
     public void midOrder(){
         this.midOrder(0);
     }
    public void midOrder(int index){
        if(arr==null||arr.length==0){
            System.out.println("该数组为空,无法进行二叉树的中序遍历");
        }
        if(index*2+1<arr.length){
            midOrder(2*index+1);
        }
        System.out.println(arr[index]);
        if(index*2+2<arr.length){
            midOrder(index*2+2);
        }
    }

    //后序遍历
    public void postOrder(){
         this.postOrder(0);
    }
    public void postOrder(int index){
        if(arr==null||arr.length==0){
            System.out.println("数组为空");
        }
        if(index*2+1<arr.length){
            postOrder(index*2+1);
        }
        if(index*2+2<arr.length){
            postOrder(index*2+2);
        }
        System.out.println(arr[index]);
    }
}

//线索化二叉树 充分利用空指针域
在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

中序遍历线索化二叉树

在这里插入图片描述

package 算法.线索化二叉树;

public class BinaryTreeDemo2 {
    public static void main(String[] args) {
        //测试
        HeroNode node1 = new HeroNode(1,"tom");
        HeroNode node2 = new HeroNode(3,"jack");
        HeroNode node3 = new HeroNode(6,"smith");
        HeroNode node4 = new HeroNode(8,"ddd");
        HeroNode node5 = new HeroNode(10,"gogo");
        HeroNode node6 = new HeroNode(14,"old");
        //手动创建线索二叉树
        BinaryTree tree = new BinaryTree();
        tree.setRoot(node1);
        node1.setLeft(node2);
        node1.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        tree.threadedNode();
        //以10号为例,看前驱结点是否为3
        HeroNode leftNoed = node5.getLeft();
        System.out.println(leftNoed);
        //后继结点
        HeroNode rightNode = node5.getRight();
        System.out.println(rightNode);
        tree.threadList();
    }
}

//对于线索化二叉树,应该新增属性来区分左右指针指向的类型,是子节点还是前驱后继节点
class  HeroNode {
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;
    //如果 leftType为0,则指向左子节点  如果为1 则指向左前驱节点
    //如果 rightType为0 ,则指向右子节点,如果为1,则指向右后继节点
    private int leftType;
    private int rightType;
    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }



    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

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

    public HeroNode getRight() {
        return right;
    }

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

    //重写toString()方法,用于输出
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }


}
//定义BinaryTree 中序线索化二叉树
class BinaryTree{
    private HeroNode root;

    //为了实现线索化二叉树,需要创建一个指向当前节点的前驱节点的引用,才可以完成使当前节点指向前驱节点的操作
    private HeroNode pre =null;
    //设置根节点,通过根节点调用节点的方法可以增加节点
    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //重载
     public void threadedNode(){
         this.threadedNode(root);
     }
    //传入参数是需要线索化的节点
    public void threadedNode(HeroNode node){
        //如果node==null,不能线索化
        if(node==null){
          return;
        }
        //中序线索化
        //1线索化左子树
        threadedNode(node.getLeft());
        //2线索化当前节点
        /* 2.1 先处理当前节点的前驱结点
            2.2 处理后继结点
            2.3每处理一个结点,当当前结点时下一个结点的前驱端点
         * */
        //2.1
        if(node.getLeft()==null){
            //让当前结点的左指针指向前驱结点
            node.setLeft(pre);
            //修改当前结点的左指针类型
            node.setLeftType(1);
        }
        //2.2
           //对于第一个结点,没有前驱结点,它的前驱结点为空
         if(pre!=null&&pre.getRight()==null){
             //前驱结点的右指针指向当前结点
             pre.setRight(node);
             pre.setRightType(1);
         }
         //2.3
          pre=node;
        //3线索化右子树
        threadedNode(node.getRight());
    }
    //定义遍历线索化二叉树的方法
    public void threadList(){
         //定义一个node,存储当前的结点,从root开始
        HeroNode node = root;
         while (node!=null){
             //循环的找到找到leftType==0的结点,第一个找到的就是8结点
             //后面随着遍历而变化,因为当lifeType等于1时,说明该节点是按照线索化
             //处理后的有效结点

             //先找到第一个点
             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();
         }
    }


}

赫夫曼树

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

0

在这里插入图片描述

利用数组创建赫夫曼树

package 算法.树;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HuffmanTree {
    public static void main(String[] args) {
     int [] arr={13,7,8,3,29,6,1};
     preOrder(createHuffmanTree(arr));


    }
    //参数 需要创建赫夫曼树的数组,返会赫夫曼树的根节点
    public static Node createHuffmanTree(int[] arr){
        List<Node> list = new ArrayList<>();
         //把结点存入list,并用colletions,sort根据权排序
        for (int i = 0; i <arr.length ; i++) {
            list.add(new Node(arr[i]));
        }
        //排序
        Collections.sort(list);
        //循环处理
        //最后list中只剩下赫夫曼树的头节点
        while (list.size()>1) {

            //取出节点数权最少的两个二叉树
            Node leftNode = list.get(0);
            Node rightNode = list.get(1);
            //构建一颗新的二叉树,根节点权值是两颗二叉树的权值之和
            Node parent = new Node(leftNode.value + rightNode.value);
            parent.left = leftNode;
            parent.right = rightNode;
            //从Array中剔除处理过的两颗二叉树
            list.remove(leftNode);
            list.remove(rightNode);
            list.add(parent);
            Collections.sort(list);
        }
        //最终只用返回赫夫曼树的头节点,就成功创建一颗赫夫曼树了
        return list.get(0);
    }

    //编写一个调用结点前序遍历的方法
    public static void preOrder(Node root){
        if(root==null){
            System.out.println("赫夫曼树为空");
        }else {
            root.preorder();
        }
    }


}

//结点类
//为了让Node支持Collection排序,应该实现Comparable接口
class Node implements Comparable<Node>{
    int value;
    Node left;
    Node right;

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

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

    @Override
    public int compareTo(Node o) {
        //从小到大进行排序
        return this.value-o.value;
    }
    //写一个前序遍历
    public void preorder(){
        System.out.println(this);
        if(this.left!=null){
            this.left.preorder();
        }
        if(this.right!=null){
            this.right.preorder();
        }
    }
}

二叉排序树
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

二叉排序树

package 算法.树;

public class BinarySortTreeDemo {
    public static void main(String[] args) {
      int[] arr={7,3,10,12,5,1,9};
      BinarySortTree binarySortTree = new BinarySortTree();
        for (int i = 0; i <arr.length ; i++) {
            binarySortTree.add(new SortTreeNode(arr[i]));
        }
        binarySortTree.midOrder();
    }
}
//创建二叉排序树
class BinarySortTree{
     private SortTreeNode root;
     //添加结点的方法
    public void  add(SortTreeNode node){
        if (root==null){
            root=node;
        }else {
            root.add(node);
        }
    }
    //中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
    public void midOrder(){
        if(root!=null){
            root.midOrder();
        }else {
            System.out.println("排序树为空");
        }
    }
}

class SortTreeNode{
    int value;
    SortTreeNode left;
    SortTreeNode right;

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

    //添加结点的方法,要满足二叉排序树的要求
    public void  add (SortTreeNode 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  midOrder(){
        if (this.left!=null){
            this.left.midOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.midOrder();
        }
    }

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

删除二叉排序树的结点
在这里插入图片描述

删除一个子节点的时候还要考虑删除结点是根节点时候的情况(没有父节点)

在这里插入图片描述

在这里插入图片描述

package 算法.树;

public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new SortTreeNode(arr[i]));
}
binarySortTree.midOrder();
binarySortTree.delNode(2);
binarySortTree.delNode(5);
binarySortTree.delNode(9);
binarySortTree.delNode(12);
binarySortTree.delNode(7);
binarySortTree.delNode(3);
binarySortTree.delNode(10);

     binarySortTree.delNode(1);

    System.out.println("");
    binarySortTree.midOrder();
    System.out.println(binarySortTree.getRoot());
}

}

//创建二叉排序树
class BinarySortTree {
private SortTreeNode root;

//添加结点的方法
public void add(SortTreeNode node) {
    if (root == null) {
        root = node;
    } else {
        root.add(node);
    }
}

public SortTreeNode getRoot() {
    return root;
}

//查找要删除的结点
public SortTreeNode search(int value) {
    if (root == null) {
        return null;
    } else {
        return root.search(value);
    }
}

//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
    if (root == null) {
        return null;
    } else {
        return root.searchParent(value);
    }
}

//删除结点
public void delNode(int value) {
    if (root == null) {
        return;
    } else {
        //找到要删除的结点
        SortTreeNode targetNode = root.search(value);
        if (targetNode == null) {
            //没找到
            return;
        }
        //如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
        if (root.left == null && root.right == null) {
            root = null;
            return;
        }

        //根据三种情况开始判断。
        SortTreeNode parentNode = root.searchParent(value);
        //第一种情况如果要删除的结点是叶子结点
        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) {
            //先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
            SortTreeNode temp = targetNode.right;
            while (temp.left != null) {
                temp = temp.left;//循环找到targetNode右子树的最小值
            }
            //删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
            delNode(temp.value);
            //把temp中的数据赋给target
            targetNode.value = temp.value;

        } else {
            //删除的结点只有一个结点

            if (targetNode.left != null) {//删除结点的左子节点不空
                //要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
                if (parentNode != null) {//父节点不为空
                    if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                        parentNode.left = targetNode.left;
                    } else {
                        parentNode.right = targetNode.left;
                    }
                } else {
                    root = targetNode.left;
                }
            } else {//targetNode的右子节点不为空
                if (parentNode != null) {
                    if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                        parentNode.left = targetNode.right;
                    } else {
                        parentNode.right = targetNode.right;
                    }
                } else {
                    root=targetNode.right;
                }

            }
        }
    }

}

//中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
public void midOrder() {
    if (root != null) {
        root.midOrder();
    } else {
        System.out.println("排序树为空");
    }
}

}

class SortTreeNode {
int value;
SortTreeNode left;
SortTreeNode right;

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

//查找要删除结点的方法
public SortTreeNode search(int value) {
    if (this.value == value) {
        return this;
    } else if (value < this.value) {
        if (this.left == null) {
            return null;
        }
        return this.left.search(value);
    } else {//要查找的值不小于当前结点的value
        if (this.right == null) {
            return null;
        }
        return this.right.search(value);
    }
}

//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
    if ((this.left != null && this.left.value == value) ||
            (this.right != null && this.right.value == value)) {
        return this;
    } else {
        if (this.left != null && value < this.value) {
            return this.left.searchParent(value);
        } else if (this.right != null && value >= this.value) {
            return this.right.searchParent(value);

        } else {
            return null;//没有父节点
        }
    }

}

//添加结点的方法,要满足二叉排序树的要求
public void add(SortTreeNode 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 midOrder() {
    if (this.left != null) {
        this.left.midOrder();
    }
    System.out.println(this);
    if (this.right != null) {
        this.right.midOrder();
    }
}

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

}

平衡二叉树

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

package 算法.树.平衡二叉树;


public class AVLTreeDemo {
    public static void main(String[] args) {
        //int[] arr = {4,3,6,5,7,8};
        int[] arr = {10,12,8,9,7,6};
        AVLTree avlTree= new AVLTree();
        for (int i = 0; i <arr.length ; i++) {
            avlTree.add(new AVLTreeNode(arr[i]));
        }
        avlTree.midOrder();
        System.out.println(avlTree.getRoot().height());
        System.out.println(avlTree.getRoot().leftHeight());
        System.out.println(avlTree.getRoot().rightHeight());
    
    }
}

//创建AVLTree
class AVLTree {
    private AVLTreeNode root;



    //左旋方法


    //增加查找当前结点子树高度的方法
    //添加结点的方法
    public void add(AVLTreeNode node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

    public AVLTreeNode getRoot() {
        return root;
    }

    //查找要删除的结点
    public AVLTreeNode search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //查找要删除结点的父节点
    public AVLTreeNode searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //删除结点
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            //找到要删除的结点
            AVLTreeNode targetNode = root.search(value);
            if (targetNode == null) {
                //没找到
                return;
            }
            //如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }

            //根据三种情况开始判断。
            AVLTreeNode parentNode = root.searchParent(value);
            //第一种情况如果要删除的结点是叶子结点
            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) {
                //先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
                AVLTreeNode temp = targetNode.right;
                while (temp.left != null) {
                    temp = temp.left;//循环找到targetNode右子树的最小值
                }
                //删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
                delNode(temp.value);
                //把temp中的数据赋给target
                targetNode.value = temp.value;

            } else {
                //删除的结点只有一个结点

                if (targetNode.left != null) {//删除结点的左子节点不空
                    //要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
                    if (parentNode != null) {//父节点不为空
                        if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                            parentNode.left = targetNode.left;
                        } else {
                            parentNode.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else {//targetNode的右子节点不为空
                    if (parentNode != null) {
                        if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                            parentNode.left = targetNode.right;
                        } else {
                            parentNode.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }

                }
            }
        }

    }

    //中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
    public void midOrder() {
        if (root != null) {
            root.midOrder();
        } else {
            System.out.println("排序树为空");
        }
    }
}

class AVLTreeNode {
    int value;
    AVLTreeNode left;
    AVLTreeNode right;

    public AVLTreeNode(int value) {
        this.value = value;
    }
    //右旋的方法
    private void rightRotate(){
        AVLTreeNode newNode =  new AVLTreeNode(value);
        newNode.right=right;
        newNode.left=left.right;
        value=left.value;
        left=left.left;
        right=newNode;
    }

    //左旋的方法
    private void leftRotate(){
        //根据当前根节点的值创建一个新的结点
        AVLTreeNode newNode= new AVLTreeNode(value);
        //新结点的左子树指向根节点的左子树
        newNode.left=left;
        //新结点的右子树指向根节点的右子树的左子树
        newNode.right= this.right.left;
        //把当前根节点的值替换成它右子树的值
        this.value=right.value;
        //当前结点的右子树设置成右子树的右子树
        this.right=right.right;
        //当前结点的左子树指向新结点
        left= newNode;
    }

    //返回左子树高度
    public int leftHeight() {
        if (left == null) {
            return 0;
        } else {
            return left.height();
        }
    }

    //返回右子树高度
    public int rightHeight() {
        if (right == null) {
            return 0;
        } else {
            return right.height();
        }
    }

    //返回以当前结点为根节点的树高度,因为要算上当前节点,所以要加一
    public int height() {
        return Math.max(left == null ? 0 : left.height() , right == null ? 0 : right.height())+1;
    }

    //查找要删除结点的方法
    public AVLTreeNode search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {//要查找的值不小于当前结点的value
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    //查找要删除结点的父节点
    public AVLTreeNode searchParent(int value) {
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            if (this.left != null && value < this.value) {
                return this.left.searchParent(value);
            } else if (this.right != null && value >= this.value) {
                return this.right.searchParent(value);

            } else {
                return null;//没有父节点
            }
        }

    }

    //添加结点的方法,要满足二叉排序树的要求
    public void add(AVLTreeNode 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);
            }

        }
        //当添加一个结点后,右子树的高度-左子树的高度>1,进行左旋
        if (rightHeight()-leftHeight()>1){
            //如果根节点的右子节点的左子树高度大于右子树
            if(right.leftHeight()>rightHeight()){
                right.rightRotate();
            }
            this.leftHeight();
            return;//如果进行了一次左旋,就不进行右旋的判断了
        }
        if(leftHeight()-rightHeight()>1){
           //如果根节点的左子结点的右子树高度大于左子树
            if (left.rightHeight()>leftHeight()){
                //左子结点进行一次左旋
                left.leftRotate();
            }
            this.rightRotate();
        }
    }

    //中序遍历
    public void midOrder() {
        if (this.left != null) {
            this.left.midOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.midOrder();
        }
    }

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

多叉树(B树)

二叉树的问题分析

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

邻接表
在这里插入图片描述

在这里插入图片描述

package 算法.图;

import java.util.ArrayList;
import java.util.Arrays;

public class Graph {
    private ArrayList<String> vertexList;//存储顶点集合
    private int [][] edges;//存储图的邻接矩阵
    private int numOfEdges; //边的数目Arr
    //构造器 参数的顶点个数
    public Graph(int n){
        edges=new int[n][n];
        vertexList=new ArrayList<String>();
        numOfEdges=0;
    }
    //插入顶点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }
    //添加边
    /*v1 v2 是顶点的下标 比如第一个顶点A下标是0 第二个i当地B是1
    *  weight 是用于修改矩阵的值
    * */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] =weight;
        edges[v2][v1]=weight;
        numOfEdges++;
    }
    //图中常用方法
    //返回结点个数
    public int getVertexNum(){
        return  vertexList.size();
    }
    //边的数组
    public int gerEdges(){
        return numOfEdges;
    }
    //返回结点i(下标)对应的数据 0->A
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }
    //显示矩阵
    public void showGraph(){
        for (int []link:edges){
            System.out.println(Arrays.toString(link));

        }
       
    }

    public static void main(String[] args) {
        int n=5;
        String [] vertexList={"a","b","c","d","f"};
        Graph graph = new Graph(n);
        for (int i = 0; i <vertexList.length ; i++) {
            graph.insertVertex(vertexList[i]);
        }
        //添加边
        //AB  AC  BC BD BE
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.showGraph();
    }


}

图的深度优先dps
在这里插入图片描述

在这里插入图片描述

package 算法.图;

import java.util.ArrayList;
import java.util.Arrays;

public class Graph {
    private ArrayList<String> vertexList;//存储顶点集合
    private int [][] edges;//存储图的邻接矩阵
    private int numOfEdges; //边的数目Arr
    //构造器 参数的顶点个数
    private boolean[] isVisted;//记录某个结点是否被访问
    public Graph(int n){
        edges=new int[n][n];
        vertexList=new ArrayList<String>();
        numOfEdges=0;
        isVisted = new boolean[n];
    }

    //深度优先算法//第一次下标是0
    public void dfs(boolean[] isVisted,int i){
        //首先访问一个顶点时输出
        System.out.print(getValueByIndex(i)+"->");
        //把访问过的结点设置为已经访问
        isVisted[i]=true;
        //查找结点i的第一个邻接点
        int w = getFirstNeighbor(i);
        while (w!=-1){//如果有邻接点
            if (!isVisted[w]){//没有被访问过
                 //以当前结点w递归dfs
                dfs(isVisted,w);
            }
            //如果已经访问过,返回i,访问下一个邻接结点
            w=getNextNeighbor(i,w);
        }
    }
    //对dfs 进行一个重载,遍历所有结点进行dfs
    public void dfs(){
        //遍历所有的结点,进行dfs【回溯】
        for (int i = 0; i <getVertexNum() ; i++) {
            if (!isVisted[i]){
                dfs(isVisted,i);
            }
        }
    }

    //根据前一个邻接结点的下标(v2)来获取下一个邻接结点的下标
    public  int getNextNeighbor(int v1,int v2){
        for (int i = v2+1; i <vertexList.size() ; i++) {
            if(edges[v1][i]>0){
                return i;
            }
        }
        return -1;
    }

    //得到当前结点的第一个邻接结点的下标
    /*index 当前结点的下标
    * 如果存在就返回邻接结点下标 否则返回-1
    * */
    public  int getFirstNeighbor(int index){
        for (int i = 0; i <vertexList.size() ; i++) {
            if (edges[index][i]>0){
                return i;
            }
        }
        return -1;
    }
    //插入顶点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }
    //添加边
    /*v1 v2 是顶点的下标 比如第一个顶点A下标是0 第二个i当地B是1
    *  weight 是用于修改矩阵的值
    * */
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2] =weight;
        edges[v2][v1]=weight;
        numOfEdges++;
    }
    //图中常用方法
    //返回结点个数
    public int getVertexNum(){
        return  vertexList.size();
    }
    //边的数组
    public int gerEdges(){
        return numOfEdges;
    }
    //返回结点i(下标)对应的数据 0->A
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }
    //显示矩阵
    public void showGraph(){
        for (int []link:edges){
            System.out.println(Arrays.toString(link));

        }

    }

    public static void main(String[] args) {
        int n=5;
        String [] vertexList={"a","b","c","d","e"};
        Graph graph = new Graph(n);
        for (int i = 0; i <vertexList.length ; i++) {
            graph.insertVertex(vertexList[i]);
        }
        //添加边
        //AB  AC  BC BD BE
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.showGraph();
        System.out.println("深度遍历");
        graph.dfs();
    }


}

在这里插入图片描述

变长编码 需要满足字符编码不能是其他编码的前缀
赫夫曼编码
赫夫曼编码就是满足了前缀要求的边长编码
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

package 算法.赫夫曼编码的实现;
//数据压缩的实现

import java.util.*;

public class HuffmanCode {
    public static void main(String[] args) {
        String content = "java是世界上最好的语言!";
        byte[] contentBytes = content.getBytes();
        byte[] huffmanCodeBytes = huffmanZip(contentBytes);
        System.out.println(Arrays.toString(huffmanCodeBytes));
        //解压获取到原来的字节数组
        byte [] decodeByte= decode(huffmanCodes,huffmanCodeBytes);

        System.out.println(new String(decodeByte));

    }

    //接收一个字符数组,返回一个建立好的list<Node>
    public static List<Node> getNodes(byte[] bytes) {
        //创建一个arrayList
        List<Node> list = new ArrayList<>();
        //创建一个hashMap存放字符和字符出现的次数 key是字符 value是出现的次数
        Map<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);
            }
        }
        //把map中的键值对转成node 加入list
        for (Map.Entry<Byte, Integer> entry : counts.entrySet()) {
            list.add(new Node(entry.getKey(), entry.getValue()));
        }
        return list;
    }

    //参数 需要创建赫夫曼树的list,返回赫夫曼树的根节点
    public static Node createHuffmanTree(List<Node> nodes) {

        //把list排序
        Collections.sort(nodes);
        //循环处理
        //最后list中只剩下赫夫曼树的头节点
        while (nodes.size() > 1) {

            //取出节点数权最少的两个二叉树
            Node leftNode = nodes.get(0);
            Node rightNode = nodes.get(1);
            //构建一颗新的二叉树,根节点权值是两颗二叉树的权值之和,没有Byte域
            Node parent = new Node(null, leftNode.weight + rightNode.weight);
            parent.left = leftNode;
            parent.right = rightNode;
            //从Array中剔除处理过的两颗二叉树
            nodes.remove(leftNode);
            nodes.remove(rightNode);
            nodes.add(parent);
            Collections.sort(nodes);
        }
        //最终只用返回赫夫曼树的头节点,就成功创建一颗赫夫曼树了
        return nodes.get(0);
    }

    //编写一个调用结点前序遍历的方法
    public static void preOrder(Node root) {
        if (root == null) {
            System.out.println("赫夫曼树为空");
        } else {
            root.preorder();
        }
    }

    //生成赫夫曼编码表,存放在map<Byte,String>
    static Map<Byte, String> huffmanCodes = new HashMap<>();
    //在生成赫夫曼编码时,需要用字符串拼接
    static StringBuilder stringBuilder = new StringBuilder();

    /*
     *  功能,将传入的node结点的所有叶子结点的赫夫曼编码得到,放入map
     *  node 传入结点
     *  code 路径 左子节点是0 右子节点是1
     *  StringBuilder 字符串拼接
     * */
    private static void getCodes(Node node, String code, StringBuilder stringBuilder) {
        //把形式参数中的内容传给新的stringBuilder
        StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
        stringBuilder1.append(code);
        if (node != null) {//node等于null 不处理
            //判断当前结点是叶子结点还是非叶子结点
            if (node.data == null) {
                //非叶子结点向左递归
                getCodes(node.left, "0", stringBuilder1);
                //向右递归
                getCodes(node.right, "1", stringBuilder1);
            } else {//如果是叶子结点
                huffmanCodes.put(node.data, stringBuilder1.toString());
            }
        }
    }

    //为了调用方便重载getCode,只需要传入赫夫曼树的根节点
    private static Map<Byte, String> getCodes(Node root) {
        if (root == null) {
            return null;
        }
        getCodes(root, "", stringBuilder);
        return huffmanCodes;
    }

    //将字符串对应的byte[] 通过赫夫曼编码表压缩
    //返回的是赫夫曼编码处理后的byte
    //也就是先转成对应的赫夫曼编码的字符串,再8位数一组转换成的byte,
    //并且由于计算机用二进制补码存储数据,输出的时候我们可以把得到的byte转换成我们的十进制数
    public static int lastStrLen=0;//定义一个全局变量,保存最后字节数组最后一个元素转成字符串的位数
    private static byte[] zip(byte[] bytes, Map<Byte, String> huffmanCodes) {
        //利用stringBuilder将bytes转成赫夫曼编码对应的字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (byte b : bytes) {
            stringBuilder.append(huffmanCodes.get(b));
        }
        System.out.println("测试"+stringBuilder);
        //将"1010100001111"对应的字符串8位一组转换成byte[]
        //len 是字节数
        int len;
        if (stringBuilder.length() % 8 == 0) {
            len = stringBuilder.length() / 8;
        } else {
            len = stringBuilder.length() / 8 + 1;
        }
        //存储赫夫曼编码压缩后的byte数组
        byte[] HuffmanCodeBytes = new byte[len];
        int index = 0;
        for (int i = 0; i < stringBuilder.length(); i += 8) {
            String str;
            if (i + 8 > stringBuilder.length()) {
                //如果剩下位数不够8位
                str = stringBuilder.substring(i);//把剩下的位数全部加入str
                lastStrLen=str.length();
            } else {
                str = stringBuilder.substring(i, i + 8);
                //将str转成byte,放入resByte
            }
               HuffmanCodeBytes[index] = (byte)Integer.parseInt(str,2);
                 //使用下面的方法,当8位数的最高位是1时,会发生越界,原因是parseByte会把传入的字符串转换成int,转化后
                //由于int有32位,也就是最高位不为0,导致转换后的数字超过128
                //HuffmanCodeBytes[index] = Byte.parseByte(str,2);
                index++;


        }
        return HuffmanCodeBytes;
    }
    //传入原字节数组,返回压缩后的数组
    //使用一个方法将前面的方法封装起来,便于调用
    private static byte[] huffmanZip(byte[] bytes){
         //把原字节数组创建成一个list<Node> node中有字符和其出现的次数
        List<Node> nodes=getNodes(bytes);
        //通过list创建赫夫曼树,返回赫夫曼树的根节点
        Node huffmanTreeRoot=createHuffmanTree(nodes);
        //根据赫夫曼树生成赫夫曼编码
        Map<Byte,String> huffmanCodes = getCodes(huffmanTreeRoot);
        //根据生成的赫夫曼编码得到压缩后的赫夫曼编码数组
        byte[] huffmanCodeBytes =zip(bytes,huffmanCodes);
        return huffmanCodeBytes;
    }

    /*将一个byte 转换成一个二进制的字符串
    *   b传入的byte
    *   flag 是否是字节数组最后一个字节,如果是,由于最后一位字节不一定是8位数,需要特殊处理
    *   返回 b对应的二进制字符串 补码;
    *
    * */
    private static String byteToString(boolean flag,byte b){
        //将b转成成int,byte转换成二进制字符串的方法
        int temp = b;
        //如果要补位,因为int小于8个字节的数转换成二进制,不会显示高位
        //比如 int a=1; Integer.toBinaryString(a) =1 ;低8位负数和正数和1 0000 0000按位或然后截取低8位的话都不会改变值
        //这样就解决了正数补码只显示低位,和负数补码32位都显示的问题
        //我们需要把1与1 0000 0000(256)按位或,补齐8位
        if (flag){
            temp|=256;// 0000 0001 |1 0000 0000 = 1 0000 0001(257)
        }else {
            temp|= (1<<lastStrLen);
        }
        String str = Integer.toBinaryString(temp);//把 1 0000 0001 对应的int 转换成二进制补码
        if (flag){
            return  str.substring(str.length()-8);//截取最后8位
        }else {//如果是最后一位

            return str.substring(str.length()-lastStrLen);
        }
    }

    //编写解码的方法
    //传入赫夫曼编码表和经过压缩的字节数组
    private static byte[] decode(Map<Byte,String>huffmanCodes,byte [] huffmanBytes){
       StringBuffer stringBuffer = new StringBuffer();
       //将字节数组转换成二进制字符串
        for (int i = 0; i <huffmanBytes.length ; i++) {
            byte b = huffmanBytes[i];
            //判断是不是最后一个值
            boolean flag=(i==huffmanBytes.length-1);
            stringBuilder.append(byteToString(!flag,b));
        }
        System.out.println("测试"+stringBuilder);
        //把字符串按照指定的赫夫曼编码解码
        //把赫夫曼编码表进行调换。反向查询,接入到map
        Map<String,Byte> map = new HashMap<>();
        for (Map.Entry<Byte,String> entry:huffmanCodes.entrySet()) {
            map.put(entry.getValue(),entry.getKey());
        }

        //创建一个集合存放byte
        List<Byte> list = new ArrayList<>();
        for (int i = 0; i <stringBuilder.length() ; ) {
            int count =1;
            boolean flag =true;
            Byte b = null;
            while (flag){
                String key = stringBuilder.substring(i,i+count);
                b = map.get(key);
                if (b==null){
                    count++;
                }else {
                    //匹配成功
                    flag=false;
                }
            }
            list.add(b);
            i +=count;
        }
        //当for循环结束,我们已经获得了原来数据的byte形式,我们将其放入byte[]返回
        byte[] bytes=new byte[list.size()];
        for (int i = 0; i <bytes.length ; i++) {
            bytes[i]=list.get(i);
        }
        return bytes;
    }



}

//结点类
//为了让Node支持Collection排序,应该实现Comparable接口
class Node implements Comparable<Node> {
    //byte的包装类.是字节数据类型

    Byte data;//存放数据本身的AScii编码,'a'->97
    int weight;//权值,字符出现的次数
    Node left;
    Node right;

    public Node(Byte data, int weight) {
        this.weight = weight;
        this.data = data;
    }

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

    @Override
    public int compareTo(Node o) {
        //从小到大进行排序
        return this.weight - o.weight;
    }

    //写一个前序遍历,用于测试
    public void preorder() {
        System.out.println(this);
        if (this.left != null) {
            this.left.preorder();
        }
        if (this.right != null) {
            this.right.preorder();
        }
    }
}

时间复杂度

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

排序算法

稳定性表示两个一样大小的值在排序后是否会交换原来的顺序
在这里插入图片描述

在这里插入图片描述

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

冒泡排序


//冒泡算法(从小到大)(可以有相等的数值) o(n^2)
package 算法;

public class MaoPaoSort {
    public static void main(String[] args) {
        int []array = {4,5,2,11,6,3};
        for (int i = 0; i <array.length-1 ; i++) {
            for (int j = 0; j < array.length-i-1; j++) {
                 int temp;
                 if(array[j]>array[j+1]){
                     temp=array[j];
                     array[j]=array[j+1];
                     array[j+1]=temp;
                 }
            }
        }
        for (int i = 0; i <array.length ; i++) {
            System.out.println(array[i]);
        }
    }
}

冒泡排序优化,如果发现在某次排序中,没有进行过交换,说明已经有序,我们可以用一个变量来标识


//冒泡算法(从小到大)(可以有相等的数值),8万次20秒
//平均复杂度 o(n^2)  稳定
package 算法;

public class MaoPaoSort {
    public static void main(String[] args) {
        int []array = {4,5,2,11,6,3};
        boolean flag =false;//标识
        //计算八万个数排序时间
        int[] arr = new int[80000];
        for(i=0;i<80000;i++){
           arr[i]= (int)(Math.random()*80000);
        }
        Date date1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        String dateStr1 = simpleDateFormat.format(date1);
        System.out.println("排序前的时间是"+dateStr1);
        for (int i = 0; i <array.length-1 ; i++) {
            for (int j = 0; j < array.length-i-1; j++) {
                 int temp;
                 if(array[j]>array[j+1]){
                     flag=true;
                     temp=array[j];
                     array[j]=array[j+1];
                     array[j+1]=temp;
                 }
             }
             if(!flag){//在一次排序中一次交换都没有发生
                     break;
                 }else{
                    flag =false;//重置flag,进行下一次交换
                 }
        }
        for (int i = 0; i <array.length ; i++) {
            System.out.println(array[i]);
        }
        Date date2 = new Date();
        String dateStr2 = simpleDateFormat.farmat(date2)
        System.out.print;
    }
}

选择排序

//选择排序(从小到大)(可以有相等的数值),虽然复杂度都是0(n^2),但是运行时间比冒泡快,8万次 5秒
//不稳定
package 算法;

public class SelectSort {
    public static void main(String[] args) {
        int []array ={7,5,9,2,3,4,4};
        for (int i = 0; i < array.length-1; i++) {
            int min = i;
            for (int j = i; j <array.length-1 ; j++) {
                if(array[min]>array[j+1]){
                     min=j+1;

                }

            }
            if(i!=min){
                int temp;
                temp=array[i];
                array[i]=array[min];
                array[min]=temp;
            }
        }
        for (int i = 0; i <array.length ; i++) {
            System.out.println(array[i]);
        }
    }
}

插入排序

在这里插入图片描述

在这里插入图片描述

package 算法.排序算法; 8万次大概5秒 时间复杂度o(n^2//稳定
public class InsertSort {
    public static void main(String[] args) {

        int[] arr = new int[10];
        for (int i = 0; i <10; i++) {
            arr[i]= (int)(Math.random()*10);
        }
        sort(arr);
        for (int i = 0; i <arr.length ; i++) {
            System.out.print(arr[i]+" ");
        }
    }
    public  static void  sort(int [] arr){
              //创建变量应该放在循环的外面,节省内存开销
             int insertVal = 0;//定义待插入的数
             int insertIndex=0;//定义插入下标
        for (int i = 1; i < arr.length; i++) {
            
            insertVal = arr[i];
            insertIndex = i;
            if(insertVal<arr[insertIndex-1]){
                while (insertIndex-1>=0&&arr[insertIndex-1]>insertVal){
                    arr[insertIndex]=arr[insertIndex-1];
                    insertIndex--;
                }
            }
            arr[insertIndex]=insertVal;
        }
    }

}

在这里插入图片描述

希尔排序
在这里插入图片描述

在这里插入图片描述

package 算法.排序算法;
//o(nlogn) 8万次大概1秒  不稳定

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

public class ShellSort {
    public static void main(String[] args) {
         int[] arr=new int[80000];
        for (int i = 0; i <arr.length ; i++) {
            arr[i]=(int)(Math.random()*80000);
        }
        Date date1 =new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        String dateStr1 = simpleDateFormat.format(date1);
        System.out.println(dateStr1);
        //shellSort1(arr);
        shellSort2(arr);
        Date date2 = new Date();
        System.out.println(simpleDateFormat.format(date2));
    }


    //希尔排序交换法,效率比移动法低  8万次排序17秒
    public static void  shellSort1(int [] arr){
        int temp = 0;
        //把所有元素看出独立的组,每次循环组数除2,直到剩下一个大组
        for (int gap =arr.length/2; gap >0 ; gap/=2) {
             //每次循环,都把各个小组的数字排序,这里小组的排序不是插入排序,效率低
            for (int i = gap; i <arr.length ; i++) {
                for (int j = i-gap; j >=0 ; j-=gap) {
                    if(arr[j]>arr[j+gap]){
                        temp=arr[j];
                        arr[j]=arr[j+gap];
                        arr[j+gap]=temp;
                    }
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }


    //希尔排序移动法,效率很高,8万次大概1秒
    public  static  void shellSort2(int[] arr){
        for (int gap = arr.length/2; gap >0 ; gap/=2) {
            //从第gap个元素,逐个对其所在的组进行插入排序
            for (int i = gap; i <arr.length ; i++) {
                int j= i;
                int temp = arr[j];
                //当要插入的值比前面的值大,则把插入的值放到一个合适的位置,否则当前位置就是要插入的位置,不做改动
                if(arr[j-gap]>arr[j]){
                    while (j-gap>=0&&arr[j-gap]>temp){
                        arr[j]= arr[j-gap];
                        j -=gap;
                    }
                    //退出while循环时,当前位置就适当的插入位置
                    arr[j]=temp;
                }
            }

        }
        //System.out.println(Arrays.toString(arr));
    }
}

快速排序

在这里插入图片描述

在这里插入图片描述

//快速排序比希尔排序更快 800万个数排序4秒左右
// nlog n 不稳定
package 算法.排序算法;
import java.util.*;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
        quickSort(arr, 0, arr.length - 1);
        System.out.println("排序后:");
        System.out.println(Arrays.toString(arr));

    }

    private static void quickSort(int[] arr, int low, int high) {

        if (low < high) {
            // 找寻基准数据的正确索引
            int index = getIndex(arr, low, high);

            // 进行迭代对index之前和之后的数组进行相同的操作使整个数组变成有序
            quickSort(arr, 0, index - 1);
            quickSort(arr, index + 1, high);
        }

    }

    private static int getIndex(int[] arr, int low, int high) {
        // 基准数据
        int tmp = arr[low];
        while (low < high) {
            // 当队尾的元素大于等于基准数据时,向前挪动high指针
            while (low < high && arr[high] >= tmp) {
                high--;
            }
            // 如果队尾元素小于tmp了,需要将其赋值给low
            arr[low] = arr[high];
            // 当队首元素小于等于tmp时,向前挪动low指针
            while (low < high && arr[low] <= tmp) {
                low++;
            }
            // 当队首元素大于tmp时,需要将其赋值给high
            arr[high] = arr[low];

        }
        // 跳出循环时low和high相等,此时的low或high就是tmp的正确索引位置
        // 由原理部分可以很清楚的知道low位置的值并不是tmp,所以需要将tmp赋值给arr[low]
        arr[low] = tmp;
        return low; // 返回tmp的正确位置
    }
}

归并排序
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

//800万次排序大概3秒 稳定
//nlog n
package 算法.排序算法;


import java.util.Arrays;

public class MergetSort {
    public static void main(String[] args) {
        int[] arr ={8,4,5,7,1,3,6,2};
        int[]temp = new int[arr.length];
        mergetSort(arr,0,arr.length-1,temp);
        System.out.println(Arrays.toString(arr));

    }

    //分解和递归调用merge
    public static void mergetSort(int arr[],int left,int right,int[] temp){
         if(left<right){
             int mid = (left+right)/2;
             //向左递归进行分解
             mergetSort(arr,left,mid,temp);
             //向右递归
             mergetSort(arr,mid+1,right,temp);
             //合并
             merge(arr,left,mid,right,temp);
         }
    }


    //合并的方法
    /*
    *  arr 排序的原始数组
    *  left 左边有序序列的初始索引
    *  mid+1   右边有序序列的初始索引
    *  right   右边有序序列的最后一个索引
    *  temp    中转数组
    * */
    public  static void merge(int[] arr,int left,int mid ,int right,int[] temp){
        int i = left;  //左边序列初始索引
        int j = mid+1; // 右边序列初始索引
        int t = 0 ;   //指向临时数组当前索引的辅助指针
        //一,先把左右两边有序的数组按照规则填充到temp数组
        //直到两边的有序序列有一边处理完为止
        while (i<=mid&&j<=right){
            //如果左边的有序序列的当前元素,小于右边有序序列的当前数据
            //就把左边的数据填充到temp
            if(arr[i]<=arr[j]){
                temp[t]=arr[i];
                i++;
                t++;
            }else {//如果右边小于左边
                temp[t]=arr[j];
                t++;
                j++;
            }
        }
        //二把有剩余数据的一边,依次把数据加入到temp
        while (i<=mid){
            temp[t]=arr[i];
            t++;
            i++;
        }
        while (j<=right){
            temp[t]=arr[j];
            t++;
            j++;
        }

        //三 把temp的数据元素拷贝到arr中
        t=0;
        int tempLeft = left;
        while (tempLeft<=right){
            arr[tempLeft]=temp[t];
            tempLeft++;
            t++;
        }
    }
}

基数排序
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

//基数排序比快速排序还要快 800万大概一秒,但是耗费比较多的内存
//8000万次排序 大概需要3.3g内存
//稳定  复杂度 o(n*k) k是桶的个数
// 80000000*11(二维数组加临时数组共11个数组)*4(int字节长度)/1024/1024/1024=3.3g
package 算法.排序算法;

import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
      int[] arr ={53,3,542,748,14,214};
      radoxSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    //基数排序方法
    public static void radoxSort(int[] arr){
        //得到数组中最大的数
        //假设第一个就是最大的
        int max = arr[0];
        for (int i = 0; i <arr.length ; i++) {
            if(arr[i]>max){
                max= arr[i];
            }
        }
        //得到最大的位数是几
        int maxLength = (max+"").length();

        //定义一个二维数组,表示10个桶
        int[][] bucket = new int[10][10];
        //为了记录每次循环,每个桶中放入了多少个数据,用一个一维数组来记录
        int[] bucketElementCount=new int[10];
        for (int i = 0,n=1; i <maxLength ; i++,n*=10) {
             //从最低位开始,到最高位,依次进行桶排序
            //取出每个元素对应的位数,第一次取个位,直到取到最高位
            for (int j = 0; j <arr.length ; j++) {
                int digitOfElement = arr[j]/n%10;
                //根据取出的值放入对应的桶中
                bucket[digitOfElement][bucketElementCount[digitOfElement]]=arr[j];
                bucketElementCount[digitOfElement]++;

            }
            //按照桶中放入的元素顺序取出,放入原来的数组
            int index =0;
            for (int k = 0; k <bucketElementCount.length ; k++) {
                //判断桶中是否有数据
                if(bucketElementCount[k]!=0){
                    for (int l = 0; l <bucketElementCount[k] ; l++) {
                        arr[index++]=bucket[k][l];
                    }
                }
                //每一次遍历完一个桶,就把存放这个桶有效数字个数的数组的对应值清空
                bucketElementCount[k]=0;
            }
        }
    }
}

堆排序
https://www.cnblogs.com/chengxiao/p/6129630.html
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package 算法.排序算法;
//堆排序 8万次大概4秒 复杂度 n*log(n);

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {4,6,8,5,9,10,10,45,45,77};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //编写一个堆排序的方法,将数组升序排序
    public static void heapSort(int[] arr){
        //最后一个非叶子结点的位置
        int temp = 0;
        for (int i = arr.length/2-1; i >=0 ; i--) {
            adjustHeap(arr,i,arr.length);
        }
        //循环过后就可以得到一个大顶堆,
        for (int j = arr.length-1; j >0 ; j--) {
            temp=arr[j];
            arr[j]=arr[0];
            arr[0]=temp;
            //经过第一次顶堆排序,剩下的结点具备了大顶堆的特点,只需要判断第一个根节点的左右子节点大小就可以了。
            adjustHeap(arr,0,j);
        }
    }

    //将一个数组(二叉树),调整成一个大顶堆
    /*
      arr 要调整的数组
      i 表示最后一个还没处理的非叶子结点在数组中的索引
      length 表示多少个元素继续调整,length是不断减少的
    */

    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 (k+1<length&&arr[k]<arr[k+1]){
                //如果有子节点大于左字节点
                k++;
            }
            if (arr[k]>temp){
                arr[i]=arr[k];
                i=k;//i的位置插入了arr[k]的值,此时应该把temp的值寻找一个合适的位置放置(在arr[k]的子节点)
            }else {
                break;
            }
        }//放for循环结束,我们已经把以i为父节点的树的最大值放在了arr[i]的位置
        //此时应该把temp放到找到的合适位置
        arr[i]=temp;
    }
}

查找算法
在这里插入图片描述

//二分法
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析	
//且数组中没有重复的元素
//二分法查找 :查找指定值的元素
 // 该方法建立在数组已经排序的基础上
  // 下面程序基于从小到大排序分析	
  //且数组中没有重复的元素
  public class Test{
     public static void main(String[] args){
      int[] a = {1,2,3,4,7,9,45,75,100};
      int destElement = 9;
      int index = binarySearch(a,destElement);
      //传入数组,和查询的值,该方法如果找到指定元素就返回下标,否则返回-1
      //输出
      System.out.println((destElement==-1?)destElement+"元素不存在!":destElement+"在数组的下标是"+index);
     }
     //定义二分法
     public static int binarySearch(int[] a,destElement){
       int begin = 0;
       int end = a.length-1;
     
       while(begin<=end){//当begin不等于end,会一直执行
           int mid = (begin+end)/2;  
        if (a[mid]==destElement){
         return mid;
        }else if(a[mid]>destElement){
          end= mid - 1;
        }else if(a[mid]<destElement){
          begin  =mid +1;
        }
       }
       return -1; //没有找到
     }
      
  }
//二分查找优化,当数组中有多个要查找的值时,如何将所有的元素都查找到(有序序列)
//思路当找到返回值后,向左边和右边寻找相同的值


import java.util.ArrayList;
import java.util.List;

public class suibian {

    public static void main(String[] args) {
        int[] a = {1,2,3,4,7,9,9,9,75,75,75,100};
        List<Integer> list= binarySearch(a,0,a.length,75);
        System.out.println(list);
    }

    public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal){
        if(left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
            return null;
        }
        int mid = (left+right)/2;
        int midVal = arr[mid];
        if(findVal>midVal){
            return binarySearch(arr,mid+1,right,findVal);
        }else if(findVal<midVal){
            return binarySearch(arr,left,mid-1,findVal);
        }else{
            ArrayList<Integer> resIndexList = new ArrayList<>();
            int temp = mid -1;
            while(true){
                if(temp<0||arr[temp]!=findVal){
                    break;
                }
                resIndexList.add(temp);
                temp-=1;
            }
            resIndexList.add(mid);//把二分法找到的索引也加入list
            //向右边扫描
            temp = mid +1;
            while(true){
                if(temp>arr.length-1||arr[temp]!=findVal){
                    break;
                }
                resIndexList.add(temp);
                temp++;
            }
            return resIndexList;

        }
    }

}

在这里插入图片描述

//是二分法的改进版,数组同样需要有序
//代码类似,但是改进的mid的取值算法

 public static ArrayList<Integer> binarySearch(int[] arr, int left, int right, int findVal){
         //判断findVal的值,防止mid越界
        if(left>right||findVal<arr[0]||findVal>arr[arr.length-1]){
            return null;
        }
        int mid = left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left]);
        int midVal = arr[mid];
        if(findVal>midVal){
            return binarySearch(arr,mid+1,right,findVal);
        }else if(findVal<midVal){
            return binarySearch(arr,left,mid-1,findVal);
        }else{
            ArrayList<Integer> resIndexList = new ArrayList<>();
            int temp = mid -1;
            while(true){
                if(temp<0||arr[temp]!=findVal){
                    break;
                }
                resIndexList.add(temp);
                temp-=1;
            }
            resIndexList.add(mid);//把二分法找到的索引也加入list
            //向右边扫描
            temp = mid +1;
            while(true){
                if(temp>arr.length-1||arr[temp]!=findVal){
                    break;
                }
                resIndexList.add(temp);
                temp++;
            }
            return resIndexList;

        }
    }

}

斐波那契数列递归方法

输入一个n ,输出斐波那契数列第n项的数字

 public class Solution {
public int Fibonacci(int n) {
if(n==0){
    return 0;}
if(n==1){
    return 1;}
 
return Fibonacci(n-1) + Fibonacci(n-2);}

}

返回数组中出现次数超过数组长度一半的数字

package 算法.返回数组中出现次数超过数组长度一半的数字;

public class Solution {
    public static void main(String[] args) {
         int [] arr = {1,2,2,2,3,4,5,2,2,};
        int res=function(arr);
        System.out.println(res);


    }
    public static int function(int[] arr){
        int size = arr.length;
        for (int i = 0; i <arr.length-1 ; i++) {
            for (int j = 0; j < arr.length-i-1; j++) {
               if(arr[j]>arr[j+1]){
                   int temp = arr[j];
                   arr[j]= arr[j+1];
                   arr[j+1]=temp;
               }
            }
        }
        int mid = arr.length/2;
        return arr[mid];
    }
}

进阶

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
import java.util.HashMap;
import java.util.Map;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        Map<Integer,Integer> map =new HashMap<>();

        int len = array.length;
        for (int i = 0; i <len ; i++) {
           Integer count=map.get(array[i]);
           if(count==null){
               map.put(array[i],1);
           }else {
               map.put(array[i],count+1);
           }
        }
        int res = 0;
        for (int b:map.keySet()) {
            if (map.get(b)>=(len/2)+1){
                  res=b;
            }
        }
        return res;
    }
}

//冒泡算法(从小到大)(可以有相等的数值)
package 算法;

public class MaoPaoSort {
    public static void main(String[] args) {
        int []array = {4,5,2,11,6,3};
        for (int i = 0; i <array.length-1 ; i++) {
            for (int j = 0; j < array.length-i-1; j++) {
                 int temp;
                 if(array[j]>array[j+1]){
                     temp=array[j];
                     array[j]=array[j+1];
                     array[j+1]=temp;
                 }
            }
        }
        for (int i = 0; i <array.length ; i++) {
            System.out.println(array[i]);
        }
    }
}

选择排序(从小到大)(可以有相等的数值)
package 算法;

public class SelectSort {
    public static void main(String[] args) {
        int []array ={7,5,9,2,3,4,4};
        for (int i = 0; i < array.length-1; i++) {
            int min = i;
            for (int j = i; j <array.length-1 ; j++) {
                if(array[min]>array[j+1]){
                     min=j+1;

                }

            }
            if(i!=min){
                int temp;
                temp=array[i];
                array[i]=array[min];
                array[min]=temp;
            }
        }
        for (int i = 0; i <array.length ; i++) {
            System.out.println(array[i]);
        }
    }
}

//二分法
//二分法查找 :查找指定值的元素
// 该方法建立在数组已经排序的基础上
// 下面程序基于从小到大排序分析	
//且数组中没有重复的元素
//二分法查找 :查找指定值的元素
 // 该方法建立在数组已经排序的基础上
  // 下面程序基于从小到大排序分析	
  //且数组中没有重复的元素
  public class Test{
     public static void main(String[] args){
      int[] a = {1,2,3,4,7,9,45,75,100};
      int destElement = 9;
      int index = binarySearch(a,destElement);
      //传入数组,和查询的值,该方法如果找到指定元素就返回下标,否则返回-1
      //输出
      System.out.println((destElement==-1?)destElement+"元素不存在!":destElement+"在数组的下标是"+index);
     }
     //定义二分法
     public static int binarySearch(int[] a,destElement){
       int begin = 0;
       int end = a.length-1;
     
       while(begin<=end){//当begin不等于end,会一直执行
           int mid = (begin+end)/2; 
        if (a[mid]==destElement){
         return mid;
        }else if(a[mid]>destElement){
          end= mid - 1;
        }else if(a[mid]<destElement){
          begin  =mid +1;
        }
       }
       return -1; //没有找到
     }
      
  }
//题目描述
//在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
public class Solution {
    public boolean Find(int target, int [][] array) {
 
        /*
        思路:
        根据数组的特点可以发现,如果从左下角开始查找更为方便
        左下角的数比同一列的数大,同时比同一行的数小
        也就是从左下角开始比,如果target传入的数大于它则列数加一继续比较
        如果小于它则行数减一继续比较
        */
        //定义行数
        int rows=array.length;
        //定义列数
        int lies=array[0].length;
        //定义i用来技计数
        int i=0;
        //while里的条件是极限情况,不满足条件的时候则停止
        while((rows>0)&&(i<lies))
        {
            //目标大于左下角,让列数自增
            if(target>array[rows-1][i])
            {
                i++;
            //目标小于左下角,让行数自减
            }else if(target<array[rows-1][i])
            {
                rows--;
            }else
            {
                //除了上述两种情况就是相等了,说明已经找到了
                return true;
            }
        }
        //遍历完还没有找到,说明不存在
        return false;
    }
}

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。



//replace(int start, int end, String str):将起始位置为start,结束位置为end-1的子串替换为str。不生成新的StringBuilder对象,在原来的StringBuilder对象上修改。

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int index = str.indexOf(" ");
        while(index != -1){
            str.replace(index,index+1,"%20");
            index = str.indexOf(" ",index);//查找指定字符或字符串在字符串中第一次出现地方的索引,未找到的情况返回 -1.
           // 从index的地方开始找,返回第一次出现的索引
        }
        String result = str.toString();
        return result;

    }
}

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

这里要使用栈,利用栈先进后出的方法

在这里插入图片描述

/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.Stack;
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<Integer> stack = new Stack<>();
        while (listNode != null) {
            stack.push(listNode.val);
            listNode = listNode.next;
        }
 
        ArrayList<Integer> list = new ArrayList<>();
        while (!stack.isEmpty()) {
            list.add(stack.pop());
        }
        return list;       
    }
}

用两个栈实现队列

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(node);
    }
    
    public int pop() {
     while(!stack1.empty()){
         stack2.push(stack1.peek());
         stack1.pop();
     }
     int value = stack2.peek();
     stack2.pop();
     while(!stack2.empty()){
         stack1.push(stack2.peek());
         stack2.pop();
     }
     return value;
    }
}

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {

//先根据题目要求判断数组的大小为0则返回0
        if(array.length==0){
            return 0;
        }
        int left=0;
        int right=array.length-1;
        while(left<right){
            int mid=(left+right)/2;
            if(array[mid]>array[right]){
                left=mid+1;

//在这一步不能直接使用经典的二分法思想
            }else if(array[mid]<array[right]){
                right--;;
            }else{
                right=mid;
            }
        }
        return array[left];
    }
}

题目描述

给出一个正整数N和长度L,找出一段长度大于等于L的连续非负整数,他们的和恰好为N。答案可能有多个,我我们需要找出长度最小的那个。
例如 N = 18 L = 2:
5 + 6 + 7 = 18
3 + 4 + 5 + 6 = 18
都是满足要求的,但是我们输出更短的 5 6 7

输入描述:

输入数据包括一行: 两个正整数N(1 ≤ N ≤ 1000000000),L(2 ≤ L ≤ 100)

输出描述:

从小到大输出这段连续非负整数,以空格分隔,行末无空格。如果没有这样的序列或者找出的序列长度大于100,则输出No

思路 对于连续序列求和 有高斯公式 sum = (start +end)*(end-start+1)/2

所以sum=(start+start+L-1)*L/2

package 算法.求序列和;

public class Function {
    public static void main(String[] args) {
     String s = getLine(18,2);
        System.out.println(s);
    }
    public static String getLine(long n,int L){
        if(n<1||n>1000000000||L<2||L>=100){
            return null;
        }
        long start =0;
        String line="";
        for (int i =L ; i <=100 ; i++) {
            // n=(2*start+L-1)*L/2
            long doubleStart = n*2/i-i+1;
            //如果start为整数,说明找到了最短序列
            if(doubleStart%2==0){
                start=doubleStart/2;
                for (int j = 0; j <i ; j++) {
                    if (j<i-1){
                        line+=start+j+" ";
                    }else {
                        //如果是最后一个不输入空格
                        line+=start+j;
                    }
                }
                return  line;
            }
        }
        return "NO";

    }
}

斐波那契数列递归方法

输入一个n ,输
出斐波那契数列第n项的数字

public class Solution {
public int Fibonacci(int n) {
if(n==0){
    return 0;}
if(n==1){
    return 1;}
 
return Fibonacci(n-1) + Fibonacci(n-2);}

}
package 算法.剑指offer.根据前序遍历和中序遍历创建二叉树.hhh;

/*因为是树的结构,一般都是用递归来实现。

用数学归纳法的思想就是,假设最后一步,就是root的左右子树都已经重建好了,那么我只要考虑将root的左右子树安上去即可。

根据前序遍历的性质,第一个元素必然就是root,那么下面的工作就是如何确定root的左右子树的范围。

根据中序遍历的性质,root元素前面都是root的左子树,后面都是root的右子树。那么我们只要找到中序遍历中root的位置,就可以确定好左右子树的范围。

正如上面所说,只需要将确定的左右子树安到root上即可。递归要注意出口,假设最后只有一个元素了,那么就要返回。000000
* */

import java.util.Arrays;
public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        //数组长度为0的时候要处理
        if(pre.length == 0){
            return null;
        }

        int rootVal = pre[0];

        //数组长度仅为1的时候就要处理
        if(pre.length == 1){
            return new TreeNode(rootVal);
        }

        //我们先找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
        TreeNode root = new TreeNode(rootVal);
        int rootIndex = 0;
        for(int i=0;i<in.length;i++){
            if(rootVal == in[i]){
                rootIndex = i;
                break;
            }
        }

        //递归,假设root的左右子树都已经构建完毕,那么只要将左右子树安到root左右即可
        //这里注意Arrays.copyOfRange(int[],start,end)是[)的区间
        root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,rootIndex+1),Arrays.copyOfRange(in,0,rootIndex));
        root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,rootIndex+1,pre.length),Arrays.copyOfRange(in,rootIndex+1,in.length));


        return root;
    }
    public static void main(String[] args) {
        int [] pre = {1,2,4,7,3,5,6,8};
        int [] mid= {4,7,2,1,5,3,8,6};
        TreeNode root =new Solution().reConstructBinaryTree(pre,mid);
        root.preOrder();

    }

}




 class TreeNode {
      int val;
     TreeNode left;
      TreeNode right;
      TreeNode(int x) { val = x; }
      //编写一个前序遍历的方法用于遍历
     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 "TreeNode{" +
                 "val=" + val +
                 '}';
     }
 }

跳台阶问题

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

比较倾向于找规律的解法,f(1) = 1, f(2) = 2, f(3) = 3, f(4) = 5, 可以总结出f(n) = f(n-1) + f(n-2)的规律,但是为什么会出现这样的规律呢?假设现在6个台阶,我们可以从第5跳一步到6,这样的话有多少种方案跳到5就有多少种方案跳到6,另外我们也可以从4跳两步跳到6,跳到4有多少种方案的话,就有多少种方案跳到6,其他的不能从3跳到6什么的啦,所以最后就是f(6) = f(5) + f(4);这样子也很好理解变态跳台阶的问题了

public class Solution {
    public int JumpFloor(int target) {
        int n = target;
        if (n==1){
           return 1;
       }
       if (n==2){
           return 2;
       }
       return JumpFloor(n-1)+JumpFloor(n-2);
    }
}

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

 每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。
 public class Solution {
    public int JumpFloorII(int target) {
        if(target <= 0)
            return 0;
        
        int result = 1;
        while(target > 1){
            result = 2 * result;
            target--;
        }
        return result;
     }
}

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

public class Solution {
    public static int NumberOf1(int n) {
         // 返回指定值的二进制,用字符串保存
        String str = Integer.toBinaryString(n);
        int count=0;
        //String.toCharArray()把字符串变成char[]
        for (char b:str.toCharArray()) {
          if(b=='1'){
              count++;
          }
        }
        return count;
    }
}

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0

public class Solution {
   public double Power(double base, int exponent) {
        double result = base;
        int n = exponent;
        if (exponent < 0) {
            exponent = - exponent;
        }
        else if(exponent == 0) {
            return 1;
        }
            for (int i = 1; i < exponent; i++) {
                    result *= base;
            }
         
        return n < 0 ? 1 / result : result;
      }
}

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

package 算法.剑指offer;

import java.util.ArrayList;
import java.util.List;

public class Solution {
    public void reOrderArray(int [] array) {
        List<Integer> list1 =new ArrayList<>();
        List<Integer> list2 =new ArrayList<>();
         int temp=0;
        for (int i = 0; i <array.length ; i++) {
            if(array[i]%2==1){
                list1.add(array[i]);
            }else {
                list2.add(array[i]);
            }
        }
        for (int a:list1) {
            array[temp]=a;
            temp++;
        }
        temp=list1.size();
        for (int b:list2) {
            array[temp]=b;
            temp++;
        }
    }


}



//方法二 插入排序思想
package 算法.剑指offer;

import java.util.Arrays;

public class Solution {
    public void reOrderArray(int [] array) {
        //相对位置不变,稳定性
        //插入排序的思想
        int m = array.length;
        int k = 0;//记录已经摆好位置的奇数的个数
        for (int i = 0; i < m; i++) {
            if (array[i] % 2 == 1) {
                int j = i;
                while (j > k) {//j >= k+1
                    int tmp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = tmp;
                    j--;
                }
                k++;
            }
        }
    }

    public static void main(String[] args) {
        int [] arr = {1,7,2,9,5,4,6,11};
        Solution solution = new Solution();
        solution.reOrderArray(arr);
        System.out.println(Arrays.toString(arr));
    }
}

输入一个链表,反转链表后,输出新链表的表头。

  public class Solution {
    public ListNode ReverseList(ListNode head) {
       if(head==null){
           return null;
       }
        ListNode pre = null;
      ListNode temp = null;
      while (head!=null){
          //用temp保存head的下一个结点
          temp=head.next;
          //让当前结点指向pre,反转链表
          head.next=pre;
          //pre变成当前结点
          pre=head;
          head=temp;


      }
      return pre;
    }
}
import java.util.Stack;

class Solution {
    public ListNode ReverseList(ListNode head) {
        if (head==null){
            return null;
        }
        Stack<ListNode> stack = new Stack<>();
        while (head!=null){
            stack.push(head);
            head =head.next;
        }
        head = stack.pop();
        ListNode temp = head ;
        while (!stack.empty()){
            temp.next=stack.pop();
            temp=temp.next;
        }
        //防止链表成环
        temp.next=null;
        return head;

    }
}

贪心算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YQGA5c9-1584535809922)(E:\笔记\图片\1584282191146.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BGxGaybX-1584535809922)(E:\笔记\图片\1584282227811.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDIEC1KK-1584535809924)(E:\笔记\图片\1584282402554.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rGT2MIW-1584535809925)(E:\笔记\图片\1584282526487.png)]

//贪心算法不一定是最优解,注意事项

//代码
package 算法.贪心算法;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

public class GreedyAlgorighm {
    public static void main(String[] args) {
        //创建广播电台,用map存放 key是广播台编号,value是一个set(无序唯一) 存放着覆盖的地区信息
      HashMap<String, HashSet<String>> broadCasts= new HashMap<String,HashSet<String>>();
    HashSet<String> hashSet1 = new HashSet<String>();
    hashSet1.add("北京");
    hashSet1.add("上海");
    hashSet1.add("天津");

    HashSet<String> hashSet2 = new HashSet<String>();
    hashSet2.add("广州");
    hashSet2.add("北京");
    hashSet2.add("深圳");

    HashSet<String> hashSet3 = new HashSet<String>();
    hashSet3.add("上海");
    hashSet3.add("成都");
    hashSet3.add("杭州");

    HashSet<String> hashSet4 = new HashSet<String>();
    hashSet4.add("上海");
    hashSet4.add("天津");


    HashSet<String> hashSet5 = new HashSet<String>();
    hashSet5.add("杭州");
    hashSet5.add("大连");
    //加入map
   broadCasts.put("K1",hashSet1);
    broadCasts.put("K2",hashSet2);
    broadCasts.put("K3",hashSet3);
    broadCasts.put("K4",hashSet4);
    broadCasts.put("K5",hashSet5);
        //allAress 存放当前还没覆盖的地区
    HashSet<String> allAress =new HashSet<String>();
    allAress.add("北京");
    allAress.add("上海");
    allAress.add("天津");
    allAress.add("广州");
    allAress.add("深圳");
    allAress.add("成都");
    allAress.add("杭州");
    allAress.add("大连");

    //创建ArrayList 存放被选中的天台的集合
        ArrayList<String> select = new ArrayList<String>();
    //定义一个临时的集合,在遍历的过程中,存放遍历过程中的电台覆盖区域和还未覆盖地区的交集
        HashSet<String> tempSet = new HashSet<String>();
    //定义maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖的地区对应的电台的key
    //如果maxKey不为null,则会加入到select中
        String maxKey = null;
        while (allAress.size()!=0){
            //如果allAress不为0,表示还没有覆盖所有的地区
            //遍历broadCast,取出对应key
            //每次循环需要把maxKey清空,防止temp.size还大于0时,因为temp.size 小于broadCasts.get(maxKey).size()而不进行maxKey的重置
            maxKey=null;
            for (String key:broadCasts.keySet()) {
                //每一次进行for 必须将tempSet清空
                tempSet.clear();
                //当前这个key能覆盖到的地区
                HashSet<String> areas = broadCasts.get(key);
                tempSet.addAll(areas);
                //求出tempSet和allAreas集合的交集,会把交集赋值给tempSet
                tempSet.retainAll(allAress);
                //如果当前这个集合包含的未覆盖地区数量,比maxkey指向的集合的地区还多,就把maxKey指向这个集合
                if(tempSet.size()>0&&(maxKey==null||tempSet.size()>broadCasts.get(maxKey).size())){
                    maxKey=key;
                }

            }
            if(maxKey!=null) {
                select.add(maxKey);
                //将maxKey指向的地区从allAreas去掉
                allAress.removeAll(broadCasts.get(maxKey));
            }
        }

        System.out.println("得到的结果是"+select);
    }

}

KMP算法 字符串匹配问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3TWYWdTH-1584535809925)(E:\笔记\图片\1584324756964.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3om4AEfX-1584535809926)(E:\笔记\图片\1584324909511.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bw4LykH2-1584535809927)(E:\笔记\图片\1584326157747.png)]

部分匹配值的表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNY3oMGX-1584535809927)(E:\笔记\图片\1584325992548.png)]

next记录的是当前字符串前缀和后缀匹配的个数

package 算法.KMPAlgorithm;

import java.util.Arrays;

public class Kmp {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 ="ABCDABD";//结果15
        int[] next=kmpNext(str2);
        //输出部分匹配值表
        System.out.println("next:"+ Arrays.toString(next));
        //匹配字符串,返回索引值
        System.out.println(kmpSearch(str1,str2,next));

    }

    //获取字串的字符匹配值表
    public static int[] kmpNext(String str){
        //创建一个next表,每个下标存储的值是部分匹配值,也可以理解为前缀与后缀最大相同的·1个数
        int [] next = new int[str.length()];
         //第一个字符的匹配值一定为0
        next[0]=0;
        for (int i = 1,j=0; i <str.length() ; i++) {
            //如果str.charAt(i)!=str.charAt(j)
            while(j>0&&str.charAt(i)!=str.charAt(j)){
                j=next[j-1];
            }

            //当满足str.charAt(i)==str.charAt(j),部分匹配值加一
            if(str.charAt(i)==str.charAt(j)){
                j++;
            }
            next[i]=j;
        }
        return next;
    }
    //得到字串的字符串匹配表后,我们可以开始写kmp算法了
    //参数 第一个是源字符串,第二个是要匹配的字符串 数组是字串2的部分匹配值表
    //如果匹配返回第一个匹配值的索引,如果不匹配,返回-1
    public static int kmpSearch(String str1,String str2,int[] next){
        for (int i = 0,j=0; i <str1.length() ; i++) {

            while (j>0&&str1.charAt(i)!=str2.charAt(j)){
                j=next[j-1];
            }

            if(str1.charAt(i)==str2.charAt(j)){
                j++;
            }
            if (j==str2.length()){
                return i-j+1;
            }

        }
        return -1;
    }
}

分治算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KoRAVmE-1584535809928)(E:\笔记\图片\1584340379464.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zjnjkanl-1584535809928)(E:\笔记\图片\1584340439795.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3R2YZZg-1584535809929)(E:\笔记\图片\1584340511467.png)]

分治算法之汉诺塔

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qt9PWbuC-1584535809930)(E:\笔记\图片\1584341034562.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BoX6IC73-1584535809930)(E:\笔记\图片\1584341276408.png)]

package 算法.分治算法之汉诺塔;

public class HanNuoTower {
    public static void main(String[] args) {
      HanNuo(3,'a','b','c');
    }
    public static void HanNuo(int num,char a,char b,char c){
        if(num==1){
            System.out.println("第1个盘从"+a+"->"+c);
        }else {
            //把上面的所有盘移动到b,移动中利用c塔
            HanNuo(num-1,a,c,b);
            //把最下面的盘从a移动到c
            System.out.println("第"+num+"个盘从"+a+"->"+c);
            //把b塔所有盘从b移动到c,移动中利用a塔
            HanNuo(num-1,b,a,c);
        }
    }
}

动态规划算法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUZaycHB-1584535809931)(E:\笔记\图片\1584339893690.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1RvwYJlS-1584535809931)(E:\笔记\图片\1584344535186.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NMtjx35Y-1584535809932)(E:\笔记\图片\1584344622777.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcCpehE8-1584535809932)(E:\笔记\图片\1584347355265.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m3yxA97J-1584535809933)(E:\笔记\图片\1584347076058.png)]

package 算法.动态规划之背包问题;

public class bag {
    public static void main(String[] args) {
        //物品的重量
        int[] w = {1,4,3}; //0是对应表格的物品为0磅的情况
        int[] val={1500,3000,2000};//物品价格
        int bagWeight = 4 ;//背包重量
        int n = val.length ; //物品个数
        //创建二维数组
        //v[i][j]表示 前i个物品中能够装入容量为j的背包的最大价值
        //n+1,m+1是因为表格第0行和第0列都为0
        int[][] v= new int[n+1][bagWeight+1];
        //定义一个二维数组记录商品的存放情况
        int[][] path = new int[n+1][bagWeight+1];
        //第0列初始化
        for (int i = 0; i <v.length ; i++) {
            v[i][0] =0;
        }
        //第0行初始化
        for (int i = 0; i <v[0].length ; i++) {
            v[0][i]=0;
        }
        //根据前面的公式进行动态规划处理
        // i和j都是1 因为0行0列已经初始化
        for (int i = 1; i <v.length ; i++) {
            for (int j = 1; j <v[0].length ; j++) {
                 //如果当前物品大于背包当前容量j,那么当前最大价值为
                if(w[i-1]>j){
                    v[i][j]=v[i-1][j];
                }else {
                    //否则 v[i][j] =max{val[i-1]+v[i-1][j-w[i-1]],v[i-1][j]]}
                    if(v[i-1][j]<val[i-1]+v[i-1][j-w[i-1]]){
                        //满足上面条件,表明增加当前商品选择后,有更好的方案
                        v[i][j]=val[i-1]+v[i-1][j-w[i-1]];
                        //把当前的情况记录到patj
                        path[i][j]=1;
                    }else {
                        v[i][j]=v[i-1][j];
                    }
                }
            }
        }
        //测试一下输出
        for (int i = 0; i <v.length ; i++) {
            System.out.println("\n");
            for (int j = 0; j <v[0].length ; j++) {
                System.out.print(v[i][j]);
                System.out.print(" ");
            }

        }
        //输出最高价值,并显示最佳的搭配方式
        int i =path.length-1;
        int j = path[0].length-1;
        //从path的最后开始找
        System.out.println("最大价值为:"+v[i][j]);
        while (i>0&&j>0){
            //path[][]=1 的值都是增加当前商品后的最好方案
            if(path[i][j]==1){
                System.out.println("第"+i+"个商品放入背包");
                //一个商品只能放一次,放入后,j减去商品重量
                j=j-w[i-1];
            }
            i--;
        }

    }
}

稀疏数组

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YaCxrp29-1584535809933)(E:\笔记\图片\1584510271658.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTtaHH2c-1584535809934)(E:\笔记\图片\1584510483882.png)]

稀疏数组第一行记录原表的行数 列数 特殊值个数

下面分别记录每个特殊值的所在位置和值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2fjEfHi-1584535809936)(E:\笔记\图片\1584510771963.png)]

package 算法.稀疏数组;

public class SparseArray {
    public static void main(String[] args) {
         int [][] testArr = new int[11][11];
         testArr[1][1]=1;
         testArr[2][3]=3;
         testArr[3][4]=6;
         int[][] SparseArray = getSparseArray(testArr,0);
        for (int i = 0; i <SparseArray.length ; i++) {
            for (int j = 0; j <SparseArray[0].length ; j++) {
                System.out.print(SparseArray[i][j]+"\t");

            }
            System.out.println(" ");
        }
        System.out.println("");
        int[][] resArr = getArray(SparseArray,0);
        for (int i = 0; i <resArr.length ; i++) {
            for (int j = 0; j <resArr[0].length ; j++) {
                System.out.print(resArr[i][j]+"\t");

            }
            System.out.println(" ");
        }
    }
    //接收普通数组,和数组的大多数默认值,返回稀疏数组
    public  static  int[][] getSparseArray(int arr[][],int defautNum){
        //获取普通数组的特殊值个数
        int count =0;
        int temp=1;
        for (int i = 0; i <arr.length ; i++) {
            for (int j = 0; j <arr[0].length ; j++) {
                if(arr[i][j]!=defautNum){
                    count++;
                }
            }
        }
        int[][] SparseArray=new int[count+1][3];
        SparseArray[0][0]=arr.length;
        SparseArray[0][1]=arr[0].length;
        SparseArray[0][2]=count;
        for (int i = 0; i <arr.length ; i++) {
            for (int j = 0; j <arr[0].length ; j++) {
                if(arr[i][j]!=defautNum){
                    SparseArray[temp][0]=i;
                    SparseArray[temp][1]=j;
                    SparseArray[temp][2]=arr[i][j];
                    temp++;
                }
            }
        }
        return SparseArray;
    }
    //接收稀疏数组,返回普通数组
    public static int[][] getArray(int[][] sparseArr,int defalutNum){

        int arr[][]= new int[sparseArr[0][0]][sparseArr[0][1]];
        for (int i = 0; i <arr.length ; i++) {
            for (int j = 0; j <arr[0].length ; j++) {
                arr[i][j]=defalutNum;
            }
        }
        for (int i = 1; i <sparseArr.length ; i++) {
            arr[sparseArr[i][0]][sparseArr[i][1]]=sparseArr[i][2];
        }
        return arr;
    }
}

数组模拟队列**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHk1DDfb-1584535809936)(E:\笔记\图片\1584379322297.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-btAEKIGC-1584535809936)(E:\笔记\图片\1584379351004.png)]

package 算法.用数组模拟队列;

import java.util.Scanner;

public class ArrayQueueDemo {
    public static void main(String[] args) {

        //创建一个队列
        ArrayQueue queue= new ArrayQueue(3);
        char key = ' ';//接收用户输入
        Scanner scanner= new Scanner(System.in);
        boolean loop = true;
         //输出菜单
        while (loop){
            System.out.println("输入s (show):显示队列");
            System.out.println("输入e (exit):退出程序");
            System.out.println("输入a (add):添加数据");
            System.out.println("输入g (get):取出数据");
            System.out.println("输入h (head):查看头数据");
            key = scanner.next().charAt(0);
            switch (key){
                case 's':
                    queue.show();;
                    break;
                case 'a':
                    System.out.println("输入一个数");
                    int value =scanner.nextInt();
                    queue.add(value);
                    break;
                case 'e':
                    System.out.println("程序退出");
                    return;
                case 'g':
                    try {
                        int res =  queue.getQueue();
                        System.out.println("取出的数据是"+res);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                case 'h':
                    try {
                        int res =  queue.headQueue();
                        System.out.println("队列头数据是"+res);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                default:
                        break;

            }
        }
    }
}
class ArrayQueue{
    private int maxSize;//定义数组最大容量
    private int front;//队列头
    private  int rear;//队列尾
    private int[] arr; //存放数据的数组,模拟队列
    //创建队列的构造器
    public ArrayQueue(int maxSize){
        this.maxSize=maxSize;
        arr=new int[maxSize];
        front=-1;//指向队列头的前一个位置
        rear=-1;
    }
    public boolean isFull(){
         return rear==maxSize-1;
    }
    public boolean isEmpty(){
        return  rear==front;
    }
    //添加数据
    public void add(int n){
         if(isFull()){
             System.out.println("队列满无法加入");

         }else {
             rear++;
             arr[rear]=n;
         }
    }
    //获取队列头数据并且出队
    public int  getQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列已经空");
        }else {
            front++;
            return  arr[front];
        }
    }
    public void show(){
        //显示队列数据
        if(isEmpty()){
            System.out.println("队列空");
            return;
        }
        for (int i = 0; i <arr.length ; i++) {
            System.out.printf("arr[%d]=%d",i,arr[i]);
            System.out.println("");
        }
    }
    //显示队列头数据
    public int headQueue(){
        if(isEmpty()){
            throw new RuntimeException("当前数据为空,无法获取头部数据");
        }else {
            return  arr[front+1];
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ktYqMb3d-1584535809937)(E:\笔记\图片\1584383034054.png)]

环形队列

//有个位置是空的,而且会动态变化,尾指针一直指向该位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qcn6OcbO-1584535809938)(E:\笔记\图片\1584415996754.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rLdTqLb-1584535809938)(E:\笔记\图片\1584416145791.png)]

package 算法.用数组模拟队列;

import java.util.Scanner;

public class CircleArrayQueuedemo {
    public static void main(String[] args) {

        //创建一个队列
        CircleArrayQueue queue= new CircleArrayQueue(4);
        char key = ' ';//接收用户输入
        Scanner scanner= new Scanner(System.in);
        boolean loop = true;
        //输出菜单
        while (loop){
            System.out.println("输入s (show):显示队列");
            System.out.println("输入e (exit):退出程序");
            System.out.println("输入a (add):添加数据");
            System.out.println("输入g (get):取出数据");
            System.out.println("输入h (head):查看头数据");
            key = scanner.next().charAt(0);
            switch (key){
                case 's':
                    queue.show();;
                    break;
                case 'a':
                    System.out.println("输入一个数");
                    int value =scanner.nextInt();
                    queue.add(value);
                    break;
                case 'e':
                    System.out.println("程序退出");
                    return;
                case 'g':
                    try {
                        int res =  queue.getQueue();
                        System.out.println("取出的数据是"+res);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                case 'h':
                    try {
                        int res =  queue.headQueue();
                        System.out.println("队列头数据是"+res);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;

            }
        }
    }
}
class CircleArrayQueue{
    private int maxSize;//定义数组最大容量
    private int front;//队列头
    private  int rear;//队列尾
    private int[] arr; //存放数据的数组,模拟队列
    //创建队列的构造器
    public CircleArrayQueue(int maxSize){
        this.maxSize=maxSize;
        arr=new int[maxSize];
        front=0;//头指针指向队列第一个元素
        rear=0;//尾指针指向队列最后一个元素的后一位,留一个空间
    }
    public boolean isFull(){
        return (rear+1)%maxSize==front;
    }
    public boolean isEmpty(){
        return  rear==front;
    }
    //添加数据
    public void add(int n){
        if(isFull()){
            System.out.println("队列满无法加入");

        }else {

            arr[rear]=n;
            rear=(rear+1)%maxSize;
        }
    }
    //取队列头数据并且出队
    public int  getQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列已经空");
        }else {
            int value = arr[front];
            front=(front+1)%maxSize;
            return  value;
        }
    }
    public void show(){
        //显示队列数据
        //从front开始遍历
        //个数为 (rear+maxSeze-front)%maxSize
        if(isEmpty()){
            System.out.println("队列空");
            return;
        }
        for (int i = front; i <front+(rear+maxSize-front)%maxSize; i++) {
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }
    //显示队列头数据
    public int headQueue(){
        if(isEmpty()){
            throw new RuntimeException("当前数据为空,无法获取头部数据");
        }else {
            return  arr[front+1];
        }
    }
}

单链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7k6VdYDN-1584535809939)(E:\笔记\图片\1584421756115.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JecYHnZv-1584535809939)(E:\笔记\图片\1584423836606.png)]

package 算法.单向链表的创建和遍历;

public class LinkedListDemo {
    public static void main(String[] args) {
      HeroNode node1 = new HeroNode(1,"宋江");
      HeroNode node2 = new HeroNode(2,"卢俊义");
      HeroNode node3 = new HeroNode(3,"吴用");
      HeroNode node4 = new HeroNode(4,"林冲");
      LinkList list = new LinkList();
      list.add(node1);
      list.add(node2);
      list.add(node3);
      list.add(node4);
      list.show();
    }
}
//创建一个链表类
class LinkList{
  //先初始化一个头节点
    private HeroNode head= new HeroNode(0,"");
    //添加结点
    //如果不考虑编号
    //找到链表的尾指针,指向添加的结点
    public  void  add(HeroNode node){
        HeroNode temp =head;
        while (true){
            if(temp.next==null){
                break;
            }
            temp=temp.next;
        }
        temp.next=node;
    }
    //遍历链表
    public void show(){
        HeroNode temp = head.next;//本例中头节点不存放数据
        if (head.next==null){
            System.out.println("链表为空");
            return;
        }
        while (true){
            if(temp==null){
             break;
            }
            System.out.println(temp);
            temp=temp.next;
        }
    }
}
//结点类
class  HeroNode{
    public  int no;
    public  String name;
    public HeroNode next;
    public HeroNode(int no,String name){
        this.no=no;
        this.name=name;

    }


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

根据排名来添加结点,修改结点,删除结点

package 算法.单向链表的创建和遍历;

public class LinkedListDemo {
    public static void main(String[] args) {
      HeroNode node1 = new HeroNode(1,"宋江");
      HeroNode node2 = new HeroNode(2,"卢俊义");
      HeroNode node3 = new HeroNode(3,"吴用");
      HeroNode node4 = new HeroNode(4,"林冲");
      HeroNode node5 = new HeroNode(4,"林");
      HeroNode node6 = new HeroNode(6,"456");

      LinkList list = new LinkList();
      list.add(node1);
      list.add(node2);
      list.add(node4);
      list.addByNo(node3);

      list.show();

        System.out.println("");
       list.del(4);
      list.show();

    }
}
//创建一个链表类
class LinkList{
  //先初始化一个头节点
    private HeroNode head= new HeroNode(0,"");
    //添加结点
    //如果不考虑编号
    //找到链表的尾指针,指向添加的结点
    public  void  add(HeroNode node){
        HeroNode temp =head;
        while (true){
            if(temp.next==null){
                break;
            }
            temp=temp.next;
        }
        temp.next=node;
    }
    //第二种添加英雄的方式
    //根据排名将英雄加入指定的位置,如果有这个排名。则添加失败
    public void addByNo(HeroNode node){
        //辅助指针指向要添加的位置的上一个结点
        HeroNode temp =head;
        //标志当前的添加的编号是否存在
        boolean flag= false;
        while (true){
            if (temp.next==null){
                //temp已经在链表的最后
                //当前结点指向要添加的结点
                break;
            }
            if(temp.next.no>node.no){
                //如果下一个结点的编号大于要添加的结点
                //那就添加到当前位置
                break;
            }else if(temp.next.no==node.no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        //判断是否要添加的编号已经存在
        if(flag){
            System.out.println("编号已经存在");
        }else {
            node.next=temp.next;
            temp.next=node;
        }
    }
    //根据no修改结点的信息,no不能修改,否则会打乱编号排序,修改no应该删除结点,再重新添加。
    //根据newHeronode的no值来添加修改即可
    public void update(HeroNode newHeroNode){
        if(this.head.next==null){
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head.next;
        boolean flag = false;
        while(true){
            if(temp==null){
                break;
            }
            if(temp.no==newHeroNode.no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag==true){
            temp.name=newHeroNode.name;
        }else {
            System.out.println("没有找到要修改的结点");
        }
    }
    //根据no删除结点
    public void del(int no){
         HeroNode temp = head;
        while (head==null){
             System.out.println("链表为空,无法删除");
             return;
         }
         while(true){
             if(temp.next==null){//要写在temp.next.no==nod判断语句之前,不然当temp是表尾会发生空指针异常
                 System.out.println("不存在指定删除的结点");
                 break;
             }
             if(temp.next.no==no){

                 temp.next=temp.next.next;
                 break;
             }

             temp=temp.next;
         }

    }
    //遍历链表
    public void show(){
        HeroNode temp = head.next;//本例中头节点不存放数据
         boolean flag=false;//flag标志添加的编号是否存在
        if (head.next==null){
            System.out.println("链表为空");
            return;
        }
        while (true){
            if(temp==null){
             break;
            }
            System.out.println(temp);
            temp=temp.next;
        }
    }
}
//结点类
class  HeroNode{
    public int no;
    public  String name;
    public HeroNode next;
    public HeroNode(int no,String name){
        this.no=no;
        this.name=name;

    }


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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDYdqNzb-1584535809940)(E:\笔记\图片\1584438505756.png)]

package 栈;

public class ArrayStackDemo {
    public static void main(String[] args) {
     ArrayStack stack = new ArrayStack(10);
     stack.push(1);
     stack.push(3);
     stack.push(5);
     stack.pop();
     stack.show();
    }
}

class  ArrayStack{
 private int maxSize;
 private int[] stack;
 private int top= -1;

    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack =new int[maxSize];

    }
    //栈满
    public boolean isFull(){
        return top==maxSize-1;
    }
    public boolean isEmpty(){
        return  top==-1;
    }
    //入栈
    public void push(int value){
        if(isFull()){
            System.out.println("栈已满");
            return;
        }
        top++;
        stack[top]=value;
    }
    public int pop(){
      if(isEmpty()){
          System.out.println("栈已空");
           throw new RuntimeException("栈空");
      }
      int value= stack[top];
      top--;
      return value;
    }
    //遍历栈,从栈顶开始输出
    public void show(){
        if(isEmpty()){
            System.out.println("栈空");
        }
        for (int i = top; i >=0 ; i--) {
            System.out.println(stack[i]);
        }
    }
}

t.println(“链表为空,无法删除”);
return;
}
while(true){
if(temp.nextnull){//要写在temp.next.nonod判断语句之前,不然当temp是表尾会发生空指针异常
System.out.println(“不存在指定删除的结点”);
break;
}
if(temp.next.no==no){

             temp.next=temp.next.next;
             break;
         }

         temp=temp.next;
     }

}
//遍历链表
public void show(){
    HeroNode temp = head.next;//本例中头节点不存放数据
     boolean flag=false;//flag标志添加的编号是否存在
    if (head.next==null){
        System.out.println("链表为空");
        return;
    }
    while (true){
        if(temp==null){
         break;
        }
        System.out.println(temp);
        temp=temp.next;
    }
}

}
//结点类
class HeroNode{
public int no;
public String name;
public HeroNode next;
public HeroNode(int no,String name){
this.no=no;
this.name=name;

}


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

}




**栈**

[外链图片转存中...(img-hDYdqNzb-1584535809940)]

package 栈;

public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack stack = new ArrayStack(10);
stack.push(1);
stack.push(3);
stack.push(5);
stack.pop();
stack.show();
}
}

class ArrayStack{
private int maxSize;
private int[] stack;
private int top= -1;

public ArrayStack(int maxSize) {
    this.maxSize = maxSize;
    stack =new int[maxSize];

}
//栈满
public boolean isFull(){
    return top==maxSize-1;
}
public boolean isEmpty(){
    return  top==-1;
}
//入栈
public void push(int value){
    if(isFull()){
        System.out.println("栈已满");
        return;
    }
    top++;
    stack[top]=value;
}
public int pop(){
  if(isEmpty()){
      System.out.println("栈已空");
       throw new RuntimeException("栈空");
  }
  int value= stack[top];
  top--;
  return value;
}
//遍历栈,从栈顶开始输出
public void show(){
    if(isEmpty()){
        System.out.println("栈空");
    }
    for (int i = top; i >=0 ; i--) {
        System.out.println(stack[i]);
    }
}

}

在这里插入图片描述

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

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


package facetest;

import java.util.*;

public class Main {
    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        in.nextLine();
        String arrayStr = in.nextLine();
        String[] arr = arrayStr.split(" ");
        int [] num = new int[n];
         ArrayList<Integer> list =new ArrayList<>();
        for (int i = 0; i < n; i++) {
            num[i] = Integer.parseInt(arr[i]);
            list.add(num[i]);
        }
        for (int i = 0; i <num.length ; i++) {
            Collections.sort(list);
            list.remove(i);
            int result = list.get(n/2-1);
            System.out.println(result);
            list.add(num[i]);
        }
    }


}

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值