java数据结构学习持续更新

一、稀疏数组

概念:
在这里插入图片描述
简单例子:
在这里插入图片描述

(1) 二维数组->稀疏数组 || 稀疏数组->二维数组

在这里插入图片描述

(2)存入文件

package 稀疏数组;


import java.awt.*;
import java.io.*;

/**
 * @Author: LBC
 * @Date: 2024/1/1
 */

public class SparseArrayFile {
    public static void main(String[] args) throws Exception{
        //创建一个原始数组11*11
        //0表示没有棋子;1表示黑子;2表示蓝子
        int chessArray1[][] = new int[11][11];
        chessArray1[1][2] = 1;
        chessArray1[2][3] = 2;
        System.out.println("原始的二维数组:");
        for (int[] row : chessArray1) {
            for (int data : row) {
                System.out.printf("%d\t", data);
            }
            System.out.println();
        }


        //二维数组->稀疏数组
        //1.遍历二维数组,获取非0数据个数
        int sum = 0;
        for (int i = 0; i < chessArray1.length; i++) {
            for (int j = 0; j < chessArray1.length; j++) {
                if (chessArray1[i][j] != 0) {
                    sum++;
                }
            }
        }
        System.out.println("sum =" + sum);

        //2.创建对应的稀疏数组
        int sparseArray[][] = new int[sum + 1][3];
        //给稀疏数组赋值
        sparseArray[0][0] = chessArray1.length;
        sparseArray[0][1] = chessArray1[0].length;
        sparseArray[0][2] = sum;
        //遍历二维数组,将非0数据存放到稀疏数组
        int count = 0;
        for (int i = 0; i < chessArray1.length; i++) {
            for (int j = 0; j < chessArray1.length; j++) {
                if (chessArray1[i][j] != 0) {
                    count++;
                    sparseArray[count][0] = i;
                    sparseArray[count][1] = j;
                    sparseArray[count][2] = chessArray1[i][j];
                }
            }
        }

        //保存稀疏数组
        File file = new File("D:\\桌面\\数据结构\\DataStructure\\File\\Map.data");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");

        //输出稀疏数组的形式
        System.out.println();
        System.out.println("稀疏数组的形式为:");
        for (int i = 0; i < sparseArray.length; i++) {
            System.out.printf("%d\t%d\t%d\t\n",sparseArray[i][0],sparseArray[i][1],sparseArray[i][2]);

            if (i == sparseArray.length-1) {
                outputStreamWriter.append(sparseArray[i][0]+","+sparseArray[i][1]+","+sparseArray[i][2]);
            }else {
                outputStreamWriter.append(sparseArray[i][0]+","+sparseArray[i][1]+","+sparseArray[i][2]+",");
            }
        }

        System.out.println("-----------写入文件中");
        outputStreamWriter.close();
        fileOutputStream.close();

        System.out.println("-----------打开文件中");
        Desktop.getDesktop().open(file);
        System.out.println("-----------读取Map.data");

        //创建FileReader对象
        FileInputStream fileInputStream = new FileInputStream(file);
        //读取
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
        StringBuffer sb = new StringBuffer();
        while (inputStreamReader.ready()){
            sb.append((char)inputStreamReader.read());// 转成char加到StringBuffer对象中
        }

        System.out.println(sb.toString());
        inputStreamReader.close();
        inputStreamReader.close();

        System.out.println("-------------------------------恢复稀疏数组SparseArrayFile");
        //创建对应的稀疏数组
        String[] str = sb.toString().split(",");
        int[][] SparseArrayFile = new int[str.length/3][3];
        //给稀疏数组赋值
        int i = 0;
        for (String s : str) {
            SparseArrayFile[(i - i % 3) / 3][i % 3]= Integer.parseInt(s);
            i++;
        }

        System.out.println("----------------恢复成二维数组chessArray2");

        //恢复
        int[][] chessArray2 = new int[SparseArrayFile[0][1]][SparseArrayFile[0][1]];

        for (int j = 1; j < SparseArrayFile.length; j++) {
            chessArray2[SparseArrayFile[j][0]][SparseArrayFile[j][1]]=SparseArrayFile[j][2];
        }
        //输出恢复后的二维数组
        System.out.println();
        for (int[] ints : chessArray2) {
            for (int data : ints) {
                System.out.printf("%d\t",data);
            }
            System.out.println();
        }

        System.out.println("--------------------------------------------------------恢复完成");




    }
}

二、队列

在这里插入图片描述

在这里插入图片描述

(1)数组模拟队列

package 队列;

import java.util.Scanner;

/**
 * @Author: LBC
 * @Date: 2024/1/3
 */

public class ArrayQueueDemo {

    public static void main(String[] args) {

        ArrayQueue arrayQueue = new ArrayQueue(3);
        char key =' ';//接受输入数据
        Scanner scanner = new Scanner(System.in);
        boolean flag = true;
        while (flag){
            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列中取出数据");
            System.out.println("h(show):查看队列的头数据");
            key = scanner.next().charAt(0); //接收一个字符
            switch (key){
                case 's':
                    arrayQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("输入一个数据:");
                    int value = scanner.nextInt();
                    arrayQueue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = arrayQueue.getQueue();
                        System.out.printf("取出的数据是%d\n",res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = arrayQueue.getHeadQueue();
                        System.out.printf("队列头的数据是%d\n",res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    flag = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");

    }
}

class ArrayQueue{

    private int maxSize; //表示数组的最大容量
    private int front;//队列头,front指向队列头的前一个位置
    private int rear;//队列尾,指向队列尾的数据
    private int[] arr;//模拟队列,存放数据

    //创建队列的构造器
    public ArrayQueue(int arrMaxsize) {
        maxSize=arrMaxsize;
        arr=new int[maxSize];
        front = -1;
        rear = -1;
    }

    //判断队列是否为满
    public boolean isFull(){
        return rear == maxSize-1;
    }

    //判断队列是否为空
    public boolean isEmpty(){
        return rear == front;
    }

    //添加数据到队列
    public void addQueue(int n){
        if(isFull()){
            System.out.println("队列满了,不能加入");
            return;
        }
        rear++;
        arr[rear]=n;
    }

    //获取队列的数据,出队列
    public int getQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列为空,获取不到数据");
        }
        front++;
        return arr[front];
    }

    //获取队列的所有数据
    public void showQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列为空,获取不到数据");
        }
        for (int i = 0; i < arr.length ;i++) {
            System.out.printf("arr[%d]=%d\n",i,arr[i]);
        }
    }

