zoukankan      html  css  js  c++  java
  • 数据结构与算法

    数据结构与算法

    数据结构包括:线性结构【数组、队列、链表、栈】+非线性结构

    稀疏数组

    应用场景

    棋盘、地图 ===》二维数组的压缩

    • 举例

    demo

    package com.summer.datastructure;
    
    /**
     * 稀疏数组
     */
    public class SparseArray {
        public static void main(String[] args) {
            //创建一个原始的二维数组  8*8
            // 0表示没有棋子 1:表示黑子  2:表示白子
            int[][] chessArray = new int[8][8];
            chessArray[1][2] = 1;
            chessArray[2][3] = 2;
            chessArray[4][5] = 2;
            System.out.println("原始的二维数组");
            for (int[] row :chessArray){
                for (int i : row) {
                    System.out.print(i+" ");
                }
                System.out.println();
            }
            //将二位数组 转 稀疏数组的思想
            //1、将二维数组遍历得到 非零 数 的个数
            int sum = 0;
            for (int[] row : chessArray){
                for (int i : row) {
                    if (i != 0){
                        sum++;
                    }
                }
            }
            //2、创建对应的稀疏数组
            int[][] sparseArray = new int[sum+1][3];
            sparseArray[0][0] = 8;
            sparseArray[0][1] = 8;
            sparseArray[0][2] = sum;
            //遍历二维数组 将非零值 存放到稀疏数组
            for(int i= 0;i<8;i++){
                if (sum == 0){
                    break;
                }
                for (int j = 0; j < 8; j++) {
                    if (sum == 0){
                        break;
                    }
                    if (chessArray[i][j] != 0){
                            sparseArray[sum][0]=i;
                            sparseArray[sum][1]=j;
                            sparseArray[sum][2]=chessArray[i][j];
                            sum--;
                    }
                }
            }
            //转换后的稀疏数组
            System.out.println("转换为稀疏数组:");
            for (int[] row:sparseArray){
                for (int i:row){
                    System.out.print(i+" ");
                }
                System.out.println();
            }
    
            //将稀疏数组转换为二维数组
            int[][] chessArrayCopy = new int[sparseArray[0][0]][sparseArray[0][1]];
            for (int i = 1; i<=sparseArray[0][2];i++){
                    chessArrayCopy[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
            }
            System.out.println("稀疏数组恢复为二维数组:");
            for (int[] ints : chessArrayCopy) {
                for (int anInt : ints) {
                    System.out.print(anInt+" ");
                }
                System.out.println();
            }
        }
    }
    
    

    结果:

    原始的二维数组
    0 0 0 0 0 0 0 0 
    0 0 1 0 0 0 0 0 
    0 0 0 2 0 0 0 0 
    0 0 0 0 0 0 0 0 
    0 0 0 0 0 2 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 
    转换为稀疏数组:
    8 8 3 
    4 5 2 
    2 3 2 
    1 2 1 
    稀疏数组恢复为二维数组:
    0 0 0 0 0 0 0 0 
    0 0 1 0 0 0 0 0 
    0 0 0 2 0 0 0 0 
    0 0 0 0 0 0 0 0 
    0 0 0 0 0 2 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 
    

    练习

    要求:【主要是IO操作】
    1、在前面的基础上,将稀疏数组保存到磁盘上,比如map.data
    2、恢复原来的数组时,读取map.data进行恢复【模拟存棋盘读棋盘】
    

    队列

    介绍

    • 应用场景 ------》银行排队叫号系统

    • 队列是一个有序列表,可以用数组或是链表来实现。

    • 遵循先入先出的原则,即:先存入队列的数据,要先取出。后存入的要后取出。

    数组模拟队列

    • 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图所示,其中maxSize是该队列的最大容量
    • 因为队列的输出、输入是分别从前后端来处理的,因此需要两个变量front及rear分别记录队列的前后端的下标,front会随着数据输出而改变,rear则会随着数据输入而改变。

    • 加入队列addQueue
      • 将尾指针往后移:rear+1,当front==rear【队列空的】
      • 若尾指针rear小于队列的最大下标maxSize-1,则数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize -1 【队列满了】

    demo

    package com.summer.datastructure.queue;
    
    import java.util.Scanner;
    
    public class ArrayQueueDemo {
        public static void main(String[] args) {
            ArrayQueue arrayQueue = new ArrayQueue(3);
            Scanner scanner = new Scanner(System.in);
            char key = ' ';//用户输入
            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':
                        arrayQueue.show();
                        break;
                    case 'e':
                        scanner.close();//关闭
                        loop=false;
                        break;
                    case 'a':
                        System.out.println("请输入一个数字");
                        int n = scanner.nextInt();
                        arrayQueue.addQueue(n);
                        break;
                    case 'g':
                        try {
                            System.out.println("取出的数据是:"+arrayQueue.getQueue());
                        } catch (Exception e) {
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h':
                        try {
                            System.out.println("队列头数据是:"+arrayQueue.headQueue());
                        } catch (Exception e) {
                            System.out.println(e.getMessage());
                        }
                        break;
                    default:
                        System.out.println("输入无效信息");
                        break;
                }
            }
            System.out.println("程序退出");
        }
    }
    
    //使用数组模拟队列---编写一个ArrayQueue的类
    class ArrayQueue{
        private int maxSize;//数组最大容量
        private int front;//队列头
        private int rear;//队列尾
        private int[] arr;//该数组用于存放数据,模拟队列
    
        //创建队列的构造器
        public ArrayQueue(int arrMaxSize){
            maxSize = arrMaxSize;
            arr = new int[maxSize];
            front = -1;//指向队列头部,分析出front是指向队列头的前位置
            rear = -1;//指向队列的尾,指向队列尾的数据【即就是队列的最后一个数据】
        }
    
        public boolean isFull(){
            return  rear == maxSize-1;
        }
    
        public boolean isEmpty(){
            return front == rear;
        }
    
        //入队列
        public void addQueue(int n){
            if (isFull()){
                System.out.println("队列已满,不能加入数据");
                return;
            }
            arr[++rear] = n;
        }
    
        //出队列
        public int getQueue(){
            if (isEmpty()){
                System.out.println("队列为空,无数据");
                throw  new  RuntimeException("队列空,不能取数据");
            }
            return arr[++front];//由于队列头指向数据前一个位置,所以要++ 取数据
        }
    
        //显示队列所有数据
        public void show(){
            if (isEmpty()){
                System.out.println("队列为空,没有数据");
                return;
            }
            for (int i = 0; i < arr.length; i++) {
                System.out.printf("arr[%d] = %d",i,arr[i]);
                System.out.println();
            }
        }
    
        //显示队列的头的数是谁,注意不是取数据
        public int headQueue(){
            if (isEmpty()){
                System.out.println("队列为空,无数据");
                throw  new RuntimeException("队列空,不能取数据");
            }
            return arr[front+1];
        }
    
    }
    
    

    结果:

    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    队列为空,没有数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0] = 10
    arr[1] = 20
    arr[2] = 0
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0] = 10
    arr[1] = 20
    arr[2] = 30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    40
    队列已满,不能加入数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    h
    队列头数据是:10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    h
    队列头数据是:20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0] = 10
    arr[1] = 20
    arr[2] = 30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    队列为空,无数据
    队列空,不能取数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    199
    队列已满,不能加入数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    

    发现问题

    • 目前数组使用一次就不能用了,没有达到复用的效果。
    • 改进:使用算法将这个数组改成一个环形的数组,核心是用取模 %

    数组环形队列

    更新如下[环形队列数组]

    1、front变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素【front的初始默认为0】

    2、rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定【rear初始值为0】

    3、当队列满时,(rear + 1)% maxSize == front【满】

    4、当队列空时,rear == front【空】

    5、当我们这样分析,队列红有效的数据个数 (rear + maxSize -front) % maxSize

    demo

    package com.summer.datastructure.queue;
    
    import java.util.Scanner;
    
    public class CircleArrayQueueDemo {
        public static void main(String[] args) {
            System.out.println("测试数组模拟环形队列");
            CircleArray circleArray = new CircleArray(4);//其队列的有效数据最大是3
            Scanner scanner = new Scanner(System.in);
            char key = ' ';//用户输入
            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':
                        circleArray.show();
                        break;
                    case 'e':
                        scanner.close();//关闭
                        loop=false;
                        break;
                    case 'a':
                        System.out.println("请输入一个数字");
                        int n = scanner.nextInt();
                        circleArray.addQueue(n);
                        break;
                    case 'g':
                        try {
                            System.out.println("取出的数据是:"+circleArray.getQueue());
                        } catch (Exception e) {
                            System.out.println(e.getMessage());
                        }
                        break;
                    case 'h':
                        try {
                            System.out.println("队列头数据是:"+circleArray.headQueue());
                        } catch (Exception e) {
                            System.out.println(e.getMessage());
                        }
                        break;
                    default:
                        System.out.println("输入无效信息");
                        break;
                }
            }
            System.out.println("程序退出");
         }
        }
    
    class CircleArray{
        private int maxSize;//数组最大容量
        //front变量的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素【front的初始默认为0】
        private int front;
        //rear变量的含义做一个调整:rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间作为约定【rear初始值为0】
        private int rear;
        private int[] arr;//该数组用于存放数据,模拟队列
    
        //创建队列的构造器
        public CircleArray(int arrMaxSize){
            maxSize = arrMaxSize;
            arr = new int[maxSize];
    //        front = 0;
    //        rear =  0;
        }
    
        public boolean isFull(){
            return  (rear + 1) % maxSize == front;
        }
    
        public boolean isEmpty(){
            return front == rear;
        }
    
        //入队列
        public void addQueue(int n){
            if (isFull()){
                System.out.println("队列已满,不能加入数据");
                return;
            }
            arr[rear] = n;
            //将rear后移,这里必须考虑取模
            rear = ++rear % maxSize;
        }
    
        //出队列
        public int getQueue(){
            if (isEmpty()){
                System.out.println("队列为空,无数据");
                throw  new  RuntimeException("队列空,不能取数据");
            }
            //这里分析出front是指向队列的第一个元素
            //1、先把front对应的值保存到一个临时变量
            //2、将front后移
            //3、将临时保存的变量返回
            int val = arr[front];
            front =++front % maxSize;
            return val;
        }
    
        //显示队列所有数据
        public void show(){
            if (isEmpty()){
                System.out.println("队列为空,没有数据");
                return;
            }
            //思路:从front开始遍历,遍历多少个元素
            for (int i = front; i <front + size(); i++) {
                System.out.printf("arr[%d]=%d
    ",i%maxSize,arr[i%maxSize]);
            }
        }
        //求出当前队列有效数据的个数
        private int size(){
            //rear = 2
            //front = 1
            //maxSize = 3
            //size = 1
            return (rear+maxSize-front)%maxSize;
        }
    
        //显示队列的头的数是谁,注意不是取数据
        public int headQueue(){
            if (isEmpty()){
                System.out.println("队列为空,无数据");
                throw  new RuntimeException("队列空,不能取数据");
            }
            return arr[front];
        }
    
    }
    
    

    结果:

    测试数组模拟环形队列
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    队列为空,没有数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0]=10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0]=10
    arr[1]=20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0]=10
    arr[1]=20
    arr[2]=30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    40
    队列已满,不能加入数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:10
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[1]=20
    arr[2]=30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    40
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[1]=20
    arr[2]=30
    arr[3]=40
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    50
    队列已满,不能加入数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:20
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:30
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:40
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    队列为空,没有数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    队列为空,无数据
    队列空,不能取数据
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    50
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    60
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    70
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[0]=50
    arr[1]=60
    arr[2]=70
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:50
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[1]=60
    arr[2]=70
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    90
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[1]=60
    arr[2]=70
    arr[3]=90
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    g
    取出的数据是:60
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    a
    请输入一个数字
    100
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    s
    arr[2]=70
    arr[3]=90
    arr[0]=100
    s(show):查看队列
    e(exit):退出程序
    a(add):添加数据到队列
    g(get):从队列中取数据
    h(head):查看队列头的数据
    e
    程序退出
    

    链表

    介绍[Linked List]

    • 链表是有序列表,但他在内存中是存储如下

    小结:

    1、链表是以节点的方式来存储

    2、每个节点包含data域 和 next域:指向下一个节点

    3、如图:发现链表的各个节点不一定是连续存放的

    4、链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

    单向链表的创建/添加

    方法一:直接添加到链表尾部

    思路

    添加(创建):

    1、先创建一个head头节点,作用就是表示单链表的头

    2、后面我们每添加一个节点,就直接加入到链表的最后

    遍历:

    1、通过一个临时变量帮助遍历整个链表

    demo
    package com.summer.datastructure.linkedlist;
    
    /**
     * 单链表
     */
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            //测试:先创建几个节点
            HeroNode h1 = new HeroNode(1,"宋江","及时雨");
            HeroNode h2 = new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode h3 = new HeroNode(3,"吴用","智多星");
            HeroNode h4 = new HeroNode(4,"林冲","豹子头");
    
            SingleLinkedList singleLinkedList = new SingleLinkedList();
            singleLinkedList.add(h1);
            singleLinkedList.add(h4);
            singleLinkedList.add(h3);
            singleLinkedList.add(h2);
            singleLinkedList.show();
        }
    }
    
    /**
     * 定义SingleLinkedList 管理我们的英雄
     */
    class SingleLinkedList{
        //初始化一个头节点  头节点不要动 不存放具体的数据
        private HeroNode head = new HeroNode(0,"","");
    
        //添加节点到单向链表
        //思路:当不考虑编号的数据时
        //1、找到当前链表的最后节点
        //2、将最后这个节点的next 指向新的节点
        public void add(HeroNode heroNode){
            //因为head节点不能变,因此我们需要一个辅助变量temp
            HeroNode temp = head;
            //遍历链表,找到最后
            while (true){
                //找到链表的最后
                if(temp.next == null){
                    break;
                }
                temp = temp.next;
            }
            //当退出while循环时 temp就指向了链表的最后
            temp.next = heroNode;//将最后的节点指向新的节点
        }
    
        public void show(){
            //判断链表是否为空
            if (head.next == null){
                System.out.println("链表为空");
                return;
            }
            //因为头节点不能动 因此需要一个临时变量来遍历
            HeroNode temp = head.next;
            while(temp != null){
                System.out.println(temp);
                temp = temp.next;
            }
            System.out.println("显示结束");
        }
    }
    /**
     * 定义一个HeroNode  每个HeroNode 对象就是一个节点
     */
    class HeroNode{
        public int no;
        public String name;
        public String nickName;
        public HeroNode next;//指向下一个节点
    
        public HeroNode(int hNo,String hName,String hNickName){
            this.no = hNo;
            this.name = hName;
            this.nickName = hNickName;
        }
    
        @Override
        public String toString() {
            return "HeroNode [no = " +no+" , name = "+name+" , nickName = "+nickName+"]";
        }
    }
    

    结果:

    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 4 , name = 林冲 , nickName = 豹子头]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    显示结束
    

    方法二:根据排名添加到指定位置

    [添加时,如果有这个排名,则添加失败并给出提示]

    思路

    需要按照编号的顺序添加:

    1、首先找到新添加的节点的位置,是通过辅助变量找到的[指针],通过遍历搞定

    2、新的节点.next = temp.next

    3、将temp.next = 新节点

    demo
    package com.summer.datastructure.linkedlist;
    
    /**
     * 单链表
     */
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            //测试:先创建几个节点
            HeroNode h1 = new HeroNode(1,"宋江","及时雨");
            HeroNode h2 = new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode h3 = new HeroNode(3,"吴用","智多星");
            HeroNode h4 = new HeroNode(4,"林冲","豹子头");
    
            SingleLinkedList singleLinkedList = new SingleLinkedList();
    //        singleLinkedList.add(h1);
    //        singleLinkedList.add(h4);
    //        singleLinkedList.add(h3);
    //        singleLinkedList.add(h2);
    //        singleLinkedList.show();
    
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h4);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h2);
            singleLinkedList.show();
        }
    }
    
    /**
     * 定义SingleLinkedList 管理我们的英雄
     */
    class SingleLinkedList{
        //初始化一个头节点  头节点不要动 不存放具体的数据
        private HeroNode head = new HeroNode(0,"","");
    
        //添加节点到单向链表
        //思路:当不考虑编号的数据时
        //1、找到当前链表的最后节点
        //2、将最后这个节点的next 指向新的节点
        public void add(HeroNode heroNode){
            //因为head节点不能变,因此我们需要一个辅助变量temp
            HeroNode temp = head;
            //遍历链表,找到最后
            while (true){
                //找到链表的最后
                if(temp.next == null){
                    break;
                }
                temp = temp.next;
            }
            //当退出while循环时 temp就指向了链表的最后
            temp.next = heroNode;//将最后的节点指向新的节点
        }
    
        //第二种方式在添加英雄时,根据排名将英雄插入到指定位置
        //[添加时,如果有这个排名,则添加失败并给出提示]
        public void  addByOrder(HeroNode heroNode){
            //因为头节点不能动,因此我们需要一个辅助变量(指针)帮助我们找到添加的位置
            //因为单链表,所以我们找的temp是位于添加位置的前一个节点,否则插入不了
            HeroNode temp = head;
            boolean flag = false;//添加的编号是否存在 默认为false
            while (true){
                if (temp.next == null){
                    temp.next = heroNode;
                    break;
                }
                if(temp.next.no > heroNode.no){
                    heroNode.next = temp.next;
                    temp.next = heroNode;
                    break;
                }else if (temp.next.no == heroNode.no){
                    System.out.println("ERROR---编号已经存在了");
                    break;
                }
                temp = temp.next;
            }
        }
    
        public void show(){
            //判断链表是否为空
            if (head.next == null){
                System.out.println("链表为空");
                return;
            }
            //因为头节点不能动 因此需要一个临时变量来遍历
            HeroNode temp = head.next;
            while(temp != null){
                System.out.println(temp);
                temp = temp.next;
            }
            System.out.println("显示结束");
        }
    }
    /**
     * 定义一个HeroNode  每个HeroNode 对象就是一个节点
     */
    class HeroNode{
        public int no;
        public String name;
        public String nickName;
        public HeroNode next;//指向下一个节点
    
        public HeroNode(int hNo,String hName,String hNickName){
            this.no = hNo;
            this.name = hName;
            this.nickName = hNickName;
        }
    
        @Override
        public String toString() {
            return "HeroNode [no = " +no+" , name = "+name+" , nickName = "+nickName+"]";
        }
    
    
    }
    

    结果:

    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    HeroNode [no = 4 , name = 林冲 , nickName = 豹子头]
    显示结束
    
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            //测试:先创建几个节点
            HeroNode h1 = new HeroNode(1,"宋江","及时雨");
            HeroNode h2 = new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode h3 = new HeroNode(3,"吴用","智多星");
            HeroNode h4 = new HeroNode(4,"林冲","豹子头");
    
            SingleLinkedList singleLinkedList = new SingleLinkedList();
    //        singleLinkedList.add(h1);
    //        singleLinkedList.add(h4);
    //        singleLinkedList.add(h3);
    //        singleLinkedList.add(h2);
    //        singleLinkedList.show();
    
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h2);
            singleLinkedList.show();
        }
    }
    
    ERROR---编号已经存在了
    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    显示结束
    

    单向链表的更新

    demo

    主要看update方法

    package com.summer.datastructure.linkedlist;
    
    /**
     * 单链表
     */
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            //测试:先创建几个节点
            HeroNode h1 = new HeroNode(1,"宋江","及时雨");
            HeroNode h2 = new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode h3 = new HeroNode(3,"吴用","智多星");
            HeroNode h4 = new HeroNode(4,"林冲","豹子头");
    
            SingleLinkedList singleLinkedList = new SingleLinkedList();
    //        singleLinkedList.add(h1);
    //        singleLinkedList.add(h4);
    //        singleLinkedList.add(h3);
    //        singleLinkedList.add(h2);
    //        singleLinkedList.show();
    
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h2);
            singleLinkedList.show();
    
            System.out.println("------update------");
            singleLinkedList.update(new HeroNode(1,"宋江11","及时雨11"));
            singleLinkedList.show();
    
            System.out.println("------update------");
            singleLinkedList.update(new HeroNode(6,"Summer","come on"));
            singleLinkedList.show();
        }
    }
    
    /**
     * 定义SingleLinkedList 管理我们的英雄
     */
    class SingleLinkedList{
        //初始化一个头节点  头节点不要动 不存放具体的数据
        private HeroNode head = new HeroNode(0,"","");
    
        //添加节点到单向链表
        //思路:当不考虑编号的数据时
        //1、找到当前链表的最后节点
        //2、将最后这个节点的next 指向新的节点
        public void add(HeroNode heroNode){
            //因为head节点不能变,因此我们需要一个辅助变量temp
            HeroNode temp = head;
            //遍历链表,找到最后
            while (true){
                //找到链表的最后
                if(temp.next == null){
                    break;
                }
                temp = temp.next;
            }
            //当退出while循环时 temp就指向了链表的最后
            temp.next = heroNode;//将最后的节点指向新的节点
        }
    
        //第二种方式在添加英雄时,根据排名将英雄插入到指定位置
        //[添加时,如果有这个排名,则添加失败并给出提示]
        public void  addByOrder(HeroNode heroNode){
            //因为头节点不能动,因此我们需要一个辅助变量(指针)帮助我们找到添加的位置
            //因为单链表,所以我们找的temp是位于添加位置的前一个节点,否则插入不了
            HeroNode temp = head;
            boolean flag = false;//添加的编号是否存在 默认为false
            while (true){
                if (temp.next == null){
                    temp.next = heroNode;
                    break;
                }
                if(temp.next.no > heroNode.no){
                    heroNode.next = temp.next;
                    temp.next = heroNode;
                    break;
                }else if (temp.next.no == heroNode.no){
                    System.out.println("ERROR---编号已经存在了");
                    break;
                }
                temp = temp.next;
            }
        }
    //修改节点的信息,根据no编号来修改,即no编号不能改
        //说明:
        //根据newHeroNode 的 no  来修改即可
        public void update(HeroNode newHeroNode){
            boolean flag = false;
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
            //先定义一个辅助变量
            HeroNode temp = head;
            while(temp.next != null){
                if (temp.no == newHeroNode.no){
                    temp.nickName = newHeroNode.nickName;
                    temp.name = newHeroNode.name;
                    flag = true;
                    break;
                }
                temp = temp.next;
            }
            if (!flag){
                System.out.println("链表中没有对应no的数据");
            }
        }
    
    
    
        public void show(){
            //判断链表是否为空
            if (head.next == null){
                System.out.println("链表为空");
                return;
            }
            //因为头节点不能动 因此需要一个临时变量来遍历
            HeroNode temp = head.next;
            while(temp != null){
                System.out.println(temp);
                temp = temp.next;
            }
            System.out.println("显示结束");
        }
    }
    /**
     * 定义一个HeroNode  每个HeroNode 对象就是一个节点
     */
    class HeroNode{
        public int no;
        public String name;
        public String nickName;
        public HeroNode next;//指向下一个节点
    
        public HeroNode(int hNo,String hName,String hNickName){
            this.no = hNo;
            this.name = hName;
            this.nickName = hNickName;
        }
    
        @Override
        public String toString() {
            return "HeroNode [no = " +no+" , name = "+name+" , nickName = "+nickName+"]";
        }
    }
    
    ERROR---编号已经存在了
    ERROR---编号已经存在了
    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    显示结束
    ------update------
    HeroNode [no = 1 , name = 宋江11 , nickName = 及时雨11]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    显示结束
    ------update------
    链表中没有对应no的数据
    HeroNode [no = 1 , name = 宋江11 , nickName = 及时雨11]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    显示结束
    
    Process finished with exit code 0
    

    单向链表的删除

    demo

    主要看del方法

    package com.summer.datastructure.linkedlist;
    
    /**
     * 单链表
     */
    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            //测试:先创建几个节点
            HeroNode h1 = new HeroNode(1,"宋江","及时雨");
            HeroNode h2 = new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode h3 = new HeroNode(3,"吴用","智多星");
            HeroNode h4 = new HeroNode(4,"林冲","豹子头");
    
            SingleLinkedList singleLinkedList = new SingleLinkedList();
    //        singleLinkedList.add(h1);
    //        singleLinkedList.add(h4);
    //        singleLinkedList.add(h3);
    //        singleLinkedList.add(h2);
    //        singleLinkedList.show();
    
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h1);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h3);
            singleLinkedList.addByOrder(h2);
            singleLinkedList.show();
    
            System.out.println("------update------");
            singleLinkedList.update(new HeroNode(1,"宋江11","及时雨11"));
            singleLinkedList.show();
    
            System.out.println("------update------");
            singleLinkedList.update(new HeroNode(6,"Summer","come on"));
            singleLinkedList.show();
    
            System.out.println("------delete------");
            singleLinkedList.del(1);
            singleLinkedList.show();
            singleLinkedList.del(3);
            singleLinkedList.show();
            singleLinkedList.del(3);
            singleLinkedList.show();
            singleLinkedList.del(2);
            singleLinkedList.show();
            singleLinkedList.del(2);
            singleLinkedList.show();
        }
    }
    
    /**
     * 定义SingleLinkedList 管理我们的英雄
     */
    class SingleLinkedList{
        //初始化一个头节点  头节点不要动 不存放具体的数据
        private HeroNode head = new HeroNode(0,"","");
    
        //添加节点到单向链表
        //思路:当不考虑编号的数据时
        //1、找到当前链表的最后节点
        //2、将最后这个节点的next 指向新的节点
        public void add(HeroNode heroNode){
            //因为head节点不能变,因此我们需要一个辅助变量temp
            HeroNode temp = head;
            //遍历链表,找到最后
            while (true){
                //找到链表的最后
                if(temp.next == null){
                    break;
                }
                temp = temp.next;
            }
            //当退出while循环时 temp就指向了链表的最后
            temp.next = heroNode;//将最后的节点指向新的节点
        }
    
        //第二种方式在添加英雄时,根据排名将英雄插入到指定位置
        //[添加时,如果有这个排名,则添加失败并给出提示]
        public void  addByOrder(HeroNode heroNode){
            //因为头节点不能动,因此我们需要一个辅助变量(指针)帮助我们找到添加的位置
            //因为单链表,所以我们找的temp是位于添加位置的前一个节点,否则插入不了
            HeroNode temp = head;
            boolean flag = false;//添加的编号是否存在 默认为false
            while (true){
                if (temp.next == null){
                    temp.next = heroNode;
                    break;
                }
                if(temp.next.no > heroNode.no){
                    heroNode.next = temp.next;
                    temp.next = heroNode;
                    break;
                }else if (temp.next.no == heroNode.no){
                    System.out.println("ERROR---编号已经存在了");
                    break;
                }
                temp = temp.next;
            }
        }
    //修改节点的信息,根据no编号来修改,即no编号不能改
        //说明:
        //根据newHeroNode 的 no  来修改即可
        public void update(HeroNode newHeroNode){
            boolean flag = false;
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
            //先定义一个辅助变量
            HeroNode temp = head;
            while(temp.next != null){
                if (temp.no == newHeroNode.no){
                    temp.nickName = newHeroNode.nickName;
                    temp.name = newHeroNode.name;
                    flag = true;
                    break;
                }
                temp = temp.next;
            }
            if (!flag){
                System.out.println("链表中没有对应no的数据");
            }
        }
    
        //删除节点
        //思路
        //1、head节点不能动 因此我们需要一个temp辅助节点找到待删除节点的前一个节点
        //2、说明我们在比较时,是temp.next.no 和 需要删除的节点的no比较
        public void del(int no){
            HeroNode temp = head;
            while (temp.next != null){
                if (temp.next.no == no){
                    temp.next = temp.next.next;
                    return;
                }
                temp = temp.next;
            }
        }
    
    
        public void show(){
            //判断链表是否为空
            if (head.next == null){
                System.out.println("链表为空");
                return;
            }
            //因为头节点不能动 因此需要一个临时变量来遍历
            HeroNode temp = head.next;
            while(temp != null){
                System.out.println(temp);
                temp = temp.next;
            }
            System.out.println("显示结束");
        }
    }
    /**
     * 定义一个HeroNode  每个HeroNode 对象就是一个节点
     */
    class HeroNode{
        public int no;
        public String name;
        public String nickName;
        public HeroNode next;//指向下一个节点
    
        public HeroNode(int hNo,String hName,String hNickName){
            this.no = hNo;
            this.name = hName;
            this.nickName = hNickName;
        }
    
        @Override
        public String toString() {
            return "HeroNode [no = " +no+" , name = "+name+" , nickName = "+nickName+"]";
        }
    }
    
    ------delete------
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    显示结束
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    显示结束
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    显示结束
    链表为空
    链表为空
    

    单链表面试题(新浪百度腾讯)

    单链表的常见面试题有如下:

    1、求单链表中有效节点的个数【遍历一遍链表即可】

        //遍历单链表
    public  int linkedLength () {
            HeroNode temp = head;
            int i = 0;
            while (temp.next != null){
                 i++;
                 temp = temp.next;
            }
            return i;
        }
    

    2、查找单链表中倒数第K个节点【新浪】

    //思路:先求出链表长度 然后倒数改为正着数遍历
        public HeroNode index(int k){
            HeroNode temp = head;
            if (k>linkedLength() || k<=0){
                throw new RuntimeException("参数输入异常");
            }
            int i = linkedLength() + 1 - k;
            for (int i1 = 0; i1 < i; i1++) {
                temp = temp.next;
            }
            return temp;
        }
    

    3、单链表的反转【腾讯】

    //思路
    //1、先定义一个节点reverseHead = new HeroNode()
    //2、从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表的reverseHead的最前端
    //3、原来的链表的head.next = reverseHead.next
    

    4、从尾到头打印单链表【百度:要求方式1:反向遍历 方式2:Stack栈】

    5、合并两个有序的单链表,合并之后的链表依然有序【课后练习】

    单向链表的缺点

    • 单项链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。
    • 单项链表不能自我删除,需要靠辅助节点,而双向链表则可以自我删除,所以单向链表删除时节点,总是找到temp,temp是待删除节点的前一个节点来删除的。

    双向链表

    双向链表增删查改

    demo

    package com.summer.datastructure.linkedlist;
    
    public class DoubleLinkedListDemo {
        public static void main(String[] args) {
            System.out.println("双向链表的测试");
            HeroNode2 h1 = new HeroNode2(1,"宋江","及时雨");
            HeroNode2 h2 = new HeroNode2(2,"卢俊义","玉麒麟");
            HeroNode2 h3 = new HeroNode2(3,"吴用","智多星");
            HeroNode2 h4 = new HeroNode2(4,"林冲","豹子头");
    
            DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
            doubleLinkedList.add(h1);
            doubleLinkedList.add(h2);
            doubleLinkedList.add(h3);
            doubleLinkedList.add(h4);
    
            doubleLinkedList.list();
            //修改
            HeroNode2 newHeroNode2 = new HeroNode2(4,"公孙胜","入云龙");
            doubleLinkedList.update(newHeroNode2);
            System.out.println("修改过后的双向链表情况");
            doubleLinkedList.list();
            doubleLinkedList.del(3);
            doubleLinkedList.del(4);
            System.out.println("删除后双向链表情况");
            doubleLinkedList.list();
        }
    }
    //创建一个双向链表的类
    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;
            while (temp.next != null){
                System.out.println(temp.next);
                temp = temp.next;
            }
        }
    
        //添加
        public void add(HeroNode2 heroNode2){
            HeroNode2 temp = head;
            while (temp.next != null){
                temp = temp.next;
            }
            //星辰
            temp.next = heroNode2;
            heroNode2.pre = temp;
        }
        //修改【可以看到双向链表的节点内容修改跟单向链表一样】
        //只是节点的类型改成了HeroNode2
        public void update(HeroNode2 heroNode2){
            HeroNode2 temp = head;
            while(temp != null){
                if (temp.no == heroNode2.no){
                    temp.nickName = heroNode2.nickName;
                    temp.name = heroNode2.name;
                    return;
                }
                temp = temp.next;
            }
            System.out.println("链表中没有对应no的数据");
        }
        //从双向链表中删除节点
        public void del(int id){
            HeroNode2 temp = head;
            while (temp != null){
                if (temp.no == id){
                    temp.pre.next = temp.next;
                    //如果是最后一个节点 temp.next.pre会报空指针
                    if (temp.next!= null){
                        temp.next.pre = temp.pre;
                    }
                    return;
                }
                temp = temp.next;
            }
            System.out.println("没有对应id的节点");
        }
    
    }
    
    /**
     * 定义一个HeroNode  每个HeroNode 对象就是一个节点
     */
    class HeroNode2{
        public int no;
        public String name;
        public String nickName;
        public HeroNode2 next;//指向下一个节点 默认为null
        public HeroNode2 pre;//指向前一个节点 默认为null
    
        public HeroNode2(int hNo,String hName,String hNickName){
            this.no = hNo;
            this.name = hName;
            this.nickName = hNickName;
        }
    
        @Override
        public String toString() {
            return "HeroNode [no = " +no+" , name = "+name+" , nickName = "+nickName+"]";
        }
    }
    

    结果:

    双向链表的测试
    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    HeroNode [no = 4 , name = 林冲 , nickName = 豹子头]
    修改过后的双向链表情况
    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    HeroNode [no = 3 , name = 吴用 , nickName = 智多星]
    HeroNode [no = 4 , name = 公孙胜 , nickName = 入云龙]
    删除后双向链表情况
    HeroNode [no = 1 , name = 宋江 , nickName = 及时雨]
    HeroNode [no = 2 , name = 卢俊义 , nickName = 玉麒麟]
    
    Process finished with exit code 0
    

    单向环形链表的应用场景

    【约瑟夫、约瑟夫环】问题

    设定编号为1,2,.....n 的 n 个人围坐一圈,约定编号为 k (1<=k<=n) 的人从1开始报数,数到 m 的那个人出列,它的下一个人又开始从1报数,数到 m 的那个人 又出列,一次类推,直到所有人出列为止,由此产生一个出队编号的序列。

    提示:

    用一个不带头节点的循环链表来处理约瑟夫问题,先构成一个有 n 个节点的单循环链表,然后由 k 节点起从1开始计数,计到 m 时,对应节点从链表中删除,然后被删除的节点的下一个节点又从1开始计数,直到最后一个节点从链表中删除算法结束。

    步骤一:

    构建一个单向的环形链表思路:

    1、先创建第一个节点,让first指向该节点,并形成环形

    2、后面我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可

    遍历环形链表:

    1、先让一个辅助指针指向first节点

    2、然后通过一个while循环遍历该环形链表即可 current.next == first 遍历结束

    步骤二:

    根据用户输入,生成一个小孩出圈的顺序

    1、需要创建一个辅助指针helper,事先应该指向环形链表的最后这个节点

    2、小孩报数前先让first和helper移动到k位置处

    3、当小孩报数时,让first和helper同时移动m-1次(因为小孩自己也要报数)

    4、这时就可以将first指向的小孩节点出圈

    ​ helper.next = first.next

    ​ first = first.next

    ​ 原来first指向的节点就没有任何引用,就会被垃圾回收机制回收

    demo

    package com.summer.datastructure.linkedlist;
    
    public class Josepfu {
        public static void main(String[] args) {
            CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
            circleSingleLinkedList.addBoy(5);
            circleSingleLinkedList.list();
            circleSingleLinkedList.countBoy(1,2,5);
        }
    }
    
    //创建一个环形的单向链表
    class CircleSingleLinkedList{
        //创建一个first节点;
        private Boy first;
        //添加小孩节点
        public void addBoy(int nums){
            if (nums<1){
                System.out.println("nums 的值不正确");
                return;
            }
            Boy curBoy = null;//辅助指针,帮助构建环形链表
            for (int i = 1; i <= nums; i++) {
                //根据编号创建Boy节点
                Boy boy = new Boy(i);
                //如果是第一个小孩
                if (i==1){
                    first = boy;
                    first.setNext(first);//构成环
                    curBoy = first;
                }else {
                    curBoy.setNext(boy);
                    curBoy = boy;
                    boy.setNext(first);
                }
            }
        }
    
        //遍历单向环形链表
        public void list(){
            if (first == null){
                System.out.println("单向环形链表为空");
                return;
            }
            Boy temp = first;
            while (temp != null){
                System.out.println("编号:---"+temp.getNo());
                if (temp.getNext() == first){
                    break;
                }
                temp = temp.getNext();
            }
        }
    
        /**
         *  根据用户的输入 小孩出圈的顺序
         * @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;
            }
    
            //先移动到要报数的位置
            for (int i = 0; i < startNo-1; i++) {
                first = first.getNext();
            }
    
            Boy helper = first;
            //事先让helper指向first后面
            while(helper != null){
                if (helper.getNext() == first){//说明helper指向了最后的节点
                    break;
                }
                helper = helper.getNext();
            }
    
            //循环操作 让小孩出圈 直到圈中只有一个节点
            while (true){
                //只有一个节点
                if (first == helper){
                    System.out.println("编号【"+first.getNo()+"】小孩 出圈");
                    System.out.println("----game over----");
                    break;
                }
                //移动m-1
                for (int i = 0; i < countNum-1; i++) {
                    first = first.getNext();
                    helper = helper.getNext();
                }
                System.out.printf("编号【%d】小孩 出圈
    ",first.getNo());
                //出圈
                first = first.getNext();
                helper.setNext(first);
            }
        }
    }
    
    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;
        }
    }
    

    结果:

    编号:---1
    编号:---2
    编号:---3
    编号:---4
    编号:---5
    编号【2】小孩 出圈
    编号【4】小孩 出圈
    编号【1】小孩 出圈
    编号【5】小孩 出圈
    编号【3】小孩 出圈
    ----game over----
    

    栈stack

    介绍

    • 栈是一个先入后出的有序列表
    • 栈是限制线性表中元素的插入和删除 只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)。
    • 根据栈的定义可知,最先放入栈中的元素在栈底,最后放入元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。

    栈的应用场景

    • 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
    • 处理递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
    • 表达式的转换 (中缀表达式转后缀表达式) 与求值(实际解决)
    • 二叉树的遍历
    • 图形的深度优先(depth-first)搜索法。

    数组模拟栈

    数组模拟栈的思路分析图

    1、使用数组来模拟栈

    2、定义一个top来表示栈顶,初始化为 -1

    3、入栈的操作,当有数据加入到栈时,top++,stack[top] = data;

    4、出栈的操作, int value = stack[top]; top-- ;return value;

    demo

    package com.summer.datastructure.linkedlist;
    
    public class Josepfu {
        public static void main(String[] args) {
            CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
            circleSingleLinkedList.addBoy(5);
            circleSingleLinkedList.list();
            circleSingleLinkedList.countBoy(1,2,5);
        }
    }
    
    //创建一个环形的单向链表
    class CircleSingleLinkedList{
        //创建一个first节点;
        private Boy first;
        //添加小孩节点
        public void addBoy(int nums){
            if (nums<1){
                System.out.println("nums 的值不正确");
                return;
            }
            Boy curBoy = null;//辅助指针,帮助构建环形链表
            for (int i = 1; i <= nums; i++) {
                //根据编号创建Boy节点
                Boy boy = new Boy(i);
                //如果是第一个小孩
                if (i==1){
                    first = boy;
                    first.setNext(first);//构成环
                    curBoy = first;
                }else {
                    curBoy.setNext(boy);
                    curBoy = boy;
                    boy.setNext(first);
                }
            }
        }
    
        //遍历单向环形链表
        public void list(){
            if (first == null){
                System.out.println("单向环形链表为空");
                return;
            }
            Boy temp = first;
            while (temp != null){
                System.out.println("编号:---"+temp.getNo());
                if (temp.getNext() == first){
                    break;
                }
                temp = temp.getNext();
            }
        }
    
        /**
         *  根据用户的输入 小孩出圈的顺序
         * @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;
            }
    
            //先移动到要报数的位置
            for (int i = 0; i < startNo-1; i++) {
                first = first.getNext();
            }
    
            Boy helper = first;
            //事先让helper指向first后面
            while(helper != null){
                if (helper.getNext() == first){//说明helper指向了最后的节点
                    break;
                }
                helper = helper.getNext();
            }
    
            //循环操作 让小孩出圈 直到圈中只有一个节点
            while (true){
                //只有一个节点
                if (first == helper){
                    System.out.println("编号【"+first.getNo()+"】小孩 出圈");
                    System.out.println("----game over----");
                    break;
                }
                //移动m-1
                for (int i = 0; i < countNum-1; i++) {
                    first = first.getNext();
                    helper = helper.getNext();
                }
                System.out.printf("编号【%d】小孩 出圈
    ",first.getNo());
                //出圈
                first = first.getNext();
                helper.setNext(first);
            }
        }
    }
    
    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;
        }
    }
    
    

    结果

    请出入你的选择
    show
    stack---index【2】data【30】
    stack---index【1】data【20】
    stack---index【0】data【10】
    show:表示显示栈
    exit:程序退出
    push:表示入栈
    pop:表示出栈
    请出入你的选择
    pop
    出栈
    30
    show:表示显示栈
    exit:程序退出
    push:表示入栈
    pop:表示出栈
    请出入你的选择
    pop
    出栈
    20
    show:表示显示栈
    exit:程序退出
    push:表示入栈
    pop:表示出栈
    请出入你的选择
    pop
    出栈
    10
    show:表示显示栈
    exit:程序退出
    push:表示入栈
    pop:表示出栈
    请出入你的选择
    pop
    出栈
    栈空
    Exception in thread "main" java.lang.RuntimeException: 栈空,没有数据
    	at com.summer.datastructure.stack.ArrayStack.pop(ArrayStackDemo.java:76)
    	at com.summer.datastructure.stack.ArrayStackDemo.main(ArrayStackDemo.java:29)
    
    Process finished with exit code 1
    
    

    栈实现综合计算器

    使用栈完成表达式的计算思路
    1、通过一个index 值 [索引],用来遍历我们的表达式
    2、如果我们发现是一个数字,就直接入数栈
    3、如果发现扫描到的是一个符号,就分如下情况
    3.1 如果发现当前的符号栈为空,就直接入栈
    3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先 级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到的结果,入数栈,然后将当前的操作符如符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈
    4、当表达式扫描完成,就顺序的从数栈和符号栈中pop出相应的数和符号,并运算
    5、最后在数栈只有一个数字,就是表达式的结果

    demo

    package com.summer.datastructure.stack;
    
    public class Calculator {
        public static void main(String[] args) {
    //        String expression = "3+2*6-2";
            String expression = "30+20*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 = ' ';//将每次扫描得到的char保存到ch
            String keepNum = "";//用于拼接多位数的
            while (true){
                if (index == expression.length()){
                    while (!operStack.isEmpty()){
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        int cal = numStack.cal(num1, num2, operStack.pop());
                        numStack.push(cal);
                 }
                    break;
                }
                //依次得到expression的每一个字符
                ch = expression.substring(index,index+1).charAt(0);
                if (operStack.isOper(ch)){//如果是运算符
                    if (operStack.isEmpty()){
                        operStack.push(ch);
                    }else {
                        int curOperPriority = operStack.priority(ch);
                        if (curOperPriority >= operStack.priority(operStack.peek())){
                            operStack.push(ch);
                        }else {
                            num1 = numStack.pop();
                            num2 = numStack.pop();
                            res =  numStack.cal(num1,num2,operStack.pop());
                            numStack.push(res);
                            operStack.push(ch);
                        }
                    }
                }else {
    //                numStack.push(ch -48);
                    //分析思路
                    //1、当初处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数
                    //2、在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈
                    //3、因此我们需要定义一个变量字符串 用于拼接
                    //处理多位数
                    keepNum +=ch;
                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入数栈
    
                    //如果ch已经是expression最后一位 直接入栈
                    if (index == expression.length() -1){
                        numStack.push(Integer.parseInt(keepNum));
                    }else {
                        if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))){// index 不要变只是往后看一位
                            //后一位是运算符 则入栈
                            numStack.push(Integer.parseInt(keepNum));
                            //!!!清空keepnum
                            keepNum = "";
                        }
                    }
                }
                index++;
            }
            System.out.printf("%s = %d 
    ",expression,numStack.pop());
        }
    }
    
    
    //定义一个数组栈  需要扩展功能
    class ArrayStack2{
        private int maxSize;//栈的大小
        private int[] stack;//数组,数组模拟栈,数据就放在该数组中
        private int top = -1;//top表示栈顶,初始化为-1
    
        public int getTop() {
            return top;
        }
    
        //构造器
        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 void push(int data){
            if (isFull()){
                System.out.println("栈满");
                return;
            }
            top++;
            stack[top] = data;
        }
    
    
        public int pop(){
            if (isEmpty()){
                System.out.println("栈空");
                throw new RuntimeException("栈空,没有数据");//运行异常 不捕获也能抛出
            }
            int temp = stack[top];
            top--;
            return temp;
        }
        //遍历栈的时候 要从栈顶开始遍历
        public void list(){
            if (isEmpty()){
                System.out.println("栈空,没有数据");
                return;
            }
            for (int i = top; i >= 0; i--) {
                System.out.printf("stack---index【%d】data【%d】
    ",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(char val){
            return val == '-' || val == '+' || val == '*' || val == '/';
        }
    
        //计算方法
        public int cal(int num1,int num2,int oper){ ;
            int result = 0;
            if (oper == '+'){
                result =num1 + num2;
            }else if (oper == '-'){
                result = num2 - num1;//注意顺序
            }else if (oper == '*'){
                result = num1 * num2;
            }else if (oper == '/'){
                result = num2 / num1;//注意顺序
            }
            return result;
        }
    
        public int peek(){
            return stack[top];
        }
    
        public void reverse(){
            int[] temp = new int[10];
            for (int i = 0; i <= top; i++) {
                temp[i] = stack[top - i];
            }
            for (int i = 0; i <= top; i++) {
                stack[i] = temp[i];
            }
        }
    }
    

    结果

    30+20*6-2 = 148 
    
    Process finished with exit code 0
    

    前缀、中缀、后缀表达式

    前缀表达式

    • 前缀表达式又称波兰表达式,前缀表达式的运算符位于操作数之前

      e.g.举例说明 (3+4)*5-6 对应的前缀表达式就是 - * + 3 4 5 6

    前缀表达式的计算机求值过程

    从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式的最左端,最后运算得出的值即为表达式的结果

    例如:(3+4)*5-6 对应的前缀表达式就是 - * + 3 4 5 6 针对前缀表达式求值步骤如下:

    1、从右至左扫描,将6 5 4 3压入堆栈

    2、遇到 + 运算符,因此弹出3 4 3+4=7 7入栈

    3、接下来* 7*5=35 35入栈

    4、接下来- 35-6=29 即最终结果为29

    中缀表达式

    • 中缀表达式就是常见的运算表达式,如(3+4)*5 -6
    • 中缀表达式的求值是我们人最熟悉的,但是对于计算机来说却不好操作(前面的那个计算机demo可以看出来这个问题哦),因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作(一般转成后缀表达式)

    后缀表达式

    • 后缀表达式又称逆波兰表达式与前缀表达式相似,只是运算符位于操作数之后
    • e.g. 举例说明 (3+4)*5 -6 对应的后缀表达式就是 3 4 + 5 * 6 -

    后缀表达式的计算机求值过程

    从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素和栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。

    e.g.例如 (3+4)*5 -6 对应的后缀表达式就是 3 4 + 5 * 6 - 针对后缀表达式求值步骤如下:

    1、从左至右扫描,将3和4压入堆栈

    2、遇到+ 3+4 = 7 7 入栈

    3、遇到5入栈

    4、遇到* 弹出5 和 7 5*7 = 35 35 入栈

    5、遇到6 入栈

    6、遇到- 弹出 6和35 用次顶元素35减去栈顶元素7 = 29 为最终结果

    逆波兰计算器

    demo

    package com.summer.datastructure.stack;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    
    public class PolandNotation {
        public static void main(String[] args) {
            //先定义逆波兰表达式
            //(3+4)*5 -6 对应的后缀表达式就是 3 4 + 5 * 6 -
            //说明为了方便,逆波兰表达式的数字和符号使用空格 隔开
            String suffixExpression = "3 4 + 5 * 6 -";
            //思路
            //1.先将"3 4 + 5 * 6 -"放到ArrayList中
            //2.将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算
    
            List<String> rpnList = getListString(suffixExpression);
            System.out.println(rpnList);
            System.out.println("计算的结果--------> "+calculate(rpnList));
        }
    
        //将一逆波兰表达式,依次将数据和运算符 放入到ArrayList中
        public static List<String> getListString(String suffixExpression){
            String[] split = suffixExpression.split(" ");
            List<String> list = new ArrayList<>();
            for (String s : split) {
                list.add(s);
            }
            return list;
            }
    
            public static int calculate(List<String> ls){
            //创建一个栈,只需要一个栈
                Stack<String> stack  = new Stack<>();
                //遍历list
                for (String item : ls) {
                    //这里使用正则表达式来取出数
                    if (item.matches("\d+")){//匹配的时多位数
                        //直接入栈
                        stack.push(item);
                    }else {
                        //pop出两个数 并运算 ,运算结果在入栈
                        int num2 = Integer.parseInt(stack.pop());
                        int num1 = Integer.parseInt(stack.pop());
                        int res = 0;
                        if (item.equals("+")){
                            res =num1+num2;
                        }else if (item.equals("*")){
                            res= num1*num2;
                        }else if (item.equals("/")){
                            res = num1/num2;
                        }else if(item.equals("-")){
                            res = num1-num2;
                        }else {
                            throw new RuntimeException("运算符有误");
                        }
                        stack.push(res+"");//把整数转换为字符串
                    }
                }
                //最后留在stack中的数据时运算的结果
                return Integer.parseInt(stack.pop());
            }
    }
    

    结果

    [3, 4, +, 5, *, 6, -]
    计算的结果--------> 29
    
    Process finished with exit code 0
    

    中缀表达式转后缀表达式步骤

    • 步骤1:初始化两个栈:运算符栈s1和储存中间结果的栈s2
    • 步骤2:从左至右扫描中缀表达式;
    • 步骤3:遇到操作数时,将其压入s2
    • 步骤4:遇到运算符时,比较其与s1栈顶运算符的优先级
      • 4-1:如果s1为空,或者栈顶运算符为左括号“(”,则直接将此运算符入栈
      • 4-2:否则,若优先级比栈顶运算符的高,也将运算符压入s1
      • 4-3:否则,将s1栈顶的运算符弹出并压入到s2中,再次转到4-1与s1中新的栈顶运算符比较
    • 步骤5:遇到括号时:
      • 5-1:如果是左括号( ,直接压入s1
      • 5-2:如果是右括号) ,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
    • 步骤6:重复步骤2至5,直到表达式的最右边
    • 步骤7:将s1中剩余的运算符依次弹出并压入s2
    • 步骤8:依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的逆波兰表达式

    e.g. 中缀表达式: 1+ ( (2 + 3) * 4 ) -5 --》 s1: - 5 + * 4 + 3 2 1 ---》 1 2 3 + 4 * + 5 -

    说明:因为s2这个栈,在整个转换过程中,没有pop操作,而且后面我们还要逆序输出,因此s2可用ArrayList代替哦,实际问题自己灵活变通。

    demo --- myself

    package com.summer.datastructure.stack;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    
    public class PolandNotation {
        public static void main(String[] args) {
    
    
    
            //完成将一个中缀表达式转换成后缀表达式的功能
            //说明
            //1、1+ ( (2 + 3) * 4 ) -5   转成  1 2 3 + 4 * + 5  -
            //2、1+ ( (2 + 3) * 4 ) -5 转换成一个ArrayList 好扫描
            //3、将中缀表达式的list转换成后缀表达式的list
    //        String express = "1+((2+3)*4)-5";
            String express = "(5-3)*5/2-(8-7)";
            List<String> list = toInfixExressionList(express);
            System.out.println(list);
            List<String> suffixExpression =  parseSuffixEpressionList(list);
            List<String> suffixExpression1 =  parseSuffixEpressionListV2(list);
            System.out.println(suffixExpression);
            System.out.println(suffixExpression1);
            System.out.println(express +" self计算的结果--------> "+calculate(suffixExpression));
            System.out.println(express +" v2计算的结果--------> "+calculate(suffixExpression1));
        }
    
        //即:1+ ( (2 + 3) * 4 ) -5   转成  1 2 3 + 4 * + 5  -
        //将得到的中缀表达式list转换成后缀表达式list
        public static List<String> parseSuffixEpressionList(List<String> ls){
            Stack<String> stack = new Stack<>();
            List<String> result = new ArrayList<>();
            for(int i = 0;i<ls.size();i++){
                if (isOper(ls.get(i))){
                    if (stack.empty()){
                        stack.push(ls.get(i));
                    }else {
                        String top = stack.pop();
                        if (top.equals("(")){
                            stack.push(top);
                            stack.push(ls.get(i));
                        }else {
                            if (rank(ls.get(i)) > rank(top)){
                                stack.push(top);
                                stack.push(ls.get(i));
                            }else {
                                result.add(top);
                                i--;
                                continue;
                            }
                        }
                    }
                }else if (ls.get(i).equals("(") || ls.get(i).equals(")")){
                    if (ls.get(i).equals("(")){
                        stack.push(ls.get(i));
                    }else {
                        String temp = stack.pop();
                        while (!temp.equals("(")){
                            result.add(temp);
                            temp = stack.pop();
                        }
                    }
                }else {
                    result.add(ls.get(i));
                }
            }
            while (!stack.isEmpty()){
                result.add(stack.pop());
            }
    
    //        for (int i = result.size()-1; i >= 0; i--) {
    //            finalResult.add(result.get(i));
    //        }
            return result;
        }
    
        //
        public static List<String> parseSuffixEpressionListV2(List<String> ls){
            Stack<String> s1 = new Stack<>();
            List<String> s2 = new ArrayList<>();
            for (String item : ls) {
                if (item.matches("\d+")){
                    s2.add(item);
                }else if (item.equals("(")){
                    s1.push(item);
                }else if (item.equals(")")){
                    while(!s1.peek().equals("(")){
                        s2.add(s1.pop());
                    }
                    s1.pop();//将一对小括号弹出
                }else {
                    //这里都是运算符
                    //当item<=栈顶运算符优先级  就把栈顶的运算符pop出来
                        while (!s1.isEmpty() && !s1.peek().equals("(") && Operation.getValue(item) <= Operation.getValue(s1.peek())){
                            s2.add(s1.pop());
                        }
                        s1.push(item);
                }
            }
            //将s1加入s2中
            while (!s1.isEmpty()){
               s2.add(s1.pop());
            }
            return s2;
        }
    
        public static boolean isOper(String s){
            if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")){
                return true;
            }
            return false;
        }
    
        public static int rank(String a){
            if (a.equals("+") || a.equals("-")){
                return 1;
            }else if (a.equals("*") || a.equals("/")){
                return 2;
            }
            throw new RuntimeException("运算符优先级未添加");
        }
    
        //将中缀表达式抓花城对应的List
        public static List<String> toInfixExressionList(String s){
            List<String> list = new ArrayList<>();
            int i = 0;
            String str;
            while (i< s.length()){
                if (s.charAt(i) < 48 || s.charAt(i)>57){
                    list.add(s.charAt(i)+"");
                    i++;
                }else {
                    str = "";
                    while (i<s.length() && s.charAt(i) >= 48 && s.charAt(i) <= 57){
                        str = "" + s.charAt(i);
                        i++;
                    }
                    list.add(str);
                }
            }
            return list;
        }
    
        //将一逆波兰表达式,依次将数据和运算符 放入到ArrayList中
        public static List<String> getListString(String suffixExpression){
            String[] split = suffixExpression.split(" ");
            List<String> list = new ArrayList<>();
            for (String s : split) {
                list.add(s);
            }
            return list;
            }
    
            public static int calculate(List<String> ls){
            //创建一个栈,只需要一个栈
                Stack<String> stack  = new Stack<>();
                //遍历list
                for (String item : ls) {
                    //这里使用正则表达式来取出数
                    if (item.matches("\d+")){//匹配的时多位数
                        //直接入栈
                        stack.push(item);
                    }else {
                        //pop出两个数 并运算 ,运算结果在入栈
                        int num2 = Integer.parseInt(stack.pop());
                        int num1 = Integer.parseInt(stack.pop());
                        int res = 0;
                        if (item.equals("+")){
                            res =num1+num2;
                        }else if (item.equals("*")){
                            res= num1*num2;
                        }else if (item.equals("/")){
                            res = num1/num2;
                        }else if(item.equals("-")){
                            res = num1-num2;
                        }else {
                            throw new RuntimeException("运算符有误");
                        }
                        stack.push(res+"");//把整数转换为字符串
                    }
                }
                //最后留在stack中的数据时运算的结果
                return Integer.parseInt(stack.pop());
            }
    
    }
    
    //编写一个类可以返回一个运算符对应的优先级
    class Operation{
        private static int ADD = 1;
        private static int SUB = 1;
        private static int MUL = 2;
        private static int DIV = 2;
    
        //写一个方法,返回对应的优先级数字
        public static int getValue(String operation){
            int result = 0;
            switch (operation){
                case "+":
                    result = ADD;
                    break;
                case "-":
                    result = SUB;
                    break;
                case "*":
                    result = MUL;
                    break;
                case "/":
                    result = DIV;
                    break;
                default:
                    throw new RuntimeException("运算符找不到");
            }
            return result;
        }
    }
    
    

    结果:

    [(, 5, -, 3, ), *, 5, /, 2, -, (, 8, -, 7, )]
    [5, 3, -, 5, *, 2, /, 8, 7, -, -]
    [5, 3, -, 5, *, 2, /, 8, 7, -, -]
    (5-3)*5/2-(8-7) self计算的结果--------> 4
    (5-3)*5/2-(8-7) v2计算的结果--------> 4
        
    [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
    [1, 2, 3, +, 4, *, +, 5, -]
    [1, 2, 3, +, 4, *, +, 5, -]
    1+((2+3)*4)-5 self计算的结果--------> 16
    1+((2+3)*4)-5 v2计算的结果--------> 16
    
    Process finished with exit code 0
    

    递归应用

    概念

    简单地说:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让那个代码变得简洁。

  • 相关阅读:
    Flex 开源框架及工具 枫
    String.Format格式说明 枫
    HTTP服务器状态代码定义(Status Code Definitions) 枫
    像素对应表 枫
    js各种获取屏幕高度宽度 枫
    c#正则表达式帮助 枫
    使用模板引擎Trimpath 枫
    分布式缓存HttpRuntime.cache应用到单点登陆中_优化登陆 枫
    Query this 和 $(this) 的区别 枫
    css and js style 枫
  • 原文地址:https://www.cnblogs.com/ls-summer/p/14564514.html
Copyright © 2011-2022 走看看