数据结构和算法基础

目录

数据结构

1. 二维数组 和 稀疏数组 相互转换

2.数组实现队列

3:数组实现环形队列

5:双向链表

6:数组实现栈结构

8:二叉树数据结构

排序算法

1:冒泡排序算法

 2:选择排序算法

3:插入排序算法

4:希尔排序==插入升级版

5:快速排序算法

6:归并排序

7:基数排序(基于桶排序实现)

8:堆排序(基于二叉树实现)

查找算法

1:线性查找

2:二分查找算法(二分递归算法)(二分非递归查找算法)

3:插值查找算法:

4:黄金分割查找算法

5:递归查找算法


数据结构

1. 二维数组 和 稀疏数组 相互转换

 1、定义数组 二维数组int [][] twoArray = new int[10][10];
 2、定义稀疏数组 int [][] sparseArray = new int[count][3];
/**
 * 二维数组 和 稀疏数组 相互转换
 * 1、定义数组 二维数组int [][] twoArray = new int[10][10];
 * 2、定义稀疏数组 int [][] sparseArray = new int[count][3];
 * @author mgq
 * @create 2021-03-05 18:11
 */
public class TwoArrayToSparseArrayDemo {
    /**
     * 1、创建一个二维数组
     * 2、创建一个稀疏数组
     * 3、把二维数组 中不等于0的数据 转成稀疏数组
     * 4、把稀疏数组转成二维数组
     */

    public static void main(String[] args) {
//        TwoArrayToSparseArray();
        int [] aa = new int[6];
        aa[0]=1;
        aa[1]=2;
        aa[2]=3;
        aa[3]=4;
//        for (final int i : aa) {
//            System.out.print(i);
//        }
//        System.out.println();
        int [] bb =aa;
        System.out.println(aa==bb);
//        bb[0]=22;
//        bb[4]=66;
//        for (final int i : aa) {
//            System.out.print(i);
//        }
//        System.out.println();
//        for (final int j : bb) {
//            System.out.print(j);
//        }

    }

    /**
     * 二维数组转出稀疏数组
     */
    public static void TwoArrayToSparseArray(){
        // TODO: 2021/3/5 定义一个10行10列二维数组
        /**
         * 二维数组 ,其中7,8,9是其中的值
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  7  0  0  0  0  0  0
         * 0  0  0  0  0  8  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  9  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         */
        int [][] twoArray = new int[10][10];
        twoArray[2][3]=7;
        twoArray[3][5]=8;
        twoArray[5][7]=9;

        // 稀疏数组的行数
        int count =0;
        System.out.println("二维数组=》》》");
        // TODO: 2021/3/5 循环二维数组
        for (int i = 0; i < twoArray.length; i++) {
            for (int j = 0; j < twoArray[i].length; j++) {
                System.out.print(twoArray[i][j]+"  ");
                // TODO: 统计出二维数组中的不等于的有多少个
                if (twoArray[i][j]!=0){
                    count++;
                }
            }
            System.out.println();
        }
        System.out.println("稀疏数组=》》》");
        // TODO: 定义一个稀疏数组,3列,行数count,根据二维数组中统计的count
        /**
         * 稀疏数组
         * 0 0 0
         * 0 0 0
         * 0 0 0
         */
        int [][] sparseArray = new int[count][3];
        for (int i = 0; i < sparseArray.length; i++) {
            for (int j = 0; j < sparseArray[i].length; j++) {
                System.out.print(sparseArray[i][j]+" ");
            }
            System.out.println();
        }

        // 记录稀疏数组存放的行数
        int count2=0;
        // TODO: 把二维数组中不等于0的数据放入到稀疏数组中去
        /**
         * 稀疏数组中的值
         * 2 3 7
         * 3 5 8
         * 5 7 9
         */
        for (int i = 0; i < twoArray.length; i++) {
            for (int j = 0; j < twoArray[i].length; j++) {
                // TODO: 规则,二维数组中的值行列 ,对应的稀疏数组中列的1,2列两个值;二维数组中的值,对应的是稀疏数组中第三列的值
                if (twoArray[i][j]!=0){
                    sparseArray[count2][0]=i; // i=行
                    sparseArray[count2][1]=j; // j=列
                    sparseArray[count2][2]=twoArray[i][j]; // 值=twoArray[i][j]
                    // 行数+1
                    count2++;
                }
            }
        }
        System.out.println("二维数组中的数转到稀疏数组中=》》》");
        for (int i = 0; i < sparseArray.length; i++) {
            for (int j = 0; j < sparseArray[i].length; j++) {
                System.out.print(sparseArray[i][j]+" ");
            }
            System.out.println();
        }

        /**
         * 又稀疏数组赋值的二维数组
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  7  0  0  0  0  0  0
         * 0  0  0  0  0  8  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  9  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         * 0  0  0  0  0  0  0  0  0  0
         */
        System.out.println(" 稀疏数组转出另外一个空的二维数组=》》》");
        // TODO: 稀疏数组转出另外一个空的二维数组
        int [][] otherTwoArray = new int[10][10];
        for (int i = 0; i < sparseArray.length; i++) {
            /**
             * 稀疏数组
             * 行 列 值
             * 2 3 7
             */
            otherTwoArray[sparseArray[i][0]][sparseArray[i][1]]=sparseArray[i][2];
        }
        for (int i = 0; i < otherTwoArray.length; i++) {
            for (int j = 0; j < otherTwoArray[i].length; j++) {
                System.out.print(otherTwoArray[i][j]+"  ");
            }
            System.out.println();
        }
    }
}

2.数组实现队列

* 数组实现队列思路,先进先出 ,但是由于数组的角标用过不能再次复用,所以需要实现环形队列,看下一个例子
*1、创建队列类
*      * 2、属性:// 定义数组 // 队列长度// 队列中第一个值的索引值 // 队列最后一个值得索引值
*      * 3、方法:addQueue() , getFirstQueue()*

 

/**
 * 数组实现队列思路,先进先出 ,但是由于数组的角标用过不能再次复用,所以需要实现环形队列,看下一个例子
 *1、创建队列类
 *      * 2、属性:// 定义数组 // 队列长度// 队列中第一个值的索引值 // 队列最后一个值得索引值
 *      * 3、方法:addQueue() , getFirstQueue()*
 * @author mgq
 * @create 2021-03-05 20:07
 */
public class ArrayToQueueDemo {

    public static void main(String[] args) {
        boolean flag = true;
        // 开启控制台输入
        Scanner scanner = new Scanner(System.in);
        ArrayQueue arrayQueue = new ArrayQueue();
        while (flag){
            System.out.println("请输入a,请设置队列的深度");
            System.out.println("请输入b,查看队列所有值");
            System.out.println("请输入c,获取队列中的第一个值");
            System.out.println("请输入d,添加一个值到队列");
            System.out.println("请输入e,退出程序");
            // scanner.next() 获取控制台输入的值
            String next = scanner.next();
            switch(next){
                case "a" :
                    System.out.println("请输入队列的深度");
                    int length = scanner.nextInt();
                    arrayQueue.setArrayQueueLength(length);
                    System.out.println("设置队列深度成功");
                    break; //可选
                case "b" :
                    int[] array = arrayQueue.getArray();
                    System.out.println("队列的值如下:");
                    for (int i = 0; i < array.length; i++) {
                        if (array[i]!=0){
                            System.out.println(array[i]);
                        }
                    }
                    break; //可选
                case "c" :
                    int firstQueue = 0;
                    try {
                        firstQueue = arrayQueue.getFirstQueue();
                    } catch (Exception e) {
                        e.getMessage();
                        System.out.println("队列中没有值");
                        break;
                    }
                    System.out.println("结果如下:"+firstQueue);
                    break; //可选
                case "d" :
                    System.out.println("输入要添加的值");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    System.out.println("添加值成功");
                    break; //可选
                case "e" :
                    flag=false;
                    break;
//                    return;
                default : //可选
                    System.out.println("输入格式不符合!!!");
                    break;
                    //语句
            }
        }
        System.out.println("程序已退出");
        // 关闭控制台
        scanner.close();

    }

    /**
     * 1、创建队列类
     * 2、属性:
     */
    static class ArrayQueue{
        // 定义数组
        private int [] array;
        // 队列长度
        private int maxLength=0;
        // 队列中第一个值的索引值
        private int firstIndex=0;
        // 队列最后一个值得索引值
        private int endIndex=0;

        // 设置队列的最大深度
        public void  setArrayQueueLength(int length){
            array=new int[length];
            maxLength=length;
        }

        // 往队列插入一个值
        public void addQueue(int value){
            // 判断队列元素是否已经满了
            if (isOrNoFull()){
                throw new RuntimeException("队列元素已满,不能在此添加");
            }
            array[endIndex]=value;
            // 为下一个索引准备加入值
            endIndex++;
        }

        // 从队列中取出一个值,取出队列中第一个值
        public int getFirstQueue(){
            // 判断队列中是否有值
            if (!isOrNoHaveValue()){
                throw new RuntimeException("队列中没有值!!!");
            }
            // 取出队列最前面的元素
            int value = array[firstIndex];
            array[firstIndex]=0;
            firstIndex++;
            return value;
        }

        // 判断队列元素是否已经满了,用endIndex和maxLength作对比,满了返回true:就不在允许添加元素, false:没有满,可以添加值
        public boolean isOrNoFull(){
            return endIndex >= maxLength;
        }

        // 判断队列中是否有值,返回true:说明有值,返回false 没有值
        public boolean isOrNoHaveValue(){
            return firstIndex!=endIndex;
        }

        // get//set
        public int[] getArray() {
            return array;
        }

        public void setArray(int[] array) {
            this.array = array;
        }

        public int getMaxLength() {
            return maxLength;
        }

        public void setMaxLength(int maxLength) {
            this.maxLength = maxLength;
        }

        public int getFirstIndex() {
            return firstIndex;
        }

        public void setFirstIndex(int firstIndex) {
            this.firstIndex = firstIndex;
        }

        public int getEndIndex() {
            return endIndex;
        }

        public void setEndIndex(final int endIndex) {
            this.endIndex = endIndex;
        }
    }
}

3:数组实现环形队列

数组实现环形队列,解决数组角标不能复用的问题,利用 取余 %来解决这个问题
/**
 * 数组实现环形队列,解决数组角标不能复用的问题,利用 取余 %来解决这个问题
 *
 * java里/和%的区别
 * 1》 / 取得是两个数相除后的整数部分。
 * 2》 % 取得是两个数相除后的余数部分。
 * 例如 int a= 7%3 ,除完以后结果就是2余数是1,那么余数1就是取余结果
 *  * 例如 int a =2%3; 余数也是2,这种比被除数小的,那么除数直接就是余数
 *  *1、创建环形队列类
 *  *      * 2、属性:// 定义数组 // 队列长度// 队列中第一个值的索引值 // 队列最后一个值得索引值
 *  *      * 3、方法:addQueue() , getFirstQueue()*
 * @author mgq
 * @create 2021-03-05 22:04
 */
public class CircleArrayToQueueDemo {