    //显示队列头数据
    public int getHeadQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列为空,获取不到数据");
        }
        return arr[front+1];
    }

}

(2)环形队列

第一种方式数组不能复用,复用需要取模 %

改进思路:
在这里插入图片描述
其中判断队列满的思想的话,可以看下图,因为是环形的,起初front=rear=0,每当添加元素时,将rear++,但是其实预留了一个长度没有用,比如定义的队列数组长度为5时,但是实际上可以使用的地址就是0,1,2,3,此时rear=4, 4这个空间用来判断队满的条件(rear+1)%maxSize==front
在这里插入图片描述

package 队列;

import java.util.Scanner;

/**
 * @Author: LBC
 * @Date: 2024/1/5
 *
 * rear指向了最后一个元素的后一个位置,
 * 对应的头也从前一个位置改成了第一个元素,
 * 如果不加1,取模就相当于再循环一次,就相当于队尾等于队列头,重合了一个位置
 */

public class ArrayQueueCircle {
    public static void main(String[] args) {
        //        创建一个环形队列,maxSize设置说明,4,其队列的有效数据最大为3
        CircleQueue circleQueue = new CircleQueue(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':
                    circleQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("输入一个数:");
                    int value = scanner.nextInt();
                    circleQueue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = circleQueue.getQueue();
                        System.out.printf("取出的数据是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = circleQueue.headQueue();
                        System.out.printf("队列头的数据是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    loop = false;
                    scanner.close();
                    break;
                default:
                    break;
            }
        }
        System.out.println("-----程序退出-----");

    }
}

/**
 * 环形队列类
 * 构造器
 * 判断是否已满、判断是否空、查看队列数据、显示队列的有效数据个数、入队列、出队列
 */

class CircleQueue {
    //    数组的最大容量
    private final int maxSize;
    //    front指向队列的第一个元素,初始值为0
    private int front;
    //    rear指向队列的最后一个元素的后一个位置,空出一个空间作为约定,初始值为0
    private int rear;
    //    存放数据,模拟队列
    private final int[] arr;

    //    创建队列构造器
    public CircleQueue(int maxSize) {
        this.maxSize = maxSize;
        front = 0;
        rear = 0;
        arr = new int[maxSize];
    }

    //    判断队列是否已满
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    //    判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    //    查看队列数据,显示队列所有数据
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列为空,没有数据!");
            return;
        }
//        从front开始遍历,注意遍历的元素个数,遍历有效数据个数
        // front = 4 , rear = 3 , maxSize= 5
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }

    }

    //    求出当前队列有效数据的个数
    // rear = 1
    // front = 0
    // maxSize =3
    public int size() {
        return (rear + maxSize - front) % maxSize;
    }

    //    显示队列的头数据,注意不是取出数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列是空的,没有数据!");
        }
        return arr[front];
    }

    //    添加数据到队列
    public void addQueue(int n) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }
        arr[rear] = n;
        System.out.println(n + "成功添加到队列");
        //        将rear后移,这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

    //    从队列取出数据,,出队列
    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法取出数据");
        }
        //        这里需要分析出front是指向队列的第一个元素
        //        1. 先把front对应的值保留到一个临时变量
        //        2. 将front后移,考虑取模
        //        3. 将临时保存的变量取回
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }
}


三、链表

在这里插入图片描述

(1)单链表

第一种添加方式:

在这里插入图片描述

//添加
 public void add(HeroNode heroNode){
        HeroNode temp = head;
        while (true){
            if(temp.next==null) {
                break;
            }
            temp=temp.next;
        }
        temp.next=heroNode;
    }
//遍历
 public void list(){
        //判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动,所以定义一个变量
        HeroNode temp = head.next;
        //遍历
        while (true){
            //判断节点是否在最后
            if (temp==null){
                break;
            }
            //输出信息
            System.out.println(temp);
            //节点后移
            temp = temp.next;
        }

    }

第二种添加方式:

在这里插入图片描述

public void addByOrder(HeroNode heroNode){
        HeroNode temp = head;
        boolean flag = false;// 编号是否存在,默认为false
        while(true){
            if(temp.next==null){
                break;
            }
            if(temp.next.no > heroNode.no){
                //位置找到,插入到temp后面
                break;
            }else if(temp.next.no == heroNode.no){
                //说明编号存在
                flag=true;
                break;
            }
            temp=temp.next;//遍历链表
        }
        //判断flag的值
        if(flag){
            System.out.printf("准备插入的英雄的编号%d已经存在,不能加入\n",heroNode.no);
        }else {
            //插入到链表中,temp的后面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

单链表的修改

在这里插入图片描述

根据编号来找

public void update(HeroNode newHeroNode){
        //判断是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点的编号no
        //定义一个辅助变量
        HeroNode temp = head.next;
        boolean flag = false; //表示是否找到该节点
        while (true){
            if(temp == null){
                break;//遍历完链表
            }
            if(temp.no==newHeroNode.no){
                //找到该节点
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到节点
        if(flag){
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        }else {
            System.out.printf("没有找到编号%d的节点\n",newHeroNode.no);
        }
    }

单链表的删除

在这里插入图片描述

 public void delete(int no){
        HeroNode temp = head;
        boolean flag = false;
        while (true){
            if(temp.next==null){
                break;
            }
            if (temp.next.no==no){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.next = temp.next.next;
        }else {
            System.out.printf("要删除的%d节点没找到\n",no);
        }
    }

单链表有效节点个数

 /**
     * @param head 链表的头节点
     * @return 有效节点的个数
     */
    public static int getLength(HeroNode head){
        if(head.next==null){
            return 0;
        }
        int length = 0;
        //定义一个辅助变量
        HeroNode cur = head;
        while (cur.next!=null){
            length++;
            cur = cur.next;
        }
        return length;
    }

查找链表倒数第k个节点

    //思路
    //1.编写getHead方法获取head节点
    //2.index 表示倒数第index个节点
    //3.先把链表遍历,得到链表总长度length
    //4.得到size后,我们从链表的第一个开始遍历(size-index)个节点,就可以得到
    //5.如果找到了返回该节点,没找到返回null
    public static HeroNode getNode(HeroNode head,int index){
        if(head.next==null){
            return null;
        }
        int size = getLength(head);
        if(index <= 0 || index > size){
            return null;
        }
        //定义辅助变量
        HeroNode cur = head.next;
        for (int i = 0; i < size - index; i++) {
            cur = cur.next;
        }
        return cur;
    }

单链表的反转

在这里插入图片描述

 public  static void reverseList(HeroNode head){
        //当前链表为空或者只有一个节点,无需反转
        if(head.next == null || head.next.next == null){
            return ;
        }
        //定义一个辅助变量,帮助遍历原来的链表
        HeroNode cur = head.next;
        HeroNode next = null; //当前节点[cur]的下一个节点
        HeroNode reverseHead = new HeroNode(0,"","");
        //遍历原来的链表,每次遍历一个节点,就将其取出,并放在新的链表中
        while (cur != null){
            next = cur.next;//保存当前节点的下一个节点
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            cur = next ;//cur后移
        }
        head.next = reverseHead.next;
    }

从尾打印单链表

在这里插入图片描述
方式二

public static void reversePrint(HeroNode head){
        if(head.next == null){
            return;
        }
        Stack<HeroNode> stack = new Stack<>();
        HeroNode cur = head.next;
        //所有节点压入栈
        while (cur != null){
            stack.push(cur);
            cur = cur.next;
        }
        //打印
        while (stack.size() > 0){
            System.out.println(stack.pop());
        }

合并两个有序单链表

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

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

  public ListNode method2(ListNode l1,ListNode l2){
        //如果某一个链表为空,返回另一个不为空的链表
        if(l1==null){
            return l2;
        }
        if(l2==null){
            return l1;
        }
 
        if(l1.val<=l2.val){
            //如果list1的头结点小于list的头结点,递归 合并list1剩余的部分和list2
            l1.next = method2(l1.next,l2);
            return l1;
        }else {
            l2.next = method2(l2.next, l1);
            return l2;
        }
    }

(2)双向链表

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

class DoubleLinkedList{
    //初始化头节点
    private HeroNode2 head = new HeroNode2(0,"","");

    //返回头节点
    public HeroNode2 getHead() {
        return head;
    }

    //显示链表[遍历]
    public void list(){
        //判断链表是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //因为头节点不能动,所以定义一个变量
        HeroNode2 temp = head.next;
        //遍历
        while (true){
            //判断节点是否在最后
            if (temp==null){
                break;
            }
            //输出信息
            System.out.println(temp);
            //节点后移
            temp = temp.next;
        }
    }

    //添加一个双向链表到最后
    public void add(HeroNode2 heroNode){
        HeroNode2 temp = head;
        while (true){
            if(temp.next==null) {
                break;
            }
            temp=temp.next;
        }
        temp.next = heroNode;
        heroNode.pre = temp;
    }

    public void update(HeroNode2 newHeroNode){
        //判断是否为空
        if(head.next==null){
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的节点的编号no
        //定义一个辅助变量
        HeroNode2 temp = head.next;
        boolean flag = false; //表示是否找到该节点
        while (true){
            if(temp == null){
                break;//遍历完链表
            }
            if(temp.no==newHeroNode.no){
                //找到该节点
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根据flag判断是否找到节点
        if(flag){
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        }else {
            System.out.printf("没有找到编号%d的节点\n",newHeroNode.no);
        }
    }

    public void delete(int no){

        if(head.next ==null){
            System.out.println("链表为空,无法删除");
            return;
        }

        HeroNode2 temp = head.next;//单链表删除需要遍历到删除节点的前一个,双向链表需要遍历到当前节点
        boolean flag = false;
        while (true){
            if(temp==null){
                break;
            }
            if (temp.no==no){
                flag=true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
//            temp.next = temp.next.next;//【单向链表】
            temp.pre.next = temp.next;
            if(temp.next != null) {
                temp.next.pre = temp.pre;
            }
        }else {
            System.out.printf("要删除的%d节点没找到\n",no);
        }
    }
}

双向链表按照序号顺序添加

 public void addByOrder(HeroNode2 heroNode){
        HeroNode2 temp = head;
        boolean flag = false;// 编号是否存在,默认为false
        while(true){
            if(temp.next==null){
                break;
            }
            if(temp.next.no > heroNode.no){
                //位置找到,插入到temp后面
                break;
            }else if(temp.next.no == heroNode.no){
                //说明编号存在
                flag=true;
                break;
            }
            temp=temp.next;//遍历链表
        }
        //判断flag的值
        if(flag){
            System.out.printf("准备插入的英雄的编号%d已经存在,不能加入\n",heroNode.no);
        }else {
            //插入到链表中,temp的后面[单向链表]
//            heroNode.next = temp.next;
//            temp.next = heroNode;

            //双向链表1.heroNode指向temp节点的下一个节点
            heroNode.next = temp.next;
            if (temp.next != null){
                temp.next.pre = heroNode;
            }
            //2.temp节点指向heroNode
            temp.next = heroNode;
            heroNode.pre = temp;
        }
    }

单向环形链表(约瑟夫问题)

在这里插入图片描述
环形链表:
在这里插入图片描述

//环形单向链表
class CircleSingleLinkedList{

    //创建一个first节点
    private Boy first = new Boy(-1);

    //添加一个小孩节点,构建环形链表
    public void addBaby(int nums){

        if(nums < 1){
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null; //帮助指针,创建环形链表
        //使用for循环创建环形链表
        for (int i = 1; i <= nums; i++) {
            //根据编号创建小孩节点
            Boy boy = new Boy(i);
            //如果是第一个小孩
            if(i == 1){
                first = boy;
                first.setNext(first);//构成环
                curBoy = first; //让帮助指针指向第一个小孩
            }else {
                curBoy.setNext(boy); //最后一个节点指向新节点
                boy.setNext(first); //新节点连接首节点
                curBoy = boy; //帮助指针回到最后节点【新节点】
            }
        }
    }

    //遍历当前环形链表
    public void showBoy(){
        //判断链表是否为空
        if (first == null){
            System.out.println("没有任何小孩");
            return;
        }
        //因为first指针不能动,所以需要辅助指针
        Boy curBoy = first;
        while (true){
            System.out.printf("小孩的编号 %d \n",curBoy.getNo());
            if (curBoy.getNext() == first){
                break;
            }
            curBoy = curBoy.getNext();
        }

    }
}

class Boy{
    private int no;
    private Boy next;

    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

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

    public Boy getNext() {
        return next;
    }

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

问题思路:
在这里插入图片描述

在这里插入图片描述

/**
     * @param startNo 表示从第几个小孩开始数数
     * @param countNum 表示数几下
     * @param nums 表示最初有多少小孩在圈中
     */
    public void countBoy(int startNo, int countNum, int nums){
        //对数据进行检验
        if(first == null || startNo < 1 || startNo > nums){
            System.out.println("输入参数不合法");
            return;
        }
        //创建辅助指针,帮小孩出圈
        Boy helper = first;
        //将辅助指针指向链表最后节点
        while (true){
            if (helper.getNext() == first){
                break;
            }
            helper = helper.getNext();
        }
        //开始数数前,first和helper需要移动 startNo-1 次
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //开始数数时,first和helper需要同时移动 countNum-1 次
        while (true){
            if (helper == first){ //说明圈中只剩下一个节点
                break;
            }
        for (int i = 0; i < countNum - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //此时的first节点指向的是出圈节点
            System.out.printf("小孩%d出圈\n",first.getNo());
        //将first指向的节点出圈
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的节点%d\n",helper.getNo());
    }

四丶栈

介绍:
在这里插入图片描述应用场景:
在这里插入图片描述
代码实现:
在这里插入图片描述

数组模拟思路

在这里插入图片描述


class ArrayStack{
    private int maxSize; //栈的大小
    private int[] stack; //数组模拟栈
    private int top = -1; //栈顶

    //构造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new int[this.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()){
            throw new RuntimeException("栈空,没有数据");
        }
        int value = stack[top];
        top--;
        return value;
    }

    //遍历栈
    public void list(){
        if(isEmpty()){
            System.out.println("栈空,没有数据");
        }
        for (int i = top; i >= 0 ; i--) {
            System.out.printf("stack[%d]=%d\n",i,stack[i]);
        }
    }
}

使用栈完成算术运算符的表达

思路:
在这里插入图片描述
解决处理多位数问题

package D;

public class Calculator {
    public static void main(String[] args) {
        String expression = "70+2*6-2";
        //创建两个栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);
        int index = 0;//用于扫描表达式
        int num1 = 0;
        int num2 = 0;
        int oper = 0;
        int res = 0;
        char ch = ' ';
        String keepNum = "";//用于拼接多位数
        while (true) {
            ch = expression.substring(index, index + 1).charAt(0);
            if (operStack.isOper(ch)) {//扫描到是符号
                if (!operStack.isEmpty()) {
                    //如果当前遍历到的符号优先级小于或等于符号栈中符号的优先级,
                    //则从数栈中pop出两个数,从符号栈中pop出一个符号,进行运算,
                    // 然后将运算结果入数栈,将当前遍历到的符号入符号栈;
                    if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        oper = operStack.pop();
                        res = numStack.cal(num1, num2, oper);
                        numStack.push(res);
                        operStack.push(ch);

                    } else {
                        //如果**当前遍历到的符号优先级大于符号栈中符号优先级**,则将该符号直接入符号栈
                        operStack.push(ch);
                    }
                } else {
                    //若符号栈为空则入符号栈;
                    operStack.push(ch);
                }

            } else {//扫描到是数字
                //numStack.push(ch-48);
                //处理多位数:
                // 当index扫描到数字后 再往后扫描一位 若后一位是符号则将该数字入数栈 否则继续扫描
                // 需要定义一个变量字符串用于拼接数字
                //如果ch已经是expression的最后一位则直接入栈
                keepNum += ch;
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));

                } else {

                    if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
                        numStack.push(Integer.parseInt(keepNum));
                        //注意要将keepNum清空
                        keepNum = "";
                    }
                }
            }
            index++;
            if (index >= expression.length()) {
                break;
            }
        }
        //表达式遍历完毕后,则按顺序从数栈和符号栈中pop出值进行运算
        while (true) {
            if (operStack.isEmpty()) {
                break;
            }
            num1 = numStack.pop();
            num2 = numStack.pop();
            oper = operStack.pop();
            res = numStack.cal(num1, num2, oper);
            numStack.push(res);
        }
        System.out.printf("表达式%s的结果为:%d", expression, numStack.pop());

    }


}

class ArrayStack2 {
    private int maxSize;//栈的最大容量
    private int[] stack;//定义一个数组 栈的数据存在数组中
    private int top = -1;//指向栈顶

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

    //栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }

    //栈空
    public boolean isEmpty() {
        return top == -1;
    }

    //显示当前栈顶元素
    public int peek() {
        return stack[top];
    }

    //入栈
    public void push(int data) {
        if (isFull()) {
            System.out.printf("栈满,无法入栈");
            return;
        } else {
            top++;
            stack[top] = data;
        }
    }

    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空");
        } else {
            int value = stack[top];
            top--;
            return value;
        }
    }

    //显示栈
    public void show() {
        if (isEmpty()) {
            throw new RuntimeException("栈空");
        } else {
            for (int i = top; i >= 0; i--) {
                System.out.printf("stack[%d]=%d\n", i, stack[i]);
            }
        }
    }

    //返回运算符的优先级   规定数字越大优先级越高
    public int priority(int oper) {
        if (oper == '*' || oper == '/') return 1;
        else if (oper == '+' || oper == '-') return 0;
        else return -1;//假设当前只有+ - * /四种符号
    }

    //判断是否是符号
    public boolean isOper(int oper) {
        return oper == '+' || oper == '-' || oper == '*' || oper == '/';
    }

    //定义运算规则
    public int cal(int num1, int num2, int oper) {
        int res = 0;
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                break;
        }
        return res;
    }

}

前,中,后缀表达式

(1)前缀表达式在这里插入图片描述

(2)中缀表达式

在这里插入图片描述

(3)后缀表达式

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

(4)逆波兰表达式运算器

在这里插入图片描述

public class PoLandLocation {
    public static void main(String[] args) {
        //先定义逆波兰表达式
        //(3+4)*5-6 ->  3 4 + 5 * 6 -
        //4*5-8+60+8/2 -> 4 5 * 8 - 60 + 8 2 / +
        //说明为了方便,逆波兰表达式 的数字和符号使用空格隔开
        String suffixExpression2 = "3 4 + 5 * 6 -";
        String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";
        //思路
        //1.先将逆波兰表达式放入ArrayList中
        //2.将ArrayList传递给一个方法,配合栈完成计算
        List<String> strings = new ArrayList<>();
        strings = getListString(suffixExpression);
        System.out.println(strings);
        System.out.println("结果为:"+calculate(strings));
    }

    //方法存入表达式
    public static List<String> getListString(String suffixExpression){
        //将表达式分割
        String[] split = suffixExpression.split(" ");
        ArrayList<String> list = new ArrayList<>();
        for (String ele: split) {
            list.add(ele);
        }
        return list;
    }

    //方法运算
    public static int calculate(List<String> ls){
        Stack<String> stack = new Stack<>();
        for (String ele: ls) {
            //使用正则表达式取出数
            if(ele.matches("\\d+")){
                //匹配数字入栈
                stack.push(ele);
            }else {
                //否则pop两个数计算结果入栈
                int num2 = Integer.parseInt(stack.pop());
                int num1 = Integer.parseInt(stack.pop());
                int res = 0;
                if(ele.equals("+")){
                    res = num1 + num2;
                } else if (ele.equals("-")) {
                    res = num1 - num2;
                }else if (ele.equals("*")) {
                    res = num1 * num2;
                }else if (ele.equals("/")) {
                    res = num1 / num2;
                }else{
                    throw new RuntimeException("包含错误运算符");
                }
                stack.push(""+res);
            }
        }
        return Integer.parseInt(stack.pop());
    }
}

(5)中缀转后缀表达式

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

package D;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;

// 逆波兰表达式完整版(可以匹配小数点)
public class PolandNotationFullVersion {
	
	//完成将一个中缀表达式转成后缀表达式的功能
	//说明
	//1. 1+((2+3)×4)-5 => 转成  1 2 3 + 4 × + 5 –
	//2. 因为直接对str 进行操作,不方便,因此 先将  "1+((2+3)×4)-5" =》 中缀的表达式对应的List
	//   即 "1+((2+3)×4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
	//3. 将得到的中缀表达式对应的List => 后缀表达式对应的List
	//   即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]

	public static void main(String[] args) {
		String expression = "1+((2+3)*4)-5+10/61";
		List<String> expressionList = expressionSplit(expression);
		List<String> postfixExpression = postfixExpressionChange(expressionList);
		System.out.println("中缀转后缀表达式:"+postfixExpression);
		double ans = calculate(postfixExpression);
		System.out.println(expression + "=" + ans);
	}
	
	/*
		1.将字符串的表达式拆分开来,保存到List中,目的是为了方便后续的计算。
	*/
    public static List<String> expressionSplit(String str) {
    	List<String> expressionList = new ArrayList<String>();
    	String temp = "";
    	
    	for(int i=0;i<str.length();i++) {
    		char item = str.charAt(i);
    		
    		if(!numberJudge(item)) {
    			expressionList.add(String.valueOf(item));
    		}else if( item>= 48 && item<= 57 || item == 46) {
    			temp+= item;
    			if(i+1==str.length() || !numberJudge(str.charAt(i+1))) {
    				expressionList.add(String.valueOf(temp));
    				temp = "";
    			}
    		}else {
    			throw new RuntimeException("表达式格式不正确");
    		}
    	}
    	
    	return expressionList;
    }
    
	/*
		2. 根据规则,将中缀表达式转为后缀表达式。一个stack,一个List存储结果
	*/
    public static List<String> postfixExpressionChange(List<String> expressionSplited){
    	Stack<String> stack = new Stack<String>(); //存储符号
    	List<String> postfixExpression = new ArrayList<String>(); //存储后缀表达式
    	
    	for(String item : expressionSplited) {
    		if(numberJudge(item.toCharArray()[0])) {
    			postfixExpression.add(item);
    		}else {    			
    			// 初始状态,符号栈里面什么都没有
    			if(stack.size()==0) {
    				stack.push(item);
    			} else {
    				Operation2 oprCls = new Operation2();
        			String lastOpr = stack.peek();
        			if(item.equals("(") || oprCls.oprPriority(item) > oprCls.oprPriority(lastOpr)) {
        				stack.push(item);
        			}else if(item.equals(")")) {
        				while(stack.size()>0 && !stack.peek().equals("(")) {
            				postfixExpression.add(stack.pop());
        				}
        				stack.pop();
        			}else {
        				postfixExpression.add(stack.pop());
        				stack.push(item);
        			}
        			lastOpr = "";
    			}
    		}
    	}
    	
    	while(stack.size()!=0) {
    		postfixExpression.add(stack.pop());
    	}
    	
    	return postfixExpression;
    }
    
    /*
	  	3.根据逆波兰表达式直接给计算器进行相应的计算
	 */
	public static Double calculate(List<String> list) {
		Stack<String> stack = new Stack<String>();
		
		for(String item : list) {
			
			if(item.matches("((\\d+)(\\.\\d+)?)")) {
				stack.push(item);
			}else {
				
				String num2Str = stack.pop();
				String num1Str = stack.pop();
				
				double num2 = Double.parseDouble(num2Str),
					num1 = Double.parseDouble(num1Str),
					ans = 0;
				
				if(item.equals("+")) {
					ans = num1 + num2;
				}else if(item.equals("-")) {
					ans = num1 - num2;
				}else if(item.equals("*")) {
					ans = num1 * num2;
				}else if(item.equals("/")) {
					ans = num1 / num2; 
				}
				
				stack.push("" + ans);
			}
		}
		
		return Double.parseDouble(stack.pop());
	}
	
	/*
	 * 4.定义一个方法,判断该字符是数字还是符号
	 */
	public static Boolean numberJudge(char ch) {
    	String oprRegEx = "[\\+\\-\\*\\/()]";
    	if(Pattern.matches(oprRegEx,String.valueOf(ch))) {
    		return false;
    	}
    	return true;
	}
}

class Operation2 {
	private int BRACKET = 0;
	private int ADD = 1;
	private int DELETE = 1;
	private int MULTIPLY = 2;
	private int DIVIDE = 2;
	
	public Integer oprPriority(String opr) {
		int result = 0;
		
		switch(opr) {
			case "+":
				result = ADD;
				break;
			case "-":
				result = DELETE;
				break;
			case "*":
				result = MULTIPLY;
				break;
			case "/":
				result = DIVIDE;
				break;
			default:
				result = BRACKET;
				break;
		}
		
		return result;
	}
}

五、递归

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

(1)迷宫问题

在这里插入图片描述

package E;

/**
 * @Author: LBC
 * @Date: 2024/2/25
 */

public class MIGONG {
    public static void main(String[] args) {
        //创建一个二维数组模拟迷宫
        int [][] map = new int [8][7];

        //使用1表示墙,上下置为1
        for (int i = 0; i < 7; i++) {
            map[0][i] = 1;
            map[7][i] = 1;
        }
        //左右置为1
        for (int i = 0; i < 8; i++) {
            map[i][0] = 1;
            map[i][6] = 1;
        }
        //设置挡板 第四行二三列
        map[3][1] = 1;
        map[3][2] = 1;

        //输出地图
        System.out.println("地图的情况:");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }

        //输出新地图
        setWay(map,1,1);
        System.out.println();
        System.out.println("新地图的情况:");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 7; j++) {
                System.out.print(map[i][j]+" ");
            }
            System.out.println();
        }
    }

    //使用递归给小球找路
    //1. map表示地图
    //2. i,j表示从地图的哪个位置出发(1,1)
    //3. 如果小球能到map[6][5]位置,则说明通路找到
    //4. 约定:当map[i][j] 为0表示该点没有走过 当为 1 表示墙 ;2 表示通路可以走 ; 3 表示该路已经走过,但是走不通
    //5. 走迷宫,需要定义一个策略(方法) 下->右->上->左 , 如果该点走不通,再回溯
    /**
     *
     * @param map 表示地图
     * @param i 从哪个位置开始
     * @param j
     * @return 找到通路返回true,返回false
     */
    public static boolean setWay(int[][] map,int i,int j){
        if(map[6][5] == 2){ //通路找到
            return true;
        }else {
            if(map[i][j] == 0){ //当前点还未走过
                //按照策略,下右上左
                map[i][j] =2; //假设该点可以走通
                if(setWay(map,i+1,j)){//向下走
                    return true;
                } else if (setWay(map,i,j+1)) {//向右走
                    return true;
                } else if (setWay(map,i,j-1)) {//向左走
                    return true;
                } else if (setWay(map,i-1,j)) {//向下走
                    return true;
                }else {
                    //说明该点走不通
                    map[i][j] = 3;
                    return false;
                }
            }else {
                return false;
            }
        }
    }
}

(2)八皇后

在这里插入图片描述
具体的执行过程如下:
先将第一个皇后放在第一行第一列,然后将第二个皇后放在第二行第一列,判断该种摆法是否符合要求。很明显这样摆不行,有两个皇后会在同一列。再将第二个皇后放在第二行第二列,这样也不行,两个皇后会在一条斜线上。将第二个皇后放在第二行第三列,这样满足当前条件。
目前已经有两个皇后满足条件,接下来放第三个,还是从第三行第一列开始放置,不满足条件再放第二列,第三列…一直到第8个皇后也能放在一个不冲突的位置,此时找到一个符合要求的解。
然后我们开始回溯,将第一个皇后放在第一行第二列,后面的就继续按上面的方式循环,一直到回溯完毕,找出所有符合条件的解为止。

参考文献2有张图,比较详细的用图描述了上面的过程,贴出来大家参考。下面图描述的是4皇后的回溯过程,原理跟8皇后是一致的。
在这里插入图片描述

package E;

public class NQueueV2 {

    public static int N = 8;
    public static int[][] boards = new int[N][N];

    public static int result = 0;

    public static void putQueQue(int k) {
        if (k == N) {
            result++;
            for(int row=0; row<N; row++) {
                for(int col=0; col<N; col++) {
                    System.out.print(boards[row][col] + " ");
                }
                System.out.println();
            }
            System.out.println();
        } else {
            for(int i=0; i<N; i++) {
                if (check(k, i)) {
                    boards[k][i] = 1;
                    putQueQue(k+1);
                    boards[k][i] = 0;
                }
            }
        }
    }

    public static boolean check(int row, int column) {
        //判断同一列
        for(int i=0; i<row; i++) {
            if (boards[i][column] == 1) {
                return false;
            }
        }

        //左斜线
        for(int m=row-1, n=column-1; m>=0 && n >= 0; m--, n--) {
            if (boards[m][n] == 1) {
                return false;
            }
        }

        //右斜线
        for(int m=row-1, n=column+1; m>=0 && n<N; m--, n++) {
            if (boards[m][n] == 1) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        putQueQue(0);
        System.out.println("result is: " + result);
    }
}

六、排序

大概:
在这里插入图片描述

(1)冒泡排序

在这里插入图片描述
一共需要排序 arr.length - 1
第一趟排序4次
第二趟排序3次
第三趟排序2次
第四趟排序1次
i + 1 趟排序 arr.length - 1 - i

1、简易版本

public class BubbleSort {
    public static void main(String[] args) {
        int []arr = new int[]{3,-1,4,-6,0};
        int temp = 0; //临时变量

        System.out.println("原数组:");
        System.out.println(Arrays.toString(arr));

        //冒泡排序
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j+1]){
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            System.out.println("第"+(i+1)+"趟冒泡排序:");
            System.out.println(Arrays.toString(arr));
        }


    }
}