    public static void main(String[] args) {
        boolean flag = true;
        // 开启控制台输入
        Scanner scanner = new Scanner(System.in);
        ArrayQueue arrayQueue = new ArrayQueue();
        while (flag){
            System.out.println("请输入a,请设置队列的深度");
            System.out.println("请输入b,查看队列所有值");
            System.out.println("请输入c,获取队列中的第一个值");
            System.out.println("请输入d,添加一个值到队列");
            System.out.println("请输入e,退出程序");
            // scanner.next() 获取控制台输入的值
            String next = scanner.next();
            switch(next){
                case "a" :
                    System.out.println("请输入队列的深度");
                    int length = scanner.nextInt();
                    arrayQueue.setArrayQueueLength(length);
                    System.out.println("设置队列深度成功");
                    break; //可选
                case "b" :
                    int[] array = arrayQueue.getArray();
                    System.out.println("队列的值如下:");
                    for (int i = 0; i < array.length; i++) {
                        if (array[i]!=0){
                            System.out.println(array[i]);
                        }
                    }
                    break; //可选
                case "c" :
                    int firstQueue = 0;
                    try {
                        firstQueue = arrayQueue.getFirstQueue();
                    } catch (Exception e) {
                        e.getMessage();
                        System.out.println("队列中没有值");
                        break;
                    }
                    System.out.println("结果如下:"+firstQueue);
                    break; //可选
                case "d" :
                    System.out.println("输入要添加的值");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    System.out.println("添加值成功");
                    break; //可选
                case "e" :
                    flag=false;
                    break;
//                    return;
                default : //可选
                    System.out.println("输入格式不符合!!!");
                    break;
                //语句
            }
        }
        System.out.println("程序已退出");
        // 关闭控制台
        scanner.close();

    }

    /**
     * 1、创建队列类
     * 2、属性:
     */
    static class ArrayQueue{
        // 定义数组
        private int [] array;
        // 队列长度
        private int maxLength=0;
        // 队列中第一个值的索引值
        private int firstIndex=0;
        // 队列最后一个值得索引值
        private int endIndex=0;

        // 设置队列的最大深度
        public void  setArrayQueueLength(int length){
            array=new int[length];
            maxLength=length;
        }

        // 往队列插入一个值
        public void addQueue(int value){
            int bianliang=0;
            // 判断队列元素是否已经满了
            if (isOrNoFull()){
                throw new RuntimeException("队列元素已满,不能在此添加");
            }
            // 实现环形队列修改处——————如果endIndex比maxleng大,那么也要取模%
            bianliang=endIndex%maxLength;

            array[bianliang]=value;
            // 为下一个索引准备加入值
            endIndex++;
        }

        // 从队列中取出一个值,取出队列中第一个值
        public int getFirstQueue(){
            int bianliang=0;
            // 判断队列中是否有值
            if (!isOrNoHaveValue()){
                throw new RuntimeException("队列中没有值!!!");
            }
            // 实现环形队列修改处——————如果firstIndex的值已经超过了最大长度maxlength ,说明之前的数都被取了一遍了,其实数组里面都是空了
            // ,那么我们就让重新去从数组第一去取,利用取模%来实现.
            bianliang=firstIndex%maxLength;

            // 取出队列最前面的元素
            int value = array[bianliang];
            array[bianliang]=0;
            firstIndex++;
            return value;
        }

        // 判断队列元素是否已经满了,用endIndex和maxLength作对比,满了返回true:就不在允许添加元素, false:没有满,可以添加值
        public boolean isOrNoFull(){

            // 环形队列需要修改的地方:尾坐标减去首坐标永远不能大于max-1,也就是不能超过队列深度
            return endIndex-firstIndex>maxLength-1;
//            return endIndex >= maxLength;
        }

        // 判断队列中是否有值,返回true:说明有值,返回false 没有值
        public boolean isOrNoHaveValue(){
            // 环形队列需要修改的地方:就是首坐标永远不能超过尾坐标
            return firstIndex-endIndex<0;
        }

        // get//set
        public int[] getArray() {
            return array;
        }

        public void setArray(int[] array) {
            this.array = array;
        }

        public int getMaxLength() {
            return maxLength;
        }

        public void setMaxLength(int maxLength) {
            this.maxLength = maxLength;
        }

        public int getFirstIndex() {
            return firstIndex;
        }

        public void setFirstIndex(int firstIndex) {
            this.firstIndex = firstIndex;
        }

        public int getEndIndex() {
            return endIndex;
        }

        public void setEndIndex(final int endIndex) {
            this.endIndex = endIndex;
        }
    }
}

4:单向链表实现

* 1:实现单向链表的增删改查操作,
*:2:并解决各大面试题
* // 实现功能:
*  1、 添加链表 向链表中添加数据
*  2、实现添加数据自动根据node1中的no号排序方法
*  3、查询链表所有节点
*  3、修改链表节点
*  5、删除数据节点
*  6、倒叙链表,也就是反转链表,两种方式实现
/**
 * 1:实现单向链表的增删改查操作,
 *:2:并解决各大面试题
 * // 实现功能:
 *  1、 添加链表 向链表中添加数据
 *  2、实现添加数据自动根据node1中的no号排序方法
 *  3、查询链表所有节点
 *  3、修改链表节点
 *  5、删除数据节点
 *  6、倒叙链表,也就是反转链表,两种方式实现
 *
 *   *  *1、创建双向链表类
 *  *  *      * 2、属性:
 *          *          //  public DoubleLikNode next; //指向下一节点对象的指针
 *  *  *      * 3、方法:申明一个空得head带头节点, add() , addOrderByNo() ,list(),update(),delete(),reverse(),reverseByStack()
 *
 * @author mgq
 * @create 2021-03-06 10:38
 */
public class SingleLinketListDemo {
    public static void main(String[] args) {
        LikNode node1 = new LikNode(1, "张三");
        LikNode node2 = new LikNode(2, "李四");
        LikNode node3 = new LikNode(3, "王五");
        LikNode node4 = new LikNode(4, "牛二");
        LikNode node6 = new LikNode(6, "牛三");
        LikNode node7 = new LikNode(7, "牛四");


        // 定义一个空链表头
        // 1、 添加链表 向链表中添加数据
        SingleLiketList linketList = new SingleLiketList();
        linketList.add(node1);
        linketList.add(node2);
        linketList.add(node3);
        linketList.add(node4);
        linketList.add(node6);
        linketList.add(node7);

        // 2、实现添加数据自动根据node1中的no号排序方法
        LikNode node5 = new LikNode(5, "排序");
        LikNode node8 = new LikNode(8, "排序");
        linketList.addOrderByNo(node5);
        linketList.addOrderByNo(node8);

        // 3、查询链表
        linketList.list();
        LikNode upt8 = new LikNode(8, "测试修改1");
        LikNode upt9 = new LikNode(9, "测试修改不存在no的数据");
        // 4、修改链表中元素
        linketList.update(upt8);
        linketList.update(upt9);
        // 修改完以后在查询一遍数据
        linketList.list();

        // 5、删除数据
        LikNode del10 = new LikNode(10, "测试删除不存的节点");
        linketList.delete(node1);
        linketList.delete(del10);
        // 删除后在查询一边
        linketList.list();

        // 6、倒叙链表,也就是反转链表
//        System.out.println("我是反转");
//        linketList.reverse();
//        linketList.list();
        // 第二种反转,利用栈,先进后出
//        Stack stack = new Stack();
        // 入栈
//        stack.push("");
        // 出栈
//        stack.pop();
        linketList.reverseByStack();
    }



}
class SingleLiketList{
    private LikNode head = new LikNode(); //定义一个空链表头

    /**
     * 添加元素节点
     * 1、首先先定义一个head 头节点,
     * 2、定义变量接收头结点,因为head的不允许变动
     *  注意:这块因为temp.next是一个新的对象,那么只能用变量来接收,如果在用head接收的话,那么head的引用地址就变了,这也就是为什么需要定义一个temp变量了
     *  temp=temp.next;   // temp == (LikNode next = temp.next);
     * * @param likNode
     */
    public void add(LikNode likNode){
        // 这一步非常重要,因为head中的值需要temp取赋值
        LikNode temp = head;
//        System.out.println(temp==head);
        while (true){
            if (temp.next==null) {
                // 说明此指针下没有对象,也就是此对象为链表尾节点,那么我们就把下一个新添加的对象付给这个尾节点
                temp.next=likNode;
                return;
            }
            // 这块因为temp.next是节点指针中的,那么只能用变量来接收,如果在用head接收的话,那么head的引用地址就变了,这也就是为什么需要定义一个temp变量了
            // 当temp是节点指针中指向的对象的时候,那么改变temp就可以改变head中对应的子节点对象的值
            temp=temp.next;   // temp == (LikNode next = temp.next); temp == 子节点对象

//            System.out.println(temp==head);
        }
    }

    /**
     * 实现添加元素后 根据 no排序,只需要记住一定要根据上一个节点去定位插入位置,这是单向链表
     * 因为正常情况下我们只要根据链表指针找到最后一个尾节点,在尾节点添加即可,
     * 那么排序的这个就需要我们在循环过程中对比 no 后,找到需要插入位置的上一个节点,因为是链表,我们只有知道了上一个节点的位置,才之后面节点的位置
     * * @param likNode
     */
    public void addOrderByNo(LikNode likNode){
        LikNode temp = head;
        // 先定义一个cur变量,表示当前node徐需要插入的位置的下一个节点
        LikNode curNext=null;
        while (true){
            // 1、如果是最后一个节点,那么就直接插入
            if (temp.next==null){
                temp.next=likNode;
                break;
            }
            // 2、如果此节点已存在,给出提示
            if (temp.next.no==likNode.no){
                System.out.println("此节点已存在,不允许再次插入!!!");
                break;
            }
            // 3、根据上一个节点的 temp.next找到下一个节点位置
            if (temp.next.no>likNode.no){
                // 先让自己指定下一个节点位置
                likNode.next=temp.next;
                // 然后让上一个节点指向自己
                temp.next=likNode;
                break;
            }
            // 如果没有找到,那么就继续循环去找
            temp=temp.next;
        }
        // 因为是单链表,那么我们就要根据指针,给这两个对象建立关系
//        likNode.next=curNext;


    }

    /**
     * 获取链表中所有的值
     * 1、首先从head中获取,直接循环head
     */
    public void list(){
        if (head==null) {
            System.out.println("链表数据为空");
            return;
        }
        LikNode temp = head;
        //当链表中数据不为空,我们就遍历head
        while (true){
            if (temp.next==null) {
                break;
            }
            System.out.println(temp.next);

            temp=temp.next;
        }
    }

    /**
     * 修改链表中的元素,根据编号no,
     * 步骤:先找到前一个节点,temp.next.no==no 去定位编号一样的,在把指针相互指向即可
    */
    public void update(LikNode likNode){
        LikNode temp = head;
        boolean flag=true; // 标记找不到匹配的no
        while (true){
            if(temp.next==null){
               break;
            }
            if(temp.next.no==likNode.no){
                // 进行修改操作  , temp.next 就是当前对象
                LikNode next = temp.next;
                next.setNext(likNode.getNext());
                next.setName(likNode.getName());
                // 如果找到,我们就给flag标记赋值false,如果找不到匹配的no,那么我们就给出提示
                flag=false;
                break;
            }

            // 遍历去找下一个节点
            temp=temp.next;
        }
        if (!flag){
            System.out.println("说明我们修改了数据");
        }else{
            System.out.println("根据no编号没有找到需要修改的数据!!!");
        }
    }

    /**
     * 删除链表中的元素,
     * 步骤,删除元素,需要我们根据no匹配到相同的编号的结点,那么我们只需要,把当前节点的上一个节点的next指针指向当前的节点的下一个节点
     */
    public void delete(LikNode node){
        LikNode temp = head;
        boolean flag =true;
        while (true){
           if (temp.next==null){break;}
            // 找到当前节点
            if (temp.next.no==node.no) {
                temp.next=temp.next.next;
                // 找到了此节点打个标记
                flag=false;
                break;
            }
            temp=temp.next;
        }
        if(!flag){
            System.out.println("删除了此节点!!");
        }else{
            System.out.println("没有找到此编号的节点,无法删除操作");
        }
    }