2、优化

某趟排序可能没有发生过一次交换,可提前结束冒泡排序

public class BubbleSortN {
    public static void main(String[] args) {
        int []arr = new int[]{3,9,-1,10,20};

        System.out.println("排序前:");
        System.out.println(Arrays.toString(arr));

        BubbleSort(arr);

        System.out.println("排序后:");
        System.out.println(Arrays.toString(arr));

    }

    public static void BubbleSort(int[] arr){
        int temp = 0;
        boolean flag = false;
        //冒泡排序
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j+1]){
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            if(!flag){
                break;
            }else {
                flag = false;
            }
        }
    }
}

(2)选择排序

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

public class SelectSort {
    public static void main(String[] args) {
        int[] arr = new int[]{104,34,119,1};

          selectSort(arr);


    }

    public static void selectSort(int[] arr){
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                if(min > arr[j]){ //重置最小值
                    min = arr[j];
                    minIndex = j;
                }
            }

            //最小值交换
            if(minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }

            System.out.println("第"+(i+1)+"轮:");
            System.out.println(Arrays.toString(arr));
        }

    }
}

选择排序运行速度比冒泡排序快

(3)插入排序

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

package F排序;

import D.ArrayStackDemo;

import java.util.Arrays;

/**
 * @Author: LBC
 * @Date: 2024/2/28
 */

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = new int[]{104,34,119,1};
        System.out.println("排序前:"+ Arrays.toString(arr));
        insertSort(arr);
        System.out.println("排序后:"+ Arrays.toString(arr));

        
    }
    public static void insertSort(int[]arr){
        for (int i = 1; i < arr.length; i++) {
            //定义待插入数
            int val = arr[i];
            int j = i;
            // 1.代插入数如果 val < val前面的数,需要进行交换
            // 2.将索引后移继续对比,若还是小于前面的数,继续循环,不小于则符合条件arr[j] = val 将数插入到正确位置
            // 3. j = 0 则说明待插入数已经是与比较部分的最小数了,直接arr[j] = val
            while (j > 0 && val < arr[j - 1]){
                arr[j] = arr[j - 1];
                j--;
            }
            arr[j] = val;
        }
    }
}

(4)希尔排序

思路
在这里插入图片描述

在这里插入图片描述

package F排序;

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

/**
 * @Author: LBC
 * @Date: 2024/3/1
 */

public class ShellSort {
    public static void main(String[] args) {
//        int []arr = new int[]{8,9,1,7,2,3,5,4,6,0};
//        shellSort(arr);
        int[] arr = new int[8];
        for (int i = 0; i < 8; i++) {
            arr[i] = (int)(Math.random()*8000000);
        }

        System.out.println("排序前:");
        System.out.println(Arrays.toString(arr));
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String format = simpleDateFormat.format(date);
        System.out.println("排序前时间:"+format);

        shellSort1(arr);

        Date date2 = new Date();
        String format2 = simpleDateFormat.format(date2);
        System.out.println("排序后时间:"+format2);
    }

    //1.交换
    public static void shellSort2(int []arr){
        //根据前面逐步分析
        int temp;
        int count = 0;
        for (int gap = arr.length / 2; gap > 0 ; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                //遍历各组所有元素,共有gap组,步长gap
            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("第"+(++count)+"轮"+Arrays.toString(arr));
        }
        }




//        int temp = 0;
//        //第一轮 10/2
//        for (int i = 5; i < arr.length; i++) {
//            for (int j = i - 5; j >= 0 ; j -= 5) {
//               if(arr[j] > arr[j + 5]){
//                   temp = arr[j];
//                   arr[j] = arr[j + 5];
//                   arr[j + 5] = temp;
//               }
//            }
//            System.out.println("第"+(i-4)+"轮:"+ Arrays.toString(arr));
//        }
//
//        //第二轮 5/2
//        for (int i = 2; i < arr.length; i++) {
//            for (int j = i - 2; j >= 0 ; j -= 2) {
//                if(arr[j] > arr[j + 2]){
//                    temp = arr[j];
//                    arr[j] = arr[j + 2];
//                    arr[j + 2] = temp;
//                }
//            }
//            System.out.println("第"+(i-1)+"轮:"+ Arrays.toString(arr));
//        }
//
//        //第三轮 2/2
//        for (int i = 1; i < arr.length; i++) {
//            for (int j = i - 1; j >= 0 ; j -= 1) {
//                if(arr[j] > arr[j + 1]){
//                    temp = arr[j];
//                    arr[j] = arr[j + 1];
//                    arr[j + 1] = temp;
//                }
//            }
//            System.out.println("第"+(i)+"轮:"+ Arrays.toString(arr));
//        }
     }

     //2.移位
    public static void shellSort1(int []arr){
        int count = 0;
        int temp = 0;
        int j =0;
        for (int gap = arr.length / 2 ; gap > 0 ; gap /= 2) {
            for (int i = gap; i < arr.length; i++) {
                temp = arr[i];
                j = i - gap;
                while (j >= 0 && arr[j] > temp){
                    arr[j + gap] = arr[j];
                    j -= gap;
                }
                arr[j + gap] = temp;
                System.out.println("第"+(++count)+"轮"+Arrays.toString(arr));
            }
        }
    }
}