    /**
     * 反转链表
     * 步骤:创建另一个链表,然后把当前链表的元素从第一个取出,放到另一个链表中,然后取出第二个放到第一个之前,以此类推。
     */
    public void reverse(){
        /*LikNode temp = head.next;
        // 定义一个新的链表,用于临时接收
        LikNode newHead = new LikNode();
        // 临时接收当前节点的下一个节点
        LikNode tempNext = null;
        while (true){
            if (temp==null) {
                break;
            }
            // 把当前节点的下一节点赋值给tempNext
            tempNext=temp.next;
            // 把新链表上的节点赋值给 temp.next,就是实现反转了,这样首先会改变temp.next的引用,这样依赖,我们后面在操作temp.next 就不会影响 tempNext了
            temp.next= newHead.next;
            // 把当前节点赋给newHead.next,并且 当前节点的next是 上一个节点,因为我们上面步骤 temp.next= newHead.next;已经接收的上次循环的节点
            newHead.next=temp;
            // 再把当前节点后面的节点赋给temp,让他继续去循环
            temp=tempNext;
        }
        // 把新链表直接给原来的head链表
        head.next=newHead.next;*/

        // 上面是注释,下面这个是第二遍
        LikNode temp = head.next;
        LikNode newHead = new LikNode();
        LikNode next = null;
        while (true){
            if(temp==null){break;}
            next=temp.next;
            temp.next= newHead.next;
            newHead.next=temp;
            temp=next;
        }
        head.next=newHead.next;
    }

    /**
     * 通过栈进行数据的反转
     */
    public void reverseByStack(){
        LikNode temp = this.head;
        LikNode cur =null;
        Stack<LikNode> stack = new Stack();
        while (true){
            if (temp.next==null) {
                stack.push(temp);
                break;
            }
            //获取当前对象进行压栈/入栈操作
            stack.push(temp);
            temp=temp.next;
        }
        System.out.println("开始出栈!!!");
        // stack.isEmpty(); 出栈
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
        System.out.println();
    }


}

/**
 * 定义单向链表节点对象
 *
 * 1、单项链表 ,首先是单项的,其次每一个对象都保存下一个对象的指针,从链表中对位对象每次都需要从头遍历链表,
 * 2、单项链表:由 链表头,节点,链表尾(为null的)组成,其中节点中包含指向下一个对象的指针
 */
class LikNode{
    public int no; //行号
    public String name; //名称
    public LikNode next; //下一节点对象

    public LikNode() {

    }

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

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

    public LikNode getNext() {
        return next;
    }

    public void setNext(final LikNode next) {
        this.next = next;
    }

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

5:双向链表

前面演示了单项链表,这次演示双向链表的增删改查(单项链表不足,由于每次删除都需要先定位到前面的节点,那么双向链表就没有这个问题)
* 双向链表优点: 删除 和 反转 不在需要像单向链表那样那样麻烦了,因为节点的pre会指向前面的节点
*  *1、创建双向链表类
*  *      * 2、属性:
        *          //  public DoubleLikNode next; //指向下一节点对象的指针
        *         // 相比较与单项链表,多了一个指向前一节点对象的指针
        *         public DoubleLikNode pre;
*  *      * 3、方法:申明一个空得head带头节点, add() , addOrderByNo() ,list(),update(),delete(),reverse(),reverseByStack()

 

/**
 * 前面演示了单项链表,这次演示双向链表的增删改查(单项链表不足,由于每次删除都需要先定位到前面的节点,那么双向链表就没有这个问题)
 * 双向链表优点: 删除 和 反转 不在需要像单向链表那样那样麻烦了,因为节点的pre会指向前面的节点
 *  *1、创建双向链表类
 *  *      * 2、属性:
         *          //  public DoubleLikNode next; //指向下一节点对象的指针
         *         // 相比较与单项链表,多了一个指向前一节点对象的指针
         *         public DoubleLikNode pre;
 *  *      * 3、方法:申明一个空得head带头节点, add() , addOrderByNo() ,list(),update(),delete(),reverse(),reverseByStack()
 * @author mgq
 * @create 2021-03-08 0:27
 */
public class DoubleLinketListDemo {
    public static void main(String[] args) {
        DoubleLikNode node1 = new DoubleLikNode(1, "张三");
        DoubleLikNode node2 = new DoubleLikNode(2, "李四");
        DoubleLikNode node3 = new DoubleLikNode(3, "王五");
        DoubleLikNode node4 = new DoubleLikNode(4, "牛二");
        DoubleLikNode node6 = new DoubleLikNode(6, "牛三");
        DoubleLikNode node7 = new DoubleLikNode(7, "牛四");


        // 定义一个空链表头
        System.out.println("添加了数据");
        // 1、 添加链表 向链表中添加数据
        DoubleLiketList linketList = new DoubleLiketList();
        linketList.add(node1);
        linketList.add(node2);
        linketList.add(node3);
        linketList.add(node4);
        linketList.add(node6);
        linketList.add(node7);

        System.out.println("根据条件添加了数据");
        // 2、实现添加数据自动根据node1中的no号排序方法
        DoubleLikNode node5 = new DoubleLikNode(5, "排序");
        DoubleLikNode node8 = new DoubleLikNode(8, "排序");
        linketList.addOrderByNo(node5);
        linketList.addOrderByNo(node8);

        System.out.println("查询了数据");
        // 3、查询链表
        linketList.list();
        DoubleLikNode upt8 = new DoubleLikNode(8, "测试修改1");
//        DoubleLikNode upt9 = new DoubleLikNode(9, "测试修改不存在no的数据");
        // 4、修改链表中元素
        linketList.update(upt8);
//        linketList.update(upt9);
        // 修改完以后在查询一遍数据
        linketList.list();

        // 5、删除数据
//        DoubleLikNode del10 = new DoubleLikNode(10, "测试删除不存的节点");
        linketList.delete(node2);
//        linketList.delete(del10);
        // 删除后在查询一边
        linketList.list();

        // 6、倒叙链表,也就是反转链表
        System.out.println("我是反转");
        linketList.reverse();
//        linketList.list();
        // 第二种反转,利用栈,先进后出
//        Stack stack = new Stack();
        // 入栈
//        stack.push("");
        // 出栈
//        stack.pop();
//        linketList.reverseByStack();

    }
}

class DoubleLiketList {
    private DoubleLikNode head = new DoubleLikNode(); //定义一个空链表头

    /**
     * 添加元素节点
     * 1、首先先定义一个head 头节点,
     * 2、定义变量接收头结点,因为head的不允许变动
     * 注意:这块因为temp.next是一个新的对象,那么只能用变量来接收,如果在用head接收的话,那么head的引用地址就变了,这也就是为什么需要定义一个temp变量了
     * temp=temp.next;   // temp == (DoubleLikNode next = temp.next);
     * * @param DoubleLikNode
     */
    public void add(DoubleLikNode likNode) {
        // 这一步非常重要,因为head中的值需要temp取赋值
        DoubleLikNode temp = head;
//        System.out.println(temp==head);
        while (true) {
            if (temp.next == null) {
                // 说明此指针下没有对象,也就是此对象为链表尾节点,那么我们就把下一个新添加的对象付给这个尾节点
                temp.next = likNode;
                // 双向链表中 当前节点指向上一节点的指针
                temp.next.pre= temp;
                return;
            }
            // 这块因为temp.next是节点指针中的,那么只能用变量来接收,如果在用head接收的话,那么head的引用地址就变了,这也就是为什么需要定义一个temp变量了
            // 当temp是节点指针中指向的对象的时候,那么改变temp就可以改变head中对应的子节点对象的值
            temp = temp.next;   // temp == (LikNode next = temp.next); temp == 子节点对象

//            System.out.println(temp==head);
        }
    }

    /**
     * 实现添加元素后 根据 no排序,只需要记住一定要根据上一个节点去定位插入位置,这是单向链表
     * 因为正常情况下我们只要根据链表指针找到最后一个尾节点,在尾节点添加即可,
     * 那么排序的这个就需要我们在循环过程中对比 no 后,找到需要插入位置的上一个节点,因为是链表,我们只有知道了上一个节点的位置,才之后面节点的位置
     * * @param likNode
     */
    public void addOrderByNo(DoubleLikNode likNode) {
        DoubleLikNode temp = head;
        // 先定义一个cur变量,表示当前node徐需要插入的位置的下一个节点
        DoubleLikNode curNext = null;
        while (true) {
            // 1、如果是最后一个节点,那么就直接插入
            if (temp.next == null) {
                temp.next = likNode;
                // 双向节点,指向上一个节点
                likNode.pre=temp;
                break;
            }
            // 2、如果此节点已存在,给出提示
            if (temp.next.no == likNode.no) {
                System.out.println("此节点已存在,不允许再次插入!!!");
                break;
            }
            // 3、根据上一个节点的 temp.next找到下一个节点位置
            if (temp.next.no > likNode.no) {
                // 双向链表,指向前一个节点
                temp.next.pre=likNode;
                // 先让自己指定下一个节点位置
                likNode.next = temp.next;
                // 双向链表,指向前一个节点
                likNode.pre=temp;
                // 然后让上一个节点指向自己
                temp.next = likNode;
                break;
            }
            // 如果没有找到,那么就继续循环去找
            temp = temp.next;
        }
        // 因为是单链表,那么我们就要根据指针,给这两个对象建立关系
//        likNode.next=curNext;


    }

    /**
     * 获取链表中所有的值
     * 1、首先从head中获取,直接循环head
     */
    public void list() {
        if (head == null) {
            System.out.println("链表数据为空");
            return;
        }
        DoubleLikNode temp = head;
        //当链表中数据不为空,我们就遍历head
        while (true) {
            if (temp.next == null) {
                break;
            }
            System.out.println(temp.next);

            temp = temp.next;
        }
    }

    /**
     * 修改链表中的元素,根据编号no,
     * 双向链表就直接定位temp.no==编号就可以了,因为是前后都有指针指向,所以就不像单链表那样需要依靠上一节点了
     */
    public void update(DoubleLikNode likNode) {
        DoubleLikNode temp = head;
        boolean flag = true; // 标记找不到匹配的no
        while (true) {
            if (temp.next == null) {
                if (temp.no==likNode.no){
                temp.setNext(likNode.getNext());
                temp.setName(likNode.getName());
                flag = false;
                break;
            }

                break;
            }
            if (temp.no==likNode.no){
                temp.setNext(likNode.getNext());
                temp.setName(likNode.getName());
                flag = false;
                break;
            }

            // 遍历去找下一个节点
            temp = temp.next;
        }
        if (!flag) {
            System.out.println("说明我们修改了数据");
        } else {
            System.out.println("根据no编号没有找到需要修改的数据!!!");
        }
    }
    /**
     * 删除链表中的元素,
     * 双向链表:删除时候不需要依赖上一个节点了在,直接根据编号匹配,然后取消当前关系,重现建立关系
     * ===单向链表:步骤,删除元素,需要我们根据no匹配到相同的编号的结点,那么我们只需要,把当前节点的上一个节点的next指针指向当前的节点的下一个节点
     */
    public void delete(DoubleLikNode node){
        DoubleLikNode temp = head;
        boolean flag =true;
        while (true){
            if (temp.next==null){
                if (temp.no==node.no) {
                    //取消当前节点和前后指针关系,让前节点指向后节点,后节点指向前节点
                    temp.next.pre=temp.pre;
                    temp.pre.next=temp.next;
                    // 找到了此节点打个标记
                    flag=false;
                    break;
                }
                break;
            }
            // 找到当前节点
//            if (temp.next.no==node.no) {
//                temp.next=temp.next.next;
//                // 找到了此节点打个标记
//                flag=false;
//                break;
//            }
            if (temp.no==node.no) {
                //取消当前节点和前后指针关系,让前节点指向后节点,后节点指向前节点
                temp.next.pre=temp.pre;
                temp.pre.next=temp.next;
                // 找到了此节点打个标记
                flag=false;
                break;
            }

            temp=temp.next;
        }
        if(!flag){
            System.out.println("删除了此节点!!");
        }else{
            System.out.println("没有找到此编号的节点,无法删除操作");
        }
    }