在比较这两个希尔排序算法的性能时,我们可以看到一些明显的区别:

交换次数不同:在 shellSort1 中,内层循环是通过移动元素而非交换元素来实现的,而 shellSort2 中使用了交换操作。交换操作相对于移动操作来说,通常需要更多的时间开销。因此,shellSort1 的效率可能会比 shellSort2 更高。

增量序列选择:两种算法中的增量序列选择方式稍有不同。shellSort1 每次将增量 gap 减半,而 shellSort2 则是直接除以 2。在实际应用中,增量序列的选择会影响算法的性能,不同的增量序列可能导致不同的排序效率。

输出调试信息:shellSort2 在每一轮排序后都会输出当前数组状态,而 shellSort1 则是在整个排序结束后输出最终结果。输出操作会增加额外的IO开销,影响排序的性能。

综合上述几点,可以解释为什么 shellSort1 可能会比 shellSort2 快。主要原因在于 shellSort1 中避免了不必要的交换操作,并且输出调试信息的时机更为合适。另外,增量序列的选择也可能对排序性能产生影响。

(5)快速排序

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

package F排序;

import java.util.Arrays;

/**
 * @Author: LBC
 * @Date: 2024/3/3
 */

public class QuickSort {
    public static void main(String[] args) {
        int []arr = new int[]{-9,78,0,23,-567,70};
        quickSort(arr,0,arr.length - 1);
        System.out.println("排序后:"+ Arrays.toString(arr));
    }

    public static void quickSort(int[]arr,int left,int right){
        int l = left; //左下标
        int r = right; //右下标
        int temp = 0;
        int pivot = arr[(right+left)/2]; //中轴值

        while (l < r){
            //从中轴值左边找 > pivot的
            while (arr[l] < pivot){
                l += 1;
            }
            //从中轴值右边找 < pivot的
            while (arr[r] > pivot){
                r -= 1;
            }
            //如果 l >= r 说明排序完成
            if(l >= r){
                break;
            }
            //从上面两个while循环找到相应值后,进行交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;

            //如果交换完后,发现这个arr[l] == pivot 需要r--前移
            if(arr[l] == pivot){
                r -= 1;
            }
            //如果交换完后,发现这个arr[r] == pivot 需要l++后移
            if(arr[r] == pivot){
                l += 1;
            }
        }
        //如果l == r,必须l++,r--,否则出现栈溢出
        if(l == r){
            l += 1;
            r -= 1;
        }

        //向左递归
        if(left < r){
            quickSort(arr,left,r);
        }
        //向右递归
        if (right > l){
            quickSort(arr,l,right);
        }
    }
}

(6)归并排序

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

package F排序;

import java.util.Arrays;

/**
 * @Author: LBC
 * @Date: 2024/3/4
 */