    /**
     * 反转链表
     * 双向链表:由于每个节点存在前一个节点的引用,那么我们就可以用pre前节点来遍历
     * ===单向链表;步骤:创建另一个链表,然后把当前链表的元素从第一个取出,放到另一个链表中,然后取出第二个放到第一个之前,以此类推。
     */
    public void reverse(){
        DoubleLikNode temp = head;
        DoubleLikNode wei = null;
        while(true){
            // 我们先拿到最后一个节点元素
            if(temp.next==null){
                wei=temp; //把最后一个节点放入wei的变量中
                break;
            }
            temp= temp.next;
        }
        // 把尾指向的pre指针指向head
        // 根据pre遍历数据
        System.out.println(wei);
        while(true){
            if (wei.pre==null){
                break;
            }
            System.out.println(wei.pre);
            wei=wei.pre;
        }


        // 上面是注释,下面这个是第二遍
//        DoubleLikNode temp = head.next;
//        DoubleLikNode newHead = new DoubleLikNode();
//        DoubleLikNode next = null;
//        while (true){
//            if(temp==null){break;}
//            next=temp.next;
//            temp.next= newHead.next;
//            newHead.next=temp;
//            temp=next;
//        }
//        head.next=newHead.next;
    }

    /**
     * 通过栈进行数据的反转
     */
    public void reverseByStack(){
        DoubleLikNode temp = this.head;
        DoubleLikNode cur =null;
        Stack<DoubleLikNode> stack = new Stack();
        while (true){
            if (temp.next==null) {
                stack.push(temp);
                break;
            }
            //获取当前对象进行压栈/入栈操作
            stack.push(temp);
            temp=temp.next;
        }
        System.out.println("开始出栈!!!");
        // stack.isEmpty(); 出栈
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
        System.out.println();
    }
}
    class DoubleLikNode {
        public int no; //行号
        public String name; //名称
        public DoubleLikNode next; //指向下一节点对象的指针
        // 相比较与单项链表,多了一个指向前一节点对象的指针
        public DoubleLikNode pre;

        public DoubleLikNode() {
        }

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

        @Override
        public String toString() {
            return "DoubleLikNode{" +
                    "no=" + no +
                    ", name='" + name + '\'' +
//                    ", next=" + next +
                    ", pre=" + pre +
                    '}';
        }

        public int getNo() {
            return no;
        }

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

        public String getName() {
            return name;
        }

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

        public DoubleLikNode getNext() {
            return next;
        }

        public void setNext(final DoubleLikNode next) {
            this.next = next;
        }

        public DoubleLikNode getPre() {
            return pre;
        }

        public void setPre(final DoubleLikNode pre) {
            this.pre = pre;
        }
    }

6:数组实现栈结构

 数组实现栈结构,上一个写的是链表反转也可以实现栈结构,先进后出
*1、创建栈类
*      * 2、属性:// 定义数组 // 栈长度// 栈顶元素位置
*      * 3、方法:push() , pop()

 

/**
 * 数组实现栈结构,上一个写的是链表反转也可以实现栈结构,先进后出
 *1、创建栈类
 *      * 2、属性:// 定义数组 // 栈长度// 栈顶元素位置
 *      * 3、方法:push() , pop()
 * @author mgq
 * @create 2021-03-08 1:38
 */
public class ArrayStackDemo {

    public static void main(String[] args) {
        System.out.println("测试栈!!!");
        ArrayStack arrayStack = new ArrayStack(5); //设置栈最大深度
        while (true) {

            System.out.println("a:往栈中插入数据");
            System.out.println("b:从栈中取数据");
            System.out.println("c:查看栈中数据");
            System.out.println("d:退出测试");
            Scanner scanner = new Scanner(System.in);
            String i = scanner.next();
            switch (i){
                case "a" :
                    System.out.println("请输入已添加的值");
                    int a = scanner.nextInt();
                    arrayStack.push(a);
                    System.out.println("添加成功");
                    break; //可选
                case "b" :
                    int pop = arrayStack.pop();
                    System.out.println("取出的值是:"+pop);
                    break;
                case "c" :
                    arrayStack.list();
                    break;
                case "d" :
                    return;
                default:
                    break;
            }

        }

    }

    /**
     * 1、创建栈类
     * 2、属性:
     * 3、方法:
     */
    static class ArrayStack{
        // 定义数组
        private int [] array;
        // 栈长度
        private int maxLength=0;
        // 栈顶元素位置
        private int top=-1;
        // 设置栈的最大深度
        public ArrayStack(int length){
            array=new int[length];
            maxLength=length;
        }

        // 往栈中添加元素,压栈 、 入栈
        public void push(int value){
            top++;
            array[top]=value;
        }
        // 从栈顶取出元素,也就是出栈
        public int pop(){
//            array[top];
            int value = array[top];
            array[top]=0;
            top--;
            return value;
        }

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

            }
        }

        public int[] getArray() {
            return array;
        }

        public void setArray(final int[] array) {
            this.array = array;
        }

        public int getMaxLength() {
            return maxLength;
        }

        public void setMaxLength(final int maxLength) {
            this.maxLength = maxLength;
        }

        public int getTop() {
            return top;
        }

        public void setTop(final int top) {
            this.top = top;
        }
    }
}

7:哈希表  HashTable实现(数组+链表)

* HashTable 哈希表实现:用数组+链表实现哈希表
* 读取速度快,占用内存小
* 数组= SingleLinketListDemo[size] ,其中 数组中根据对象的id/size取模,决定对象存在哪个链表中
* 链表= SingleLinketListDemo
* SingleLinketListDemo的链表是无head,那么就把第一个传进的对象作为head头,如果有,就用原来head

 

/**
 * HashTable 哈希表实现:用数组+链表实现哈希表
 * 读取速度快,占用内存小
 * 数组= SingleLinketListDemo[size] ,其中 数组中根据对象的id/size取模,决定对象存在哪个链表中
 * 链表= SingleLinketListDemo
 * SingleLinketListDemo的链表是无head,那么就把第一个传进的对象作为head头,如果有,就用原来head
 * @author mgq
 * @create 2021-03-11 14:15
 */
public class HashTableDemo {

    public static void main(String[] args) {
        HashTable hashTable = new HashTable(10);
        Student stu = new Student();
        stu.setId(1);
        stu.setName("张三");
        Student stu1 = new Student();
        stu1.setId(11);
        stu1.setName("李四");
        Student stu2 = new Student();
        stu2.setId(2);
        stu2.setName("王五");

        hashTable.add(stu);
        hashTable.add(stu1);
        hashTable.add(stu2);
        hashTable.list(stu.id);
        hashTable.listAll(stu.id);
        hashTable.list(stu2.id);
        hashTable.listAll(stu2.id);
        /**
         * 链表中节点为:Student{id=1, name='张三'}
         * 链表中节点为:Student{id=1, name='张三'}
         * 链表中节点为:Student{id=11, name='李四'}
         * 链表中节点为:Student{id=2, name='王五'}
         * 链表中节点为:Student{id=2, name='王五'}
         */
    }

    /**
     * 实现哈希表思路:
     * 结构: 由数组+链表组成
     * 数组:数组中存在的是链表的数组,里面是链表对象
     * 链表:存储对象,注意:其中要存储的对象,是根据数组中取模后的id值,来规定是否存储在哪个数组对应的链表上
     * 链表中,如果是有head节点,那么数组存的就是所有的head头,
     *      * 如果没有head头节点的,那么存储的就是第一个存进来的对象。
     * 注意: 数组中每个值都对应一个链表
     */
//---------------------------分割线---------------

    /**
     * 首先定义学生一个对象:单向链表的(next属性),也可以定义是双向链表的(next属性,pre属性)
     */
    static class Student{
        private int id;
        private String name;
        public Student next; // 指向下一个节点的引用指针,这个是单向链表

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

        public Student getNext() {
            return next;
        }

        public void setNext(final Student next) {
            this.next = next;
        }

        public String getName() {
            return name;
        }

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

        public int getId() {
            return id;
        }

        public void setId(final int id) {
            this.id = id;
        }
    }
// ------------------------------------分割线----------------------

    /**
     * 定义无head单向链表
     */
    static class SingleLinketListDemo{
        // 标注:因为当head是空,那么这就是无head链表,如果head=new Student ,这就是有head链表,这次我用无head链表实现
        // 直接让上来的第一个节点对象,赋值给head,这样就没有head了
        private Student head=null;

        /**
         * 链表的添加方法
         * @param stu
         */
        public void add(Student stu){
            if (head==null) {
                // 如果head等于空,直接把stu赋值给head
                head=stu;
                return;
            }
            // 如果head不等于null,说明该链表已经节点,那么循环判断子节点,直接获取到最后节点即可
            Student temp =head;
            while(true){
                if (temp.next==null) {
                    // 说明到了最后的节点,直接把stu赋值给temp.next
                    // 标注 ,如果需要根据id,进行插入的数据有序,就看我专门去写的链表demo中有了
                    temp.next=stu;
                    return;
                }
                // 节点后移
                temp=temp.next;
            }
        }

        /**
         * 循环遍历节点,放到head上
         */
        public void list(int id){
            Student temp=head;
            if(head==null){
                System.out.println("链表为空");
                return;
            }
//            System.out.println("链表中节点为:"+temp.toString());
            while (true) {
                if(temp.getId()!=id){
                    break;
                }
                if (temp.next==null) {
                    //遍历到了最后一个节点
                    System.out.println("链表中节点为:"+temp.toString());
                    break;
                }
                System.out.println("链表中节点为:"+temp.toString());
                temp=temp.next;
            }
        }

        /**
         * 循环遍历节点,放到head上
         */
        public void listAll(){
            Student temp=head;
            if(head==null){
                System.out.println("链表为空");
                return;
            }
//            System.out.println("链表中节点为:"+temp.toString());
            while (true) {
                if (temp.next==null) {
                    //遍历到了最后一个节点
                    System.out.println("链表中节点为:"+temp.toString());
                    break;
                }
                System.out.println("链表中节点为:"+temp.toString());
                temp=temp.next;
            }
        }
}
//---------------------------------分割线---------------------

    /**
     * 创建 哈希表对象
     * --属性 ;只有数组
     */
    static class HashTable{
        private SingleLinketListDemo [] linketList;
        private int length;
        /**
         * 有参构造,初始化SingleLinketListDemo[]数组
         * @param size
         */
        public HashTable(int size){
            linketList=new SingleLinketListDemo[size];
            length =size;
            // int [] a =[5],a中的值会默认是0,但是如果是对象类型的 数组,会默认为null,
            // 所以需要遍历,把linketList 中的值先初始化为一个new SingleLinketListDemo()单向链表对象;
            for (int i = 0; i < linketList.length; i++) {
                linketList[i]=new SingleLinketListDemo();
            }

        }

        /**
         * 哈希表的添加
         */
        public void add(Student stu){
            // 调用id取模方法,判断此对象应该放入数组哪个下标下的链表
            int i = hashFun(stu.getId());
            // 根据取模后的id,放入对应的下标的数组中,然后调取add方法
            linketList[i].add(stu);
        }

        /**
         * 根据id查询数据
         */
        public void list(int id){
            int i = hashFun(id);
            linketList[i].list(id);
        }

        /**
         * 根据id查询数据
         */
        public void listAll(int id){
            int i = hashFun(id);
            linketList[i].listAll();
        }
        /**
         * 1、对id取模,相同取模后的值对应的对象,就放到同一个链表内
         * 并且,当多个id数据添加后,只要取模值一样说明在数组中的下标一样,就放入同一链表
         */
        public int hashFun(int id){
            // 对id取模
            return id%length;
        }

        public SingleLinketListDemo[] getLinketList() {
            return linketList;
        }

        public void setLinketList(final SingleLinketListDemo[] linketList) {
            this.linketList = linketList;
        }

        public int getSize() {
            return length;
        }

        public void setSize(final int size) {
            this.length = size;
        }
    }
}