public class MergeSort {
    public static void main(String[] args) {
        int []arr = {8 , 4 , 5 , 7 , 1 , 3 , 6 , 2};
        int []temp = new int[arr.length];
        mergeSort(arr,0, arr.length - 1,temp);
        System.out.println("排序后:"+ Arrays.toString(arr));

    }

    //分+合的方法
    public static void mergeSort(int[]arr,int left,int right,int[]temp){
        if(left < right){
            int mid = (left + right) / 2; //中间索引
            //左递归进行分解
            mergeSort(arr,left,mid,temp);
            //右递归进行分解
            mergeSort(arr,mid + 1,right,temp);
            //合并
            merge(arr,left,mid,right,temp);
        }
    }


    //合并的方法
    /**
     *
     * @param arr 排序原始数组
     * @param left 左边有序序列的初始索引
     * @param mid 中间索引
     * @param right 右边索引
     * @param temp 做中转的数组
     */
    public static void merge(int[]arr,int left,int mid,int right,int[]temp){
        int i = left; // 初始化i,左边有序序列的初始索引
        int j = mid + 1; //初始化j,右边有序序列的初始索引
        int t = 0; // 指向temp数组的当前索引

        //(1)
        //先把左右两边(有序)的数据按照规则填充到temp数组
        //直到左右两边的有序序列,有一边处理完毕为止
        while (i <= mid && j<= right){ //继续
            //左边的有序序列的当前元素 <= 右边有序序列的当前元素
            //将左边的当前元素拷贝到temp数组,然后i,t后移
            if(arr[i] <= arr[j]){
                temp[t] = arr[i];
                t += 1;
                i += 1;
            }else {
                //右边的有序序列的当前元素 <= 左边有序序列的当前元素
                //将右边的当前元素拷贝到temp数组,然后j,t后移
                temp[t] = arr[j];
                t += 1;
                j += 1;
            }
        }

        //(2)
        //把有剩余数据的一边数据依次全部填充到temp,可能左边剩,也可能右边剩
        while (i <= mid){ //左边剩余,全部填充到temp
            temp[t] = arr[i];
            i += 1;
            t += 1;
        }
        while (j <= right){//右边剩余
            temp[t] = arr[j];
            j += 1;
            t += 1;
        }

        //(3)
        //将temp数组的元素拷贝到arr
        //并不是每次拷贝所有数据
        t = 0;
        int tempLeft = left;
        System.out.println("tL:"+tempLeft+","+"r:"+right);
        while (tempLeft <= right){
            //例子int arr[] = {8,4,5,7,1,3,6,2}
            //第一次合并 tempLeft = 0,right = 1
            //第二次 tempLeft = 2,right = 3
            //第三次 tempLeft = 0,right = 3
            //最后一次 tL = 0,right = 7
            arr[tempLeft] = temp[t];
            tempLeft += 1;
            t += 1;
        }
    }
}

(7) 基数排序

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

在这里插入图片描述

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

在这里插入图片描述


public class RadixSort {
    public static void main(String[] args) {
        int[]arr = { 53, 3 , 542 , 748 , 14 , 214};
        radixSort(arr);
    }

    public static void radixSort(int[]arr){
        //最终版本
        //1. 得到数组中最大的数的位数
        int max = arr[0];
        for (int i = 1; i < arr.length ; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        //将int 转换为 char
        int maxLength = (max + "").length();

        //二维数组来表示10个桶,每个桶就是一个一维数组
        int[][] bucket = new int[10][arr.length];

        //定义一个一维数组,来记录各个桶每次放入的数据个数
        //例如:bucketElementCount[0]记录 bucket[0] 桶放入的个数
        //bucketElementCount[1]记录 bucket[1] 桶放入的个数
        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){
                    //循环该桶即第k个桶(即第k个一维数组),放入
                    for (int j = 0; j < bucketElementCount[k]; j++) {
                        //取出元素放入到arr中
                        arr[index++] = bucket[k][j];
                        //第一轮处理后,需要将每个bucketElementCount[k] = 0
                    }
                    // 重置所有桶的计数为0
                    bucketElementCount[k] = 0;
                }
            }

            System.out.println("第"+(i+1)+"轮的排序处理 arr =" + Arrays.toString(arr));
        }
}

(8)排序总结

0、排序算法说明
0.1 排序的定义
对一序列对象根据某个关键字进行排序。

0.2 术语说明
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
0.3 算法总结

图片名词解释:
n: 数据规模
k: “桶”的个数
In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存

在这里插入图片描述

7、查找算法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值