8:二叉树数据结构

* 二叉树数据结构实现
*  * 属性:left,right左右节点
*  * 方法:前序 、中序、后序遍历二叉树,delNode 删除节点方法
/**
 * 二叉树数据结构实现
 *  * 属性:left,right左右节点
 *  * 方法:前序 、中序、后序遍历二叉树,delNode 删除节点方法
 * @author mgq
 * @create 2021-03-11 21:08
 */
public class BinaryTreeDemo {
//    public static void main(String[] args) {
//        // 定义二叉树
//        BinaryNode binaryNode1 = new BinaryNode(1,"二叉树第一个节点");
//        BinaryNode binaryNode2 = new BinaryNode(2,"二叉树左侧第二个节点");
//        binaryNode1.setLeft(binaryNode2);
//        BinaryNode binaryNode3 = new BinaryNode(3,"二叉树右侧三个节点");
//        binaryNode1.setRight(binaryNode3);
//        BinaryNode binaryNode4 = new BinaryNode(4,"二叉树左侧第四个节点");
//        binaryNode2.setLeft(binaryNode4);
//        BinaryNode binaryNode5 = new BinaryNode(5,"二叉树右侧第五个节点");
//        binaryNode2.setRight(binaryNode5);
//        System.out.println("前序遍历二叉树======================");
//
//        // 前序查询二叉树,看打印循序
//        binaryNode1.preSearch();
//        System.out.println("中序遍历二叉树======================");
//        // 中序遍历二叉树
//        binaryNode1.midSearch();
//        System.out.println("后序遍历二叉树======================");
//        // 后序遍历二叉树
//        binaryNode1.afterSearch();
//
//        // 删除节点方法
//        binaryNode1.delNode(2);
//        // 遍历
//        binaryNode1.preSearch();
//
//    }

    /**
     * 测试有序数组 用 二叉树来遍历,只适用于完全二叉树,子节点在同一层级
     * @param args
     */
    public static void main(String[] args) {
        int [] arr = {1,2,3,4,5,6,7,8,9};
        BinaryTree binaryTree = new BinaryTree(arr);
        binaryTree.preSearchSort(0);
    }
}

/**
 * 创建二叉树节点对象
 * 属性:left,right左右节点
 * 方法:前序 、中序、后序遍历二叉树
 */
class BinaryNode{
    private int no; //序号
    private String name; //名称
    private BinaryNode left; //左节点
    private BinaryNode right; //右节点

    /**
     * 前序查找
     * 步骤:
     * 1、先获取当前节点
     * 2、递归获取左侧子节点
     * 3、递归获取右侧子节点
     */
    public void preSearch(){
        // 打印当前节点
            System.out.println(this.toString());
            // 递归左侧子节点
//            preSearch(this.left);
        if (this.left!=null) {
            this.left.preSearch();
        }
            // 递归右侧子节点
//            preSearch(this.right);
        if (this.right!=null){
            this.right.preSearch();
        }
    }

    /**
     * 中序查找
     * 1,递归获取左侧子节点
     * 2、当前节点
     * 3、递归右侧子节点
     */
    public void midSearch(){
        // 递归左侧子节点
//            preSearch(this.left);
        if (this.left!=null) {
            this.left.preSearch();
        }
        // 打印当前节点
        System.out.println(this.toString());
        // 递归右侧子节点
//            preSearch(this.right);
        if (this.right!=null){
            this.right.preSearch();
        }
    }

    /**
     * 后序遍历
     * 1,递归获取左侧子节点
     * 2、递归右侧子节点
     * 3、当前节点
     */
    public void afterSearch(){
        // 递归左侧子节点
//            preSearch(this.left);
        if (this.left!=null) {
            this.left.preSearch();
        }
        // 递归右侧子节点
//            preSearch(this.right);
        if (this.right!=null){
            this.right.preSearch();
        }
        // 打印当前节点
        System.out.println(this.toString());
    }

    /**
     * 根据编号删除二叉树节点
     * @param no
     */
    public void delNode(int no){
        // 根据编号查找到二叉树父节点的子节点,因为而插入是像链表一样,只能通过父节点找到找到当前节点
        // 递归左节点查询是否编号是否符合,如果符合就给节点置为null即可
        if (this.left.no==no) {
            this.left=null;
            return;
        }
        // 递归右节点查询是否编号是否符合,如果符合就给节点置为null即可
        if (this.right.no==no) {
            this.right=null;
            return;
        }
        // 如果向左节点没有匹配就开始向左递归查询
        this.left.delNode(no);
        // 如果向右节点没有匹配就开始向右递归查询
        this.right.delNode(no);
    }

    public BinaryNode() {

    }
    public BinaryNode(final int no, final String name) {
        this.no = no;
        this.name = name;
    }
    public BinaryNode getLeft() {
        return left;
    }

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

    public BinaryNode getRight() {
        return right;
    }

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


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

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

/**
 * 只是用与完全二叉树
 * 用数组存储编号,用二叉树实现 前序 ,中序,后序遍历查询
 * 注意:==因为用数组存储了数据,只是用二叉树遍历,那么就不需要规定left和right节点了
 */
class BinaryTree{
    private int [] arr;

    /**
     * 前序查找方法,只是用与完全二叉树,也就是子节点都是同级节点
     * // 元素左子节点下角标=2*n+1
     * // 元素右子节点下角标 = 2*n+2
     * // 元素父节点下角标 = (n-1)/2
     * n表示当前节点的下角标,
     * 上面说的都是在数组中的下角标
     */
    public void preSearchSort(int n){
        System.out.println(arr[n]);
        // 向左递归判断首先2n+1一定要小于arr数组长度
        if ((2*n+1)<arr.length) {
            // 元素左子节点下角标=2*n+1
            preSearchSort(2*n+1);
        }
        // 向右递归判断首先2n+1一定要小于arr数组长度
        if ((2*n+2)<arr.length) {
            // 元素右子节点下角标 = 2*n+2
            preSearchSort(2*n+2);
        }


    }

    public BinaryTree(int[] arr) {
        this.arr = arr;
    }


    public int[] getArr() {
        return arr;
    }

    public void setArr(final int[] arr) {
        this.arr = arr;
    }

}

排序算法

1:冒泡排序算法

// 冒泡排序规则,首先比较前两个数,找出最大的数,让一个变量接收,然后调换两个位置,然后再一次类推比下去,最后确定出最后一位一定是最大的值,
// 然后下一次循环,不用在比较最后一位了,类似还是和上面一样比较。所以冒泡排序循环的次数是length-1次,并且时间复杂度是O(n^2) ,
// 也就是n的平方,因为是两个for嵌套循环:@:
/**
 * 冒泡排序算法
 * 时间复杂度是O(n^2)
 * @author mgq
 * @create 2021-03-09 14:39
 */
public class MaoPaoSort {
    public static void main(String[] args) {
            // 冒泡排序规则,首先比较前两个数,找出最大的数,让一个变量接收,然后调换两个位置,然后再一次类推比下去,最后确定出最后一位一定是最大的值,
            // 然后下一次循环,不用在比较最后一位了,类似还是和上面一样比较。所以冒泡排序循环的次数是length-1次,并且时间复杂度是O(n^2) ,
            // 也就是n的平方,因为是两个for嵌套循环
        // ===============冒泡排序算法实现步骤:
        // 定义乱序数组
        int [] arr = {2,1,4,3,8,7,6,5 };
        // 有多少元素,就循环length-1 次,因为最后就剩下一个最前面的元素,也不用比较了
        for (int j = 0; j < arr.length-1; j++) {
            // -----------------一次小循环
            // 实现两个元素对比,然后位置互换,这个试一次循环
            int temp = 0;
            for (int i = 0; i < arr.length-1-j; i++) {
                // 如果当前元素大于后面的元素,那么付给temp 变量,然后调换位置
                if (arr[i]>arr[i+1]) {
                    temp=arr[i]; // 暂时付给变量
                    arr[i]=arr[i+1]; //后面元素赋值给当前位置元素,也就是调换位置
                    arr[i+1]=temp;
                }
            }
            //---------------------------end
        }
        // 打印结果
        System.out.println(Arrays.toString(arr));

    }
}

 2:选择排序算法

// 选择排序:在一个数组中,先把两个元素相互比较,然后把小的那个值,赋值给temp变量,还有坐标给index变量赋值,然后用这个temp的值,
// 依次去和后面的比,如果有比他小的,就把小的值和索引值,付给temp和index变量,循环完然后,把temp和index 和 第一个元素位置互换,
// z这是一次循环,那么后面再次循环依次类推,再从第二个元素开始,再次找到最小的,和第二个元素互换位置,以此类推!

 

/**
 * 选择排序算法
 * 时间复杂度是O(n^2)
 * @author mgq
 * @create 2021-03-09 14:56
 */
public class XuanZeSort {
    public static void main(String[] args) {
        // 选择排序:在一个数组中,先把两个元素相互比较,然后把小的那个值,赋值给temp变量,还有坐标给index变量赋值,然后用这个temp的值,
        // 依次去和后面的比,如果有比他小的,就把小的值和索引值,付给temp和index变量,循环完然后,把temp和index 和 第一个元素位置互换,
        // z这是一次循环,那么后面再次循环依次类推,再从第二个元素开始,再次找到最小的,和第二个元素互换位置,以此类推!

        // ===============选择排序算法实现步骤:
        // 定义乱序数组
        int [] arr = {2,1,4,3,8,7,6,5 };

        // 外面循环只需要循环length-1次,因为最后一个值一定是最大的,就不用了在循环了
        for (int j = 0; j < arr.length-1; j++) {
            // ---------------------一次小循环-----
            int temp= arr[j]; //先取出第一个元素,然后和后面所有元素对比
            int index=j; // 第一个元素的索引是 0
            for (int i = j; i < arr.length; i++) {
                //如果当前元素小于temp变量中的元素,就把当前元素值和索引放入temp和index中
                if (arr[i]<temp) {
                    temp = arr[i];
                    index=i;
                }
            }
            // 把temp中的值给 第一个元素,把一个元素和 index位置互换,这样最小值就在第一个位置了
            arr[index]=arr[j];
            arr[j]=temp;
            //-------------------------------end------------
        }
        // 打印结果
        System.out.println(Arrays.toString(arr));

    }
}

3:插入排序算法

时间复杂度是O(n^2)
// 插入排序:插入排序是把一个数组,默认分成两段去看待,一个是有序数组,一个是无序数组,例如【【4,5】【7,8,9,4】】,但其实还是一个数组,
// 那么有序数组中默认数据中第一个就是有序数组的元素,那么剩下无序的,循环从无序数组中取一个,然后和有序中的数组比较,然后放到相应位置,
// 那么,当所有元素都到有序数组中的时候,就排序成功了

4:希尔排序==插入升级版

5:快速排序算法

* 快速排序是基于冒泡排序的一种改进,基本的实现步骤:
* 1:定义数组,然后取数组的一个中间的元素的值,以中间值把数组分为左侧和右侧,然后定义为左侧是小于中间值得,右边大于中间值,
* 然后递归 ,如果左边数据有大于中间值得,并且右边有小于中间值得话,那么我们就把两个数相互交换,依次类推下去。
* 2:时间复杂度O(nlogn) 属于线性对数阶
/**
 * 快速排序算法
 * java里/和%的区别
 * 1》 / 取得是两个数相除后的整数部分。
 * 2》 % 取得是两个数相除后的余数部分。
 * @author mgq
 * @create 2021-03-09 23:06
 */
public class KuaiSuSort {
    public static void main(String[] args) {
        int [] arr ={-9,78,0,23,-567,70};
//        int [] arr ={2,-9,6,3,5,4,-1};
//        int [] arr ={2,2,2};
        //[-1, 2, 4, 5, 6, 7, 9]
        //[9, 7, 6, 5, 4, 2, -1]
        kuaisu(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 快速排序是基于冒泡排序的一种改进,基本的实现步骤:
     * 1:定义数组,然后取数组的一个中间的元素的值,以中间值把数组分为左侧和右侧,然后定义为左侧是小于中间值得,右边大于中间值,
     * 然后递归 ,如果左边数据有大于中间值得,并且右边有小于中间值得话,那么我们就把两个数相互交换,依次类推下去。
     * 2:时间复杂度O(nlogn) 属于线性对数阶
     */
    /**
     *
     * @param arr 数组
     * @param left 左边开始坐标
     * @param right 右边开始坐标
     */
    public static void kuaisu(int [] arr , int left ,int right){
        int l =left; //左边开始索引值
        int r = right; // 右边开始索引值
        int middle = (left+right)/2; //求出r中间得位置索引
        int temp =0; //临时交换变量
        // l<r 那么就是一直没有循环到中间值,那么就一直循环下去r,什么时候等l==right,那么就停止
        while (l<r){
            // 如果左边的值一直小于中间值,那么就一直++索引,一直到中间值停止,或者有大于中间值得就停止,然后l==当前值得坐标
            while (arr[l]<arr[middle]){
                l++;
            }
            // 如果右边的值一直大于中间值,那么就一直--索引,一直到中间值停止,或者有小于中间值得就停止,然后r==当前值得坐标
            while (arr[r]>arr[middle]){
                r--;
            }
            // 左边都是大于中间值得这种情况,这种情况r--,会变成负数,这种情况在下面互换位置时候会出现 数组索引 异常
            if(l>=r){
                break;
            }

            // 交换位置,把左边数据大于中间值得数据,和 右边数据小于中间值得数据,进行数据的交换位置,通过一个temp临时变量进行转换
            temp = arr[l];
            arr[l]= arr[r];
            arr[r]=temp;

            // 这种情况是 当l<right,但是左边的arr[l]==arr[middle]和中间的值相等了,那么这种情况就是,在左边出现了和中间值相等情况。。
            // 为了避免死循环,那么我们就直接往前--,不理会这种数据
            // == 这个意思是当左边l 或者 右边r 挪到中间了,那么剩下的就让另一边自己挪动
            if (arr[l]==arr[middle]) {
//                l++;
                // 这块注意一定不要写反了,这块出的问题找了半天
                r--;
            }
            // 那么同理,右边也是
            if (arr[r]==arr[middle]) {
//                r--;
                // 这块注意一定不要写反了,这块出的问题找了半天
                l++;
            }
        }
        // 否则会出现栈溢出
        if (l==r) {
            l++;
            r--;
        }

        // 必须要放在for循环外边,因为上面操作是把左边所有元素和右边所有元素 和中间值对比后相互互换的过程,所以放在外面
        // 然后递归左边,因为左边[2, -9, -1, 3, 5, 4, 6]中的2, -9, -1,只能保证比3小,并不能保证顺序,所以,要递归上面的操作最后保证左边顺寻
        // 传入三个参数,第一个还是整个数组,但是由于第二遍是做百年2, -9, -1,那么我们只需要限制索引范围即可
        // left是左边界
//        if(left<l){
//            kuaisu(arr,left,l);
//        }
        // 这块注意一定不要写反了,这块出的问题找了半天
        // r是--
        if(left<r){
            kuaisu(arr,left,r);
        }
        // 递归右边,因为左边[2, -9, -1, 3, 5, 4, 6]中的 5, 4, 6,只能保证比3大,并不能保证顺序,所以,要递归上面的操作最后保证右边顺寻
        // 传入三个参数,第一个还是整个数组,但是由于第二遍是做百年5, 4, 6,那么我们只需要限制索引范围即可
        // right是右边界
//        if(right>r){
//            kuaisu(arr,r,right);
//        }
        // 这块注意一定不要写反了,这块出的问题找了半天
        // 因为l是++
        if(right>l){
            kuaisu(arr,l,right);
        }
    }
}

6:归并排序

时间复杂度O(nlogn)
* 归并排序,是经典的 先 分 后 合的算法,其中先把数组中元素,递归分成一个,然后再比较大小后,再合并,然后再比较,然后再合并,再比较,以此类推
/**
 * 归并排序
 * 时间复杂度O(nlogn)
 * @author mgq
 * @create 2021-03-10 1:57
 */
public class GuiBingSort {

    public static void main(String[] args) {

        int [] arr ={2,-9,6,3,5,4,-1};
        int [] temp=new int[arr.length];
        fenlie(arr,0,arr.length-1,temp);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 归并排序,是经典的 先 分 后 合的算法,其中先把数组中元素,递归分成一个,然后再比较大小后,再合并,然后再比较,然后再合并,再比较,以此类推
     */
    /**
     * 归并步骤之一:分裂过程
     */
    public static void fenlie(int [] arr ,int left,int right,int [] temp){
        if (left<right){
            // 分裂过程
            int mid = (left+right)/2; //中间索引
            // 递归调用本方法,分裂左边,向左递归
            fenlie(arr,left,mid,temp);
            // 递归调用本方法,分裂右边,向右递归
            fenlie(arr,mid+1,right,temp);
            //合并过程
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 归并步骤之二: 合并过程
     * @param arr 原数组
     * @param left 左边的起索引
     * @param mid  中间索引
     * @param right  右边索引
     * @param temp   临时数组,用来接收排序后的数组,然后再复制给arr
     */
    public static void merge(int [] arr ,int left ,int mid , int right,int [] temp ){
        //数组[1,2,3,4,5,6],其中123 属于左边,456属于右边 ,然后把左边和右边元素一个个对比,元素小的放到temp中
        // temp []
        int l=left; //左边元素起位置
        int r=mid+1; //右边元素起位置
        int t =0; //temp 数组从0开始

        // 1: 限制l小于mid中间索引 ,r<右边界索引
        while (l<=mid && r<=right){
            // 如果
            if (arr[l]<=arr[r]) {
                // 把对比小的元素 赋值 到 temp中,然后t++,l++,继续下一个元素对比
                temp[t]=arr[l];
                t++;
                l++;
            }else{
                // 反之,则把arr[r]放到temp中,然后 ++
                temp[t]=arr[r];
                t++;
                r++;
            }
        }

        // 2: 如果左边的没有放完到temp中,但是右边已经没有元素可以对比了,那么把左边剩余的都放到temp中
        while (l<=mid){
            temp[t]=arr[l];
            t++;
            l++;
        }

        // 3: 如果右边的没有放完到temp中,但是左边已经没有元素可以对比了,把右边剩余的都放到remp中
        while (r<=right){
            temp[t]=arr[r];
            t++;
            r++;
        }

        // 4:把临时数组temp放入到arrz中
        t=0;
        int tempIndex = left;
        // tempIndex 0,right 1;  tempIndex 2,right 3;tempIndex 0,right 3,
        while (tempIndex<=right){
            arr[tempIndex] = temp[t];
            tempIndex++;
            t++;
        }

    }
}

7:基数排序(基于桶排序实现)

基数排序==
* 时间复杂度O(n*k) 线性阶,
* 特点:
* 速度非常快,
* 缺点:典型的空间换时间,因为需定义一个二维数组来表示组桶,也就是数组,很占用内存,并且很难处理排序数组中出现负数的情况,一般有负数就不用了桶排序
* 基数排序是根据桶排序实现的算法:
* --实现步骤: 假如有一组数组arr=【1,11,123,22,55】,那么数组中最大位数是123三位,所有,我们定义三组桶,每组桶都有十个桶,
* 第一组桶,0-9循序桶号,然后根据每个元素的个位是什么就放到哪个桶去,然后取出再放到arr数组中,此时数组中为arr=【1,11,22,123,55】,这个是根据个位数排序了
* 第二组桶,0-9循序通号,然后根据每个元素的十位去放入桶中,然后放入后,在全部取出来放到arr中,此时arr【1,11,22,123,55】,这是根据十位排序
* 第三组桶,0-9通号,然后根据百位去放入桶中,再取出来,arr[1,11,22,55,123] ,那么就全部排序成功
* 这种时间复杂度是对数阶的,速度很快,单是由于每个桶是一个数组,会很占用内存,典型的空间换时间
/**
 * 基数排序==
 * 时间复杂度O(n*k) 线性阶,
 * 特点:
 * 速度非常快,
 * 缺点:典型的空间换时间,因为需定义一个二维数组来表示组桶,也就是数组,很占用内存,并且很难处理排序数组中出现负数的情况,一般有负数就不用了桶排序
 * @author mgq
 * @create 2021-03-10 3:13
 */
public class JiShuSort {

    public static void main(String[] args) {
        // 要排序的数组
        int [] arr={1,11,123,22,55};
        bucketSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 基数排序是根据桶排序实现的算法:
     * --实现步骤: 假如有一组数组arr=【1,11,123,22,55】,那么数组中最大位数是123三位,所有,我们定义三组桶,每组桶都有十个桶,
     * 第一组桶,0-9循序桶号,然后根据每个元素的个位是什么就放到哪个桶去,然后取出再放到arr数组中,此时数组中为arr=【1,11,22,123,55】,这个是根据个位数排序了
     * 第二组桶,0-9循序通号,然后根据每个元素的十位去放入桶中,然后放入后,在全部取出来放到arr中,此时arr【1,11,22,123,55】,这是根据十位排序
     * 第三组桶,0-9通号,然后根据百位去放入桶中,再取出来,arr[1,11,22,55,123] ,那么就全部排序成功
     * 这种时间复杂度是对数阶的,速度很快,单是由于每个桶是一个数组,会很占用内存,典型的空间换时间
     */
    public static void bucketSort(int [] arr){

        /**
         * 0  0  0  0  0  0  0  0  0  0
         * 1  0  0  0  0  0  0  0  0  0
         * 2  0  0  0  0  0  0  0  0  0
         * 3  0  0  0  0  0  0  0  0  0
         * 4  0  0  0  0  0  0  0  0  0
         * 5  0  0  0  0  0  0  0  0  0
         * 6  0  0  0  0  0  0  0  0  0
         * 7  0  0  0  0  0  0  0  0  0
         * 8  0  0  0  0  0  0  0  0  0
         * 9  0  0  0  0  0  0  0  0  0
         */
        // 定义一个二维数组,用来管理十个桶,Y表示桶序号 0-9 序号,X表示一个桶,桶的深度为元素的长度,
        // 0-9如上所示 ,每一个横行表示一个桶
        int y =10; // Y表示桶序号 0-9 序号
        int x =arr.length; // X表示一个桶,桶的深度为元素的长度
        int [][] bucketNumber = new int[y][x];

        int indx = arr[0];
        // 求出arr中,位数最大的值
        for (int i = 0; i < arr.length; i++) {
           if( indx<arr[i] ){
               indx=arr[i]; // 把位数大的数字放到临时变量ind中
           }
        }
        // 计算出最大位数,然后就循环定义几组桶,每组十个桶
        int weishuLength = (indx + "").length();
        System.out.println("最大位数::"+weishuLength);

        // 根据 位数 循环 个位数,十位数 ,百位数 组桶
        for (int m = 0,n=1; m < weishuLength; m++,n*=10) {
            //=============================================分割线,记录第一组根据个位数十个桶的方法==================================
            // 定义一个桶,用来统计临时存放每个桶中元素的个数的,因为这样才下一个元素要存放的话,才能找到存放在二维数组中桶内元素要存放的位置
            // 这表示0-9 十个桶,0-9索引 临时统计 0-9桶中有多少个元素,每插入一个元素,就往相应桶号的位置,记录++
            int [] temp  = new int[10];
            // 拿出arr所有元素的个位数,然后依次按照个位数找到对应的桶,放入里面
            for (int i = 0; i < arr.length; i++) {
//                int number = arr[i] % 10; // 对元素取余过后就是 ==个位数,也就是==桶序号
                int number = arr[i] / n % 10; // 这块根据位数循环,动态去取个位还是十位后者百位的那组桶序号
                // 找到序号后,把元素放入到对应序号的桶中
                bucketNumber[number][temp[number]]=arr[i];
                // 记录本次number序号桶中元素 ++ ,那么下次在存放,在二维数组中的位置就是这个元素的下一个位置了
                temp[number]++;
            }
            // 所有的元素都放入桶中后,从桶中在取出元素,重新放入到arr数组中
            // 循环二维数组,依次从桶中取出数据,放入arr数组中
            int tempIndex =0;
            for (int i = 0; i < bucketNumber.length; i++) {
                for (int j = 0; j < bucketNumber[i].length; j++) {
                    // 判断如果桶中有数据,就存放,不判断的话,就出现数组越界,会把所有二维数组元素都给arr[]
                    if (bucketNumber[i][j]!=0){
                        // 从0号桶开始取元素,直到第9号桶,然后依次放入arr[]数组中
                        arr[tempIndex] = bucketNumber[i][j];
                        // arr中每放入一个元素,就++,放下一个
                        tempIndex++;

                        // 注意:每次大循环后,到了下一组桶前,需要先把二维数组中的数都清干净,以便于下一组桶存数据
                        bucketNumber[i][j]=0;
                    }

                }
            }
//            System.out.println(Arrays.toString(arr));
            //=============================================分割线,记录第一组根据个位数十个桶的方法==================================


        }
    }
}

8:堆排序(基于二叉树实现)

* 堆排序--基于完全二叉树实现
* 满二叉树:除了叶子结点,每个节点的都有两个子节点,是满二叉树
* 完全二叉树:在满二叉树基础上,其叶子结点是从左到右分布的是完全二叉树
* 时间复杂度是 线性常数阶 O(nlogn),没有桶排序快,但是相对于桶排序省内存

 

/**
 * 堆排序--基于完全二叉树实现
 * 满二叉树:除了叶子结点,每个节点的都有两个子节点,是满二叉树
 * 完全二叉树:在满二叉树基础上,其叶子结点是从左到右分布的是完全二叉树
 * 时间复杂度是 线性常数阶 O(nlogn),没有桶排序快,但是相对于桶排序省内存
 *
 * @author mgq
 * @create 2021-03-12 11:10
 */
public class DumpSort {
    public static void main(String[] args) {
        int [] arr={2,5,9,4,8,3,1};
//        int [] arr={4,6,8,5,9};
        froTree(arr,arr.length);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 循环完全二叉树,把所有的子树都像dumpTree方法一样,对比左右节点,然后大的一个子节点和父节点进行比较,大于父节点就进行交换
     */
    public static void froTree(int arr[],int length){
        // 获取最后一个非叶子节点
//        dumpTree(arr,1,arr.length);
        // 获取第一个非叶子节点
//        dumpTree(arr,0,arr.length);
        /**
         * 公式:arr.length/2-1 能够获取最后一个非叶子节点,那么只需要i--,就可以依次从最后一个非叶子节点开始到最后顶节点,
         * 然后循环对他们进行dumpTree()方法 ,也就是子节点比较,然后和父节点交换
         */
        for (int j=length/2-1 ; j>=0;j--) {
            dumpTree(arr,j,length);
        }


        // 临时变量接收数组长度,通过最顶的节点值和最低端的叶子结点互换,然后length--,这样数组的最后一位就是最大值,以此类推,直到排序成功
        int tempLength = arr.length;
        int tempMax =0;
        for (int i = tempLength-1; i >0 ; i--) {

            // 将顶端节点和叶子节点交换;
            tempMax=arr[i];
            // 将最小值赋值到定点
            arr[i] =arr[0];
            // 将最大值放到tempLength ,tempLength--,依次放入最大值
            arr[0]=tempMax;
//            System.out.println(Arrays.toString(arr));

            // 将顶端节点和叶子节点交换,也就是将数组的最前面和最后面的交换
            dumpTree(arr,0,i);

        }
    }


    /**
     * 找到二叉树的一个子树,先让左右子节点对比,其中大的一个子节点和当前父节点在比较,如果比父节点大,那么就交换,以此类推
     * @param arr 原数组
     * @param i 数组下标(先从最后一个节点开始遍历i= arr.length/2-1)
     * @param length 数组长度,每次进行依次交换,就讲树定元素和树最后节点交换,那么对应的数组中最后的值就是最大的,然后以此类推
     */
    public static void dumpTree(int arr[], int i, int length){
        int temp = arr[i]; //临时变量先接收目前栈顶数据,用来后面和子节点交换使用
        //注意 :这步很重要 :遍历当前树的左子节点,并且当左子节点还有子节点时候,就继续遍历
        for (int k = 2*i+1; k <length ; k=2*k+1) {
            // 2*n+1 获取当前节点的左侧子节点下角标,2*n+2 右侧子节点下角标
            if ((k<length)) {
                // 对比左侧和右侧值,哪侧值大就和当前节点交换
                // k+1<length 判断父节点下还有没有右节点
                if(k+1<length && arr[k]<arr[k+1]){
                    // 如果右子节点比父节点大的话,那么就把右子节点和父节点交换
                    if (arr[k+1]>temp) {
                        // 右侧比左侧大,把右侧赋值到当前节点,然后交换
                        arr[i]=arr[k+1];
//                        arr[k+1]=temp;
                        // 注意:这步很重要,这步保证了子节点转为父节点再去循环去上面同样操作,
                        i=k+1;
                        // 再把当前右子节点作为父节点给temp变量
//                        temp=arr[k+1];
                    }
                }else{
                    // 如果右子节点比父节点大的话,那么就把右子节点和父节点交换
                    if (arr[k]>temp) {
                        // 左侧比右侧大,把左侧和当前节点交换
                        arr[i]=arr[k];
//                        arr[k]=temp;
                        // 注意:这步很重要,这步保证了子节点的子节点找到自己的父节点
                        i=k;
                        // 再把当前左子节点作为父节点给temp变量
//                        temp=arr[k];
                    }
                }
            }else{
                break;
            }
            arr[i]=temp;
            /*// 2*n+1 获取当前节点的左侧子节点下角标,2*n+2 右侧子节点下角标
            if ((2*i+2<length)) {
                // 对比左侧和右侧值,哪侧值大就和当前节点交换
                if(arr[2*i+1]<arr[2*i+2]){
                    // 如果右子节点比父节点大的话,那么就把右子节点和父节点交换
                    if (arr[2*i+2]>temp) {
                        // 右侧比左侧大,把右侧赋值到当前节点,然后交换
                        arr[i]=arr[2*i+2];
                        arr[2*i+2]=temp;
                    }
                }else{
                    // 如果右子节点比父节点大的话,那么就把右子节点和父节点交换
                    if (arr[2*i+1]>temp) {
                        // 左侧比右侧大,把左侧和当前节点交换
                        arr[i]=arr[2*i+1];
                        arr[2*i+1]=temp;
                    }
                }
            }*/
        }


    }

}

查找算法

1:线性查找

 线性查找(顺序查找),
* 数组可以是有序的也可以是无序的
/**
 * 线性查找(顺序查找),
 * 数组可以是有序的也可以是无序的
 * @author mgq
 * @create 2021-03-11 11:26
 */
public class XunXuSearch {
    public static void main(String[] args) {
        int [] arr={1,5,4,7,8,3,2};
        int value =5; //需要找到的值
        for (int i = 0; i < arr.length; i++) {
            if (arr[i]==value) {
                System.out.println("找到了,下角标为:index:"+i);
            }
        }
    }
}

2:二分查找算法(二分递归算法)(二分非递归查找算法)

* 二分查找算法(二分递归算法)(二分非递归查找算法)
* 需要查找的数组是有序的前提下,
* 并且是根据数组的中间下标去查找的,先递归左边,在递归右边
* // 二分查找 和 插值查找 和 黄金分割查找 都是对 mid中间值优化的查找算法,其实现原理差不多
* // 二分查找 确定中间值 mid= (right+left)/2
// 二分查找 和 插值查找 和 黄金分割点查找 都是对 mid中间值优化的查找算法,其实现原理差不多
/**
 * 步骤:
 * 1:先确定中间值mid
 * 2:因为arr[]是有序数组,那么当要查找的值如果大于中间值,就往右递归查找
 * 3:因为arr[]是有序数组,那么当要查找的值如果小于中间值,就向左递归查找
 * 4:最后返回mid,就是最后匹配值后的下标
* 二分查找非递归方法
* 根据mid, while循环向左和向右查找,直到reruen mid 返回下角标
* @param arr 数组
* @param findValue 需要查找的数值
/**
 * 二分查找算法(二分递归算法)(二分非递归查找算法)
 * 需要查找的数组是有序的前提下,
 * 并且是根据数组的中间下标去查找的,先递归左边,在递归右边
 * // 二分查找 和 插值查找 和 黄金分割查找 都是对 mid中间值优化的查找算法,其实现原理差不多
 * // 二分查找 确定中间值 mid= (right+left)/2
 * @author mgq
 * @create 2021-03-11 11:30
 */
public class ErFenSearch {
    public static void main(String[] args) {
        // 二分递归
//        int [] arr ={1,3,4,6,9,11,14,18};
//        int findValue=18;
//        int i = erFen(arr, 0, arr.length - 1, findValue);
//        System.out.println(i);
        // 二分不递归
        int [] arr ={1,3,4,6,9,11,14,18};
        int findValue=18;
        int i = erFenNoDiGui(arr, findValue);
        System.out.println(i);
    }

    /**
     * 二分递归查找方法
     * 根据mid,然后分别向左和向右递归,return mid,就是返回了下角标值
     * @param arr 原数组
     * @param left 左角标
     * @param right 右角标
     * @param findValue 要查找的值
     */
    public static int erFen(int [] arr ,int left ,int right,int findValue){
        System.out.println("二分算法查找了多少遍");
        // 二分查找 和 插值查找 和 黄金分割点查找 都是对 mid中间值优化的查找算法,其实现原理差不多
        /**
         * 步骤:
         * 1:先确定中间值mid
         * 2:因为arr[]是有序数组,那么当要查找的值如果大于中间值,就往右递归查找
         * 3:因为arr[]是有序数组,那么当要查找的值如果小于中间值,就向左递归查找
         * 4:最后返回mid,就是最后匹配值后的下标
         */
        // 1:确定中间值
        int mid = (right+left)/2; // 结果取整
        if(findValue>arr[mid]){ //因为arr[]是有序数组,那么当要查找的值如果大于中间值,就往右递归查找
            return erFen(arr,mid+1,right,findValue);
        }else if(findValue<arr[mid]){
            //因为arr[]是有序数组,那么当要查找的值如果小于中间值,就向左递归查找
            return erFen(arr,left,mid-1,findValue);
        }else{
            // 最后返回mid,就是最后匹配值后的下标
            return mid;
        }
    }

    /**
     * 二分查找非递归方法
     * 根据mid, while循环向左和向右查找,直到reruen mid 返回下角标
     * @param arr 数组
     * @param findValue 需要查找的数值
     * @return
     */
    public static int erFenNoDiGui(int arr [] ,int findValue){
        int left=0;
        int right=arr.length-1;
        while (left<=right){
                int mid = (left+right)/2;
                // 如果不等于mid
                if (arr[mid]==findValue) {
                    return mid;
                }
                // 首先数组是有序的,那么就可以根据值去判断是向左循环还是向右循环
                if(arr[mid]>findValue){
                    // 向右旋转
                    right=mid-1;
                }else {
                    //向左旋转
                    left=mid+1;
                }
        }
        return -1;

    }
}

3:插值查找算法:

* 需要查找的数组是有序的前提下
* 并且是根据数组的中间下标去查找的,先递归左边,在递归右边
*  // 二分查找 和 插值查找 和 黄金分割查找 都是对 mid中间值优化的查找算法,其实现原理差不多
*  插值算法中间值规则 公式:
*  // int mid= left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left])

 

/**
 * 插值算法:
 * 需要查找的数组是有序的前提下
 * 并且是根据数组的中间下标去查找的,先递归左边,在递归右边
 *  // 二分查找 和 插值查找 和 黄金分割查找 都是对 mid中间值优化的查找算法,其实现原理差不多
 *  插值算法中间值规则:
 *  // int mid= left+(right-left)*(findVal-arr[left])/(arr[right]-arr[left])
 * @author mgq
 * @create 2021-03-11 11:56
 */
public class ChaZhiSearch {
    public static void main(String[] args) {
        int [] arr ={1,3,4,6,9,11,14,18};
        int findValue=18;
        int i = chaRu(arr, 0, arr.length - 1, findValue);
        System.out.println(i);
    }

    /**
     * 二分查找方法
     * @param arr 原数组
     * @param left 左角标
     * @param right 右角标
     * @param findValue 要查找的值
     */
    public static int chaRu(int [] arr ,int left ,int right,int findValue){
        System.out.println("插值算法查找了多少遍");
        // 二分查找 和 插值查找 和 黄金分割点查找 都是对 mid中间值优化的查找算法,其实现原理差不多
        /**
         * 步骤:
         * 1:先确定中间值mid
         * 2:因为arr[]是有序数组,那么当要查找的值如果大于中间值,就往右递归查找
         * 3:因为arr[]是有序数组,那么当要查找的值如果小于中间值,就向左递归查找
         * 4:最后返回mid,就是最后匹配值后的下标
         */
        // 1:确定中间值
//        int mid = (right+left)/2; // 结果取整
         int mid= left+(right-left)*(findValue-arr[left])/(arr[right]-arr[left]);
        if(findValue>arr[mid]){ //因为arr[]是有序数组,那么当要查找的值如果大于中间值,就往右递归查找
            return chaRu(arr,mid+1,right,findValue);
        }else if(findValue<arr[mid]){
            //因为arr[]是有序数组,那么当要查找的值如果小于中间值,就向左递归查找
            return chaRu(arr,left,mid-1,findValue);
        }else{
            // 最后返回mid,就是最后匹配值后的下标
            return mid;
        }
    }
}

4:黄金分割查找算法

* 黄金分割查找算法
* 1:定义黄金分割数列
* 2:根据公式 mid=low+F(K-1)-1,计算出mid中间值,、
* 3:在循环根据mid向左边和向右边循环找到符合查找的值得下角标

5:递归查找算法

* 1、递归调用就是一个循环调用本方法,每次调用都把本身方法进行压栈操作,然后再依次根据return返回结果做计算出栈方法,并把计算的结果交给下一个栈顶方法
* 2、递归实现迷宫求出路线,和最短路线
* 3、递归 实现: 路径的 '上北下南'字符串 的 排列组合

 

/**
 * 递归算法,
 * 1、递归调用就是一个循环调用本方法,每次调用都把本身方法进行压栈操作,然后再依次根据return返回结果做计算出栈方法,并把计算的结果交给下一个栈顶方法
 * 2、递归实现迷宫求出路线,和最短路线
 * 3、递归 实现: 路径的 '上北下南'字符串 的 排列组合
 * @author mgq
 * @create 2021-03-08 10:59
 */
public class RecursiveDemo {
    // 统计步数
    static int count =0;
    // 统计最短步数
    static int ruleCount =0;
    // 统计最短距离
    static int min =0;
    static List<String> ruleslist = new ArrayList();
    public static void main(String[] args) {
//        System.out.println(print(4));
//        System.out.println(print2(4));
        mazeDemo();
    }

    /**
     * 简单测试递归调用方法
     * @param n
     * @return
     */
    public static int print(int n){
        if (n==1){
            return 1;
        }else{
            System.out.println(n);
            return print(n-1)*n;
            // 对于结果分析,假如print(4),n参数传的是4,那么根据递归规则,层层调用,最终到n==1,然后return 1 ,
            // 在到n=2,由于n=1返回结果是1,那么print(n-1)*n就应该是:print(2-1)*2=1*2=2,此时print(2-1)就是n=1 return返回的结果
            // 在到n=3,由于n=2返回结果是2,那么print(n-1)*n就应该是:print(3-1)*3=2*3=6,此时print(3-1)*3就是n=2 return的结果
            // 再到n=4,以此类推,print(4-1)*4=6*4=24,那么最终结果就是24.
            // 注意: print(4-1) 是表达式的结果,也就是上一个方法print(3)调用return返回的结果
        }
    }
    public static int print2(int n){
        if (n==1){
            return 1;
        }else{
//            System.out.println(n);
            return print2(n-1)+4;
            // 假如调用方法print2(4),那么执行循序:1--->print2(2-1)+4=1+4=5--->print2(3-1)+4=5+4=9--->print2(4-1)+4=9+4=13
        }
    }

    /**
     * 递归实现迷宫的路线问题
     */
    public static void mazeDemo(){
//        int[][] maze = initMaze();
//        route(maze,1,1);
//
//        for (int i = 0; i < maze.length; i++) {
//            for (int j = 0; j < maze[i].length; j++) {
//                System.out.print(maze[i][j]+"  ");
//            }
//            System.out.println();
//        }
//        System.out.println("起点到终点总的步数:"+count);

        System.out.println("=========================分割线,求出最短距离");
        /**
         * 递归查询上下左右排列组合 个数
         */
        List list = new ArrayList();
        list.add("上");
        list.add("下");
        list.add("左");
        list.add("右");

        arrayList(list,"");
        System.out.println("=========================分割线,求出最短距离2");
        // 统计所有走完地图终点所用的步数
        List<Integer> listCount = new ArrayList();
        /**
         * 求出最短距离
         */
        for (int i = 0; i < ruleslist.size(); i++) {
            // 记录每次步数
            ruleCount=0;
            String rule = ruleslist.get(i);
            char[] chars = rule.toCharArray();
            // 获取初始化的地图
            int[][] maze1 = initMaze();

            route2(maze1,1,1,chars);
            System.out.println("打印的地图路径的第+"+i+"+遍=============统计:用了"+ruleCount+"步走到终点");
            listCount.add(ruleCount);

            for (int x = 0; x < maze1.length; x++) {
                for (int j = 0; j < maze1[x].length; j++) {
                    System.out.print(maze1[x][j]+"  ");
                }
                System.out.println();
            }
        }
        System.out.println("步数排序");
        Collections.sort(listCount);
        listCount.stream().limit(1).forEach((data)->{
            System.out.println("最短距离为:"+data );
        });

    }

    /**
     * 地图初始化
     * @return
     */
    public static int [][] initMaze(){
    /**
     * 1  1  1  1  1  1  1  1
     * 1  0  0  0  0  0  0  1
     * 1  0  0  0  0  0  0  1
     * 1  0  0  0  0  0  0  1
     * 1  0  0  0  0  0  0  1
     * 1  0  0  0  0  0  0  1
     * 1  0  0  0  0  0  0  1
     * 1  1  1  1  1  1  1  1
     */
    //定义二维数组,创建二维地图
    int [][] maze = new int[8][8];
    for (int i = 0; i < maze.length; i++) {
        for (int j = 0; j < maze[i].length; j++) {
            // 创建迷宫,初始化围墙,围墙设置成1,
            if(i==0){
                maze[i][j]=1;
            }
            if(i==7){
                maze[i][j]=1;
            }
            if (j==0){
                maze[i][j]=1;
            }
            if (j==7){
                maze[i][j]=1;
            }
//            System.out.print(maze[i][j]+"  ");
        }
//        System.out.println();
    }
//    System.out.println("=================分割线");
    // 定义路线的起点坐标 int[1][1]
    // 定义路线的终点坐标 int[6][6]
    // 那么求出从起点到终点的路线
    // 加上围墙
    maze[3][1]=1;
    maze[3][2]=1;
//        maze[1][2]=1;
    maze[2][2]=1;
    return maze;
}

    /**
     * // 迷宫中,求出从起点到终点的 路线
     * 重要:递归,从起点开始就尝试往下去走,然后只要遇到死了,那么就会把走过的路线都会标记为3,这是利用递归方法后,出栈的方法返回boolean值,
     * 从而判断路线是否可走,打上标记;那么只要路线中有一个坐标能走通,那么就尝试递归去走新的路线,沿途都把标志设置为2
     * @param maze 代表迷宫
     * @param x 列坐标
     * @param y 行坐标
     */
    public static boolean route(int [][] maze,int x,int y){
        // 先定义 0:是未走过的路线,1:是围墙,2:走过成功的路线 3:未成功的路线
        // maze[6][6]终点等于2,说已经走到了终点,那么我们就结束方法
        if (maze[6][6] ==2){
//            count++;
            return true;
        }
            // 如果是没有走过的路线,那么就可以走
            if (maze[x][y]==0){
                // 先设置走的路线为2,后面判断走不了的话,在设置为3
                maze[x][y]=2;
                // 制定路线规则,走 :下 、右、上,左
                // 向下:坐标行加一,x+1
                if(route(maze,x+1,y)){
                    count++;
                    return true;
                }
                // 向右
                if(route(maze,x,y+1)){
                    count++;
                    return true;
                }
                // 向上
                if(route(maze,x-1,y)){
                    count++;
                    return true;
                }
                // 向左
                if(route(maze,x,y-1)){
                    count++;
                    return true;
                }else{
                    // 说明上下左右都走不通了,死路设置为3
                    maze[x][y]=3;
                }
            }
            // 除了为0 那么123都返回false
            return false;
    }


    /**
     * 求出最短距离算法
     * @param maze
     * @param x
     * @param y
     * @return
     */
    public static boolean route2(int [][] maze,int x,int y,char[] chars){
        // 先定义 0:是未走过的路线,1:是围墙,2:走过成功的路线 3:未成功的路线
        // maze[6][6]终点等于2,说已经走到了终点,那么我们就结束方法
        if (maze[6][6] ==2){
//            count++;
            return true;
        }
        // 如果是没有走过的路线,那么就可以走
        if (maze[x][y]==0){
            // 先设置走的路线为2,后面判断走不了的话,在设置为3
            maze[x][y]=2;
            //动态从list中获取路线排列方式
                for (int j = 0; j < chars.length; j++) {
                    // 制定路线规则,走 :下 、右、上,左
                    if("下".equals(String.valueOf(chars[j]))){
                        // 向下:坐标行加一,x+1
                        if(route2(maze,x+1,y,chars)){
                            ruleCount++;
                            return true;
                        }
                    }
                    if("右".equals(String.valueOf(chars[j]))){
                        // 向右
                        if(route2(maze,x,y+1,chars)){
                            ruleCount++;
                            return true;
                        }
                    }
                    if("上".equals(String.valueOf(chars[j]))){
                        // 向上
                        if(route2(maze,x-1,y,chars)){
                            ruleCount++;
                            return true;
                        }
                    }
                    if("左".equals(String.valueOf(chars[j]))){
                        // 向上
                        if(route2(maze,x,y-1,chars)){
                            ruleCount++;
                            return true;
                        }
                    }
                }
//                else{
                // 说明上下左右都走不通了,死路设置为3
                maze[x][y]=3;
//            }
        }
        // 除了为0 那么123都返回false
        return false;
    }


    /**
     * 递归查询 上下左右 路径 的所有排列方式
     * @param list
     * @param str
     */
    public static void arrayList(List list,String str)
    {
        if(list.size()==0){
            System.out.println("第" + min++ + "个:" + str);
            ruleslist.add(str);
        }
        for(int i=0;i<list.size();i++)
        {
            // 这步就是把对象赋值给second,后续在操作second的remove移除元素,就不会影响list本身元素了
            List second = new ArrayList(list);
            arrayList(second,str + second.remove(i));
        }
    }


}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值