线性数据结构和非线性数据结构(java版)

线性数据结构和非线性数据结构

数据结构包括 线性数据结构非线性数据结构

数据结构

  1. 线性数据结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。
  2. 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。顺序存储的线性表称为 顺序表,顺序表中的存储元素是连续的。
  3. 链式存储的线性表称为 链表,链表中的存储元素不一定是连续的,元素结点中存放数据元素以及相邻元素的地址信息。
  4. 线性结构常有:数组,队列,链表和栈。

非线性结构

非线性结构包括:二维数组,多维数组,广义表,树结构,图结构。

稀疏Sparsearray 数组

在这里插入图片描述

基本介绍:

  • 当一个数组中打部分元素为0,或者同为一个值的数组时,可以用稀疏数组来保存该数组。
  • 稀疏数组的处理方法:
    • 记录数组一共有几行几列,有多少个不同的值。
    • 具有不同值的元素的行和列及值记录在一个小规模的数组中,从而压缩小程序的规模。

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

思路:

  • 二维数组 ----> 稀疏数组
    • 遍历元素的二维数组, 得到有效数据的个数sum。
    • 根据sum可以创建稀疏数组 sparseArrary int[sum+1] [3].
    • 将二维数组的有效数据存入到稀疏数组。
  • 稀疏数组 ----> 二维数组
    • 先读取稀疏数组第一行,根据第一行恢复二维数组(稀疏数组第一行存入的是二维数组的行和列)。
    • 读取稀疏数组的后几行数据,并赋值给 原始的二维数组即可。

代码实现:

  • public class SparseArray {
    
        public static void main(String[] args) {
            //创建一个二维数组
            // 0没,1黑,2,白
            int[][] chessArray = new int[11][11];
            chessArray[1][2] = 1;
            chessArray[2][3] = 2;
            int sum = 0;
            for (int[] row : chessArray) {
                for (int data: row) {
                    System.out.printf("%d\t", data);
                }
                System.out.println();
            }
            for (int i = 0; i < 11; ++ i) {
                for (int j = 0; j < 11; ++ j) {
                    if (chessArray[i][j] != 0) {
                        sum ++;
                    }
                }
            }
            //2. 创建对应的稀疏数组
            int sparseArr[][] = new int[sum + 1][3];
            //给稀疏数组赋值
            sparseArr[0][0] = 11;
            sparseArr[0][1] = 11;
            sparseArr[0][2] = sum;
    
            //遍历二维数组,数据存入稀疏数组。
            for (int i = 0; i < 11; ++ i) {
                for (int j = 0; j < 11; ++ j) {
                    if (chessArray[i][j] != 0) {
                        sparseArr[i][0] = i;
                        sparseArr[i][1] = j;
                        sparseArr[i][2] = chessArray[i][j];
                    }
                }
            }
    
            //输出稀疏数组
            System.out.println();
            System.out.println("得到的稀疏数组");
            for (int i = 0; i < sparseArr.length; i ++ ) {
                System.out.printf("%d\t%d\t%d\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
            }
            System.out.println();
    
            System.out.println("还原:");
            int[][] chessArr2 = new int[sparseArr[0][0]][sparseArr[0][1]];
    
            for (int i = 1; i < sparseArr.length; ++ i) {
                chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
            }
            //输出恢复后的二维数组
            for (int[] row : chessArr2) {
                for (int data: row) {
                    System.out.printf("%d\t", data);
                }
                System.out.println();
            }
        }
    }
    
    

课后练习:

  • 把稀疏数组保存到磁盘, 读入到数组没写

    • import java.io.*;
      
      public class SparseArray {
      
          public static void main(String[] args) {
              //创建一个二维数组
              // 0没,1黑,2,白
              int[][] chessArray = new int[11][11];
              chessArray[1][2] = 1;
              chessArray[2][3] = 2;
              int sum = 0;
              for (int[] row : chessArray) {
                  for (int data: row) {
                      System.out.printf("%d\t", data);
                  }
                  System.out.println();
              }
              for (int i = 0; i < 11; ++ i) {
                  for (int j = 0; j < 11; ++ j) {
                      if (chessArray[i][j] != 0) {
                          sum ++;
                      }
                  }
              }
              //2. 创建对应的稀疏数组
              int sparseArr[][] = new int[sum + 1][3];
              //给稀疏数组赋值
              sparseArr[0][0] = 11;
              sparseArr[0][1] = 11;
              sparseArr[0][2] = sum;
      
              //遍历二维数组,数据存入稀疏数组。
              for (int i = 0; i < 11; ++ i) {
                  for (int j = 0; j < 11; ++ j) {
                      if (chessArray[i][j] != 0) {
                          sparseArr[i][0] = i;
                          sparseArr[i][1] = j;
                          sparseArr[i][2] = chessArray[i][j];
                      }
                  }
              }
              //将数据存入磁盘
              FileOutputStream fos = null;
              OutputStreamWriter writer = null;
              File file = new File("C:\\Users\\25766\\Desktop\\我的学习笔记\\数据结构和算法\\map.data");
              try {
                  fos = new FileOutputStream(file);//获取文件输出流
                  writer = new OutputStreamWriter(fos, "UTF-8");//输出流
                  for (int i = 0; i < sparseArr.length; ++ i) {
                      writer.append(sparseArr[i][0] + " " + sparseArr[i][1] + " " + sparseArr[i][2] + "\n");//数据写入文件
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      writer.close();
                      fos.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      
              //输出稀疏数组
              System.out.println();
              System.out.println("得到的稀疏数组");
              for (int i = 0; i < sparseArr.length; i ++ ) {
                  System.out.printf("%d\t%d\t%d\n", sparseArr[i][0], sparseArr[i][1], sparseArr[i][2]);
              }
              System.out.println();
      
              System.out.println("还原:");
              FileInputStream fis = null;
              InputStreamReader reader = null;
              try{
                  fis = new FileInputStream(file);
                  reader = new InputStreamReader(fis, "UTF-8");
                  StringBuffer s = new StringBuffer();
                  int t = 0;
                  while (reader.ready()) {
                      
                  }
              }catch (Exception e) {
                  e.printStackTrace();
              }finally {
                  try {
                      reader.close();
                      fis.close();
                  }catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      
              int[][] chessArr2 = new int[sparseArr[0][0]][sparseArr[0][1]];
      
              for (int i = 1; i < sparseArr.length; ++ i) {
                  chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2];
              }
              //输出恢复后的二维数组
              for (int[] row : chessArr2) {
                  for (int data: row) {
                      System.out.printf("%d\t", data);
                  }
                  System.out.println();
              }
          }
      }
      
      

队列

队列:是一种先进先出的数据结构。

队列介绍:

  • 队列是一个有序列表,可以用数组或者是链表来实现。
  • 遵循 先进先出 的原则。 即:先存入队列的数据,要先取出。后存入的要后取出。
  • 示意图:(使用数组模拟队列示意图)
    *在这里插入图片描述

数组模拟队列

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

思路分析:

  • 将我们的数据存入队列是称为 “addQueue”, addQueue 的处理需要有两个步骤

      1. 将尾指针往后移动:rear + 1, 当front == rear 队列为
      2. 若尾指针 rear 小于队列的最大下标 maxSize - 1, 则将数据存入rear所指的数组元素中,否则无法存入数据。rear == maxSize - 1(队列满了)
  • 代码实现:

    • import java.util.Scanner;
      public class ArrayQueueDemo {
          public static void main(String[] args) {
              ArrayQueue q = new ArrayQueue(3);
              char key = ' ';
              Scanner cin = 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 = cin.next().charAt(0);//接收一个字符
      
      
                  switch (key) {
                      case 's':
                          q.showQueue();
                          break;
                      case 'a':
                          System.out.println("输入一个数");
                          int v = cin.nextInt();
                          q.addQueue(v);
                          break;
                      case 'g':
                          try {
                              int res = q.getQueue();
                              System.out.printf("取出数据是%d\n", res);
                          }catch (Exception e) {
                              System.out.println(e.getMessage());
                          }
                          break;
                      case 'h':
                          try {
                              int res = q.headQueue();
                              System.out.printf("队列头部的数据为%d\n", res);
                          }catch (Exception e) {
                              System.out.println(e.getMessage());
                          }
                          break;
                      case 'e':
                          cin.close();
                          loop = false;
                          break;
                      default:
                          break;
                  }
              }
              System.out.println("程序退出");
          }
      }
      
      //使用数组模拟
      class ArrayQueue {
          private int maxSize;//表示数组的最大容量
          private int front;//队头
          private int  rear;//队尾
          private int[] arr;//数组用于存放数据,模拟队列
      
          public ArrayQueue(int arrMaxSize){
              this.maxSize = arrMaxSize;
              this.arr = new int[this.maxSize];
              this.front = -1;//指向队头,分析出前一个位置
              this.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;
              }
              rear ++;
              arr[rear] = n;
          }
          //获得队列的数据
          public int getQueue() {
              if(isEmpty()){
                  //通过抛出异常
                  throw new RuntimeException("队列不能为空");
              }
              front++;//后移动
              return arr[front];
          }
          //显示队列的所有数据
          public void showQueue() {
              if (isEmpty()) {
                  System.out.println("队列为空");
                  return;
              }
              for (int i = 0; i < arr.length; ++ i) {
                  System.out.printf("arr[%d]=%d\n", i, arr[i]);
              }
          }
          //显示队列的头数据
          public int headQueue() {
              if (isEmpty()) {
                  throw new RuntimeException("队列为空");
              }
              return arr[front + 1];
          }
      
      }
      
      

数组模拟环形队列

思路如下:

  1. front 变量的含义做一个调整:front 就是指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素
  2. rear 的含义是做一个调整: rear 指向队列的最后一个元素的最后一个位置,因为希望空出一个空间做为约定。
  3. 当队列满时,条件是(rear + 1)% maxSize = front [满]。
  4. 当队列为空的条件,rear == front
  5. 当我们这样分析,队列中有效的数据个数 (rear + maxSize - front)% maxSize // rear = 1 front = 0
  6. 就可以得到环形队列。
import java.util.Scanner;

public class CircleArrayQueue {
    public static void main(String[] args) {
        CircleArray q = new CircleArray(4);
        char key = ' ';
        Scanner cin = 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 = cin.next().charAt(0);//接收一个字符

            switch (key) {
                case 's':
                    q.showQueue();
                    break;
                case 'a':
                    System.out.println("输入一个数");
                    int v = cin.nextInt();
                    q.addQueue(v);
                    break;
                case 'g':
                    try {
                        int res = q.getQueue();
                        System.out.printf("取出数据是%d\n", res);
                    }catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = q.headQueue();
                        System.out.printf("队列头部的数据为%d\n", res);
                    }catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    cin.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

class CircleArray {
    private int maxSize;
    private int front;
    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 rear == front;
    }

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

        rear = (rear + 1) % maxSize;
    }

    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空,不能取数据");
        }

        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;
    }

    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列空的,没有数据~~");
            return;
        }

        for (int i = front; i < front + size(); i ++ ) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }
    }

    //当前队列的有效个数
    public int size() {
        return (rear + maxSize - front) % maxSize;
    }
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空");
        }
        return arr[front + 1];
    }


}

链表

单链表

在这里插入图片描述

小结:

  1. 链表事以结点的方式存储的。
  2. 每个结点包括data域,next域:指向下一个结点。
  3. 如图发现不连续。
  4. 链表分有头节点,和没有头节点的链表,根据实际需求来确定。

带头结点的链表(逻辑结构图):

在这里插入图片描述

思路:

  1. 先创建一个head头结点。作用就事表示单链表的头。
  2. 后面我们每添加一个结点,就是直接加到链表的后面
  • 遍历:

    • 代码

    • import java.awt.*;
      
      public class SingleLinkedListDemo {
          public static void main(String[] args) {
              SingleLinkedList list = new SingleLinkedList();
              list.add(new HerNode(1, "董亚", "小旋风"));
              list.add(new HerNode(2, "王仕", "小宽"));
              list.add(new HerNode(3, "李", "小琦"));
              list.add(new HerNode(4, "朱", "小朱"));
              list.add(new HerNode(5, "肖元", "小肖"));
              list.add(new HerNode(6, "何杨", "小飞飞"));
      
              list.show();
          }
      }
      //定义
      class SingleLinkedList {
          //初始化一个头结点
          private HerNode head = new HerNode(0, "", "");
      
          public void add(HerNode herNode) {
              HerNode temp = head;
              //遍历链表
              while (temp.next != null) {
                  temp = temp.next;
              }
              temp.next = herNode;
      
          }
          public void addByOrder(HerNode herNode) {
              HerNode temp = head;
              boolean flag = false;
              while (temp.next != null) {
                  if (temp.next.no > herNode.no){
                      break;
                  }else if (temp.next.no == herNode.no) {
                      flag = true;
                      break;
                  }
                  temp = temp.next;
              }
      
              if (flag) {
                  System.out.println("准备插入的英雄的编号已经存在");
              } else {
                  //插入到链表中
                  herNode.next = temp.next;
                  temp.next = herNode;
              }
          }
          //显示
          public void show() {
      
              if (head.next == null) {
                  System.out.println("链表为空");
                  return;
              }
              HerNode temp = head.next;
              while (temp != null) {
                  System.out.println(temp);
                  temp = temp.next;
              }
          }
      
          public void delete(int no) {
              HerNode temp = head;
              boolean  flag = false;
              while (temp.next != null) {
                  if (temp.next.no == no) {
                      flag = true;
                      break;
                  }
                  temp = temp.next;
              }
              if (flag) {
                  temp.next = temp.next.next;
              } else {
                  System.out.println("删除结点不存在");
              }
          }
      }
      
      //定义一个HerNode ,每个HerNode就是一个结点
      class HerNode {
          public int no;
          public String name;
          public String nickname;
          public HerNode next; //指向下一个结点
          //构造器
      
          public HerNode(int no, String name, String nickname) {
              this.no = no;
              this.name = name;
              this.nickname = nickname;
          }
      
          @Override
          public String toString() {
              return "HerNode{" +
                      "no=" + no +
                      ", name='" + name + '\'' +
                      ", nickname='" + nickname + '\'' +
                      '}';
          }
      }
      

单链表中面试题中:展现在代码中

import java.awt.*;
import java.util.Stack;

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        SingleLinkedList list = new SingleLinkedList();
        list.add(new HerNode(1, "董亚", "小旋风"));
        list.add(new HerNode(2, "王仕", "小宽"));
        list.add(new HerNode(3, "李", "小琦"));
        list.add(new HerNode(4, "朱", "小朱"));
        list.add(new HerNode(5, "肖元", "小肖"));
        list.add(new HerNode(6, "何杨", "小飞飞"));

        list.show();
        System.out.println();
        list.printReverse();
        System.out.println();
        list.revers();
        System.out.println();
        list.show();
        System.out.println();
    }
}
//定义
class SingleLinkedList {
    //初始化一个头结点
    private HerNode head = new HerNode(0, "", "");

    public void add(HerNode herNode) {
        HerNode temp = head;
        //遍历链表
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = herNode;

    }
    public void addByOrder(HerNode herNode) {
        HerNode temp = head;
        boolean flag = false;
        while (temp.next != null) {
            if (temp.next.no > herNode.no){
                break;
            }else if (temp.next.no == herNode.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            System.out.println("准备插入的英雄的编号已经存在");
        } else {
            //插入到链表中
            herNode.next = temp.next;
            temp.next = herNode;
        }
    }
    //显示
    public void show() {

        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HerNode temp = head.next;
        while (temp != null) {
            System.out.println(temp);
            temp = temp.next;
        }
    }

    public void delete(int no) {
        HerNode temp = head;
        boolean  flag = false;
        while (temp.next != null) {
            if (temp.next.no == no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.println("删除结点不存在");
        }
    }
    //不声反转单链表
    public void revers() {
        if (head.name == null || head.next.next == null) {
            return;
        }
        //定义一个辅助指针变量,帮助我们遍历原来的链表
        HerNode cur = head.next;
        HerNode next = null;
        HerNode reverseHead = new HerNode(0, "", "");

        while (cur != null) {
            next = cur.next;//先暂时保存当前结点的下一个结点,后面要使用
            cur.next = reverseHead.next;//将cur的下一个结点指向新的链表的最前端
            reverseHead.next = cur;
            cur = next;//让cur后移动
        }
        head.next = reverseHead.next;
    }
    //通过栈数据结构,逆序输出单链表
    public void printReverse(){
        if (head.next == null){
            return;
        }
        Stack<HerNode> stack = new Stack<HerNode>();

        HerNode cur = head.next;
        //将链表所有结点压入栈中
        while(cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while(!stack.empty()) {
            System.out.println(stack.peek());
            stack.pop();
        }

    }
}

//定义一个HerNode ,每个HerNode就是一个结点
class HerNode {
    public int no;
    public String name;
    public String nickname;
    public HerNode next; //指向下一个结点
    //构造器

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

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

双向链表

  • 双向链表的应用示例

    • 使用带head头的 双向链表 实现-水浒英雄排行榜

    • 管理单向链表的缺点分析:

        1. 单链表,查找的方向只能是一个方向,而双链表可以向前或者向后查找。

        2. 单链表不能 自我删除,需要靠辅助结点,而双向链表,则可以 自我删除,所以单链表删除结点时,总是找到temp的下一个结点来删除。

          • public void delete(int no) {
                    HerNode temp = head;
                    boolean  flag = false;
                    while (temp.next != null) {
                        if (temp.next.no == no) {
                            flag = true;
                            break;
                        }
                        temp = temp.next;
                    }
                    if (flag) {
                        temp.next = temp.next.next;
                    } else {
                        System.out.println("删除结点不存在");
                    }
                }
            
  • 分析 双向链表的遍历,添加,修改,删除的操作思路:

    • 遍历单向链表一样,只是可以向前,也可以向后查找

    • 添加(默认添加到双向链表的最后)

        1. 找到双向链表的最后这个节点
        2. temp.next = newHerNode;
        3. newHerNode.pre = temp;
    • 修改思路和单链表一样

    • 删除

        1. 因为是双向链表,因此我们课以实现 自我删除 某个节点。
        2. 直接找到要删除的这个节点,比如temp
        3. temp.pre.next = temp.next; (当前要删除的节点的 前驱的后继 = 删除节点的后继 )
        4. temp.next.pre = temp.pre;(当前要删除节点的 后继的前驱 = 删除节点的前驱)
    • 代码实现:

    • 在这里插入图片描述

      • public class DoubleLinkedListDemo {
            public static void main(String[] args) {
                System.out.println("双向链表测试");
                DoubleLinkedList list = new DoubleLinkedList();
                list.add(new HerNode1(1, "董亚宁", "小旋风"));
                list.add(new HerNode1(2, "王仕宽", "小宽"));
                list.add(new HerNode1(3, "李琦", "小琦"));
                list.add(new HerNode1(4, "朱瑞", "小朱"));
                list.add(new HerNode1(5, "肖元彪", "小肖"));
                list.add(new HerNode1(6, "何杨飞", "小飞飞"));
        
                list.show();
                System.out.println();
                list.delete(5);
                System.out.println("删除后");
                list.show();
                System.out.println();
            }
        }
        
        class DoubleLinkedList {
            private HerNode1 head = new HerNode1(0, "", "");
        
            //返回头结点
            public HerNode1 getHead() {
                return head;
            }
        
            //遍历双向链表
            public void show() {
        
                if (head.next == null) {
                    System.out.println("链表为空");
                    return;
                }
                HerNode1 temp = head.next;
                while (temp != null) {
                    System.out.println(temp);
                    temp = temp.next;
                }
            }
            //添加到双向链表
            public void add(HerNode1 herNode1) {
                HerNode1 temp = head;
                //遍历链表
                while (temp.next != null) {
                    temp = temp.next;
                }
                //形成双向链表
                temp.next = herNode1;
                herNode1.pre = temp;
        
            }
            //双向链表中修改一个结点
            public void modify(HerNode1 herNode1) {
                if (head.next == null) {
                    System.out.println("链表为空");
                    return;
                }
                HerNode1 temp = head;
                boolean flag = false;
                while (temp != null) {
                    if (temp.no == herNode1.no) {
                        flag = true;
                        break;
                    }
                }
                if (flag) {
                    temp.no = herNode1.no;
                    temp.name = herNode1.name;
                    temp.nickname = herNode1.nickname;
                }
            }
            //双向链表中删除一个结点
            public void delete(int no) {
                HerNode1 temp = head.next;
                boolean flag = false;
                while (temp != null) {
                    if (temp.no == no) {
                        flag = true;
                        break;
                    }
                    temp = temp.next;
                }
                if (flag) {
                    //注意最后一个结点有点特殊
                    temp.pre.next = temp.next;
                    //最后一个结点不需要执行下面这段代码
                    if (temp.next != null) {
                        temp.next.pre = temp.pre;
                    }
                }else {
                    System.out.println("要删除的结点不存在");
                }
            }
        }
        
        class HerNode1 {
            public int no;
            public String name;
            public String nickname;
            public HerNode1 next; //指向下一个结点,默认为null
            public HerNode1 pre; //指向前一个结点
            //构造器
        
            public HerNode1(int no, String name, String nickname) {
                this.no = no;
                this.name = name;
                this.nickname = nickname;
            }
        
            @Override
            public String toString() {
                return "HerNode{" +
                        "no=" + no +
                        ", name='" + name + '\'' +
                        ", nickname='" + nickname + '\'' +
                        '}';
            }
        }
        

约瑟夫问题

问题:

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

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

  • 在这里插入图片描述

      1. 先创建第一个结点,让first指向该结点,并形成环形。
      2. 后面当我们没创建一个新的结点,就把该节点,加入到已有的环形链表中即可。
  • 遍历环形链表:

      1. 先让一个辅助指针(变量),指向first结点。
      2. 然后通过一个while循环遍历该链表就可以了 curBoy.next == first结束到链表尾部。
  • 实现代码

    • public class Josefa {
          public static void main(String[] args) {
              //测试构建和遍历
              CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
              circleSingleLinkedList.addBoy(5);
              circleSingleLinkedList.showBoy();
              circleSingleLinkedList.countBoy(1, 2, 5);
          }
      }
      //创建环形单向链表
      class CircleSingleLinkedList {
          private Boy first;
      
          //添加小孩
          public void addBoy(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();
              }
          }
          //出圈,startNo为开始的编号
          public void countBoy(int startNo, int countNum, int nums) {
              //对数据检查
              if (first == null || startNo < 1 || startNo > nums) {
                  System.out.println("参数输入有误");
                  return;
              }
              Boy helper = first;
              //让helper指向first前一个结点
              while (helper.getNext() != first) {
                  helper = helper.getNext();
              }
              //从第几个小孩开始
              for (int i = 0; i < startNo - 1; ++ i) {
                  first = first.getNext();
                  helper = helper.getNext();
              }
              //出圈
              while (helper != first) {
                  for (int i = 0; i < countNum - 1; ++ i) {
                      first = first.getNext();
                      helper = helper.getNext();
                  }
                  //first指向的小孩出
                  System.out.printf("小孩%d出圈\n", first.getNo());
                  first = first.getNext();
                  helper.setNext(first);
                  //小孩出队完成
              }
              System.out.printf("小孩%d出圈\n", first.getNo());
          }
      }
      
      
      //创建要给boy类
      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. 栈的英文 stack
  2. 栈是一个先入后出(FILO-First In Last Out) 的有序列表
  3. stack 是限制线性表中的元素的插入和删除 只能在同一端 进行的一种特殊线性表。允许插入和删除的一端,称为 栈顶。另一端为栈低。
  4. 根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
  5. 在这里插入图片描述

栈的场景应用:

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

数组模拟栈的思路分析:

在这里插入图片描述

实现 栈的思路分析

  1. 使用数组来模拟栈
  2. 定义一个top来表示栈顶,初始化为-1
  3. 入栈 的操作当有数据加入时,top++;stack[top] = data;
  4. 出栈 的操作,int value = stack[top]; top–,return value;
  • 代码实现:

    • public class ArrayStackDemo {
          public static void main(String[] args) {
              ArrayStack stack = new ArrayStack(3);
              stack.push(1);
              stack.push(2);
              stack.push(3);
      
              stack.list();
              if (stack.isFull()) System.out.println("Full");
              if (stack.isEmpty()) System.out.println("Empty");
      
          }
      }
      
      class ArrayStack {
          private int maxSize; //栈的大小
          private int[] stack; //数组,数组模拟栈,数据就放在该数组
          private int top = -1; //top表示栈顶,初始化为 -1
      
          //构造器
          public ArrayStack(int maxSize) {
              this.maxSize = maxSize;
              this.stack = new int[maxSize];
          }
      
          //栈满
          public boolean isFull() {
              return top == this.maxSize - 1;
          }
          //栈空
          public boolean isEmpty() {
              return top == -1;
          }
          //入栈-push
          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("栈为空");
                  return;
              }
              for (int i = top; i >= 0; -- i) {
                  System.out.print(stack[i] + " ");
              }
              System.out.println();
          }
      
      }
      
      

作业:

  • 用链表模拟栈

  • import javax.management.relation.RoleInfoNotFoundException;
    
    public class ArrayLinkedStackDemo {
        public static void main(String[] args) {
            LinkedStack stack = new LinkedStack();
            stack.push(new stackNode(1));
            stack.push(new stackNode(2));
            stack.push(new stackNode(3));
    
            System.out.println(stack.pop());
            System.out.println(stack.pop());
            System.out.println(stack.pop());
            
        }
    }
    
    class LinkedStack {
        public stackNode head;
        public LinkedStack() {
            this.head = new stackNode(0);
            this.head.next = null;
        }
        //满足先加入的结点再后面,参考翻转单链表
        public void push(stackNode node) {
            node.next = head.next;
            head.next = node;
        }
        //删除栈顶元素
        public int pop() {
            if (head.next == null) {
                throw new RuntimeException("栈为空");
            }
            int value = head.next.data;
            head = head.next;
            return value;
        }
        //栈是否为空
        public boolean isEmpty() {
            return head.next == null;
        }
    }
    
    class stackNode {
        public int data;
        public stackNode next;
        public stackNode(int data) {
            this.data = data;
        }
    }
    
    

使用栈完成表达式的计算

思路:

在这里插入图片描述

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

代码实现:

public class Calculator {

    public static void main(String[] args) {
        //根据前面老师思路,完成表达式的运算
        String expression = "7*2*2-5+1-5+3-4"; // 15//如何处理多位数的问题?
        //创建两个栈,数栈,一个符号栈
        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循环的扫描expression
        while(true) {
            //依次得到expression 的每一个字符
            ch = expression.substring(index, index+1).charAt(0);
            //判断ch是什么,然后做相应的处理
            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); // 1 + 3
                }
            } else { //如果是数,则直接入数栈

                //numStack.push(ch - 48); //? "1+3" '1' => 1
                //分析思路
                //1. 当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数
                //2. 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈
                //3. 因此我们需要定义一个变量 字符串,用于拼接

                //处理多位数
                keepNum += ch;

                //如果ch已经是expression的最后一位,就直接入栈
                if (index == expression.length() - 1) {
                    numStack.push(Integer.parseInt(keepNum));
                }else{

                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
                    //注意是看后一位,不是index++
                    if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
                        //如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"
                        numStack.push(Integer.parseInt(keepNum));
                        //重要的!!!!!!, keepNum清空
                        keepNum = "";

                    }
                }
            }
            //让index + 1, 并判断是否扫描到expression最后.
            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);//入栈
        }
        //将数栈的最后数,pop出,就是结果
        int res2 = numStack.pop();
        System.out.printf("表达式 %s = %d", expression, res2);
    }

}

//先创建一个栈,直接使用前面创建好
//定义一个 ArrayStack2 表示栈, 需要扩展功能
class ArrayStack2 {
    private int maxSize; // 栈的大小
    private int[] stack; // 数组,数组模拟栈,数据就放在该数组
    private int top = -1;// top表示栈顶,初始化为-1

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

    //增加一个方法,可以返回当前栈顶的值, 但是不是真正的pop
    public int peek() {
        return stack[top];
    }

    //栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }
    //栈空
    public boolean isEmpty() {
        return top == -1;
    }
    //入栈-push
    public void push(int value) {
        //先判断栈是否满
        if(isFull()) {
            System.out.println("栈满");
            return;
        }
        top++;
        stack[top] = value;
    }
    //出栈-pop, 将栈顶的数据返回
    public int pop() {
        //先判断栈是否空
        if(isEmpty()) {
            //抛出异常
            throw new RuntimeException("栈空,没有数据~");
        }
        int value = stack[top];
        top--;
        return value;
    }
    //显示栈的情况[遍历栈], 遍历时,需要从栈顶开始显示数据
    public void list() {
        if(isEmpty()) {
            System.out.println("栈空,没有数据~~");
            return;
        }
        //需要从栈顶开始显示数据
        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(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/';
    }
    //计算方法
    public int cal(int num1, int num2, int oper) {
        int res = 0; // res 用于存放计算的结果
        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)X 5 - 6对应的前缀表达式就是 - X + 3 4 5 6.

前缀表达式:

  • 从右到左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符队他们做对应的计算(栈顶元素 和 次顶元素),并将结果压入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
  • 例如:(3 + 4) X 5 - 6,针对前缀表达式求值步骤如下:
    1. 从右到左扫描,将6、5、4、3压入栈
    2. 遇到 + 运算符,因此弹出3、4,计算值,得7,将7压入栈中。
    3. 接下来是X运算符,因此弹出7、5,计算出7 X 5 = 35, 将35压入栈。
    4. 最后是 - 运算符,计算出值35 - 6的值,29,由次得出结果。

中缀表达式:

    1. 中缀表达式就是 常见的运算表达式,如 (3 + 4) X 5 - 6
    2. 中缀表达式的求值,是我们最熟悉的,但是对计算机来说却不好操作,因此,再计算结果时,往往将会将中缀表达式转化成其他表达式来操作(一般只转化成后缀表达式)。

后缀表达式:

    1. 后缀表达式又称为 逆波兰表达式 ,与前缀表达式相似,只是运算符的位置位于操作数之后。
    2. 举例:(3 + 4)* 5 - 6 对应的后缀表达式就式 3 4 + 5 * 6 -
    3. 再比如:在这里插入图片描述
  • 后缀表达式的求值计算

    • 从左向右扫描,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶里两个数,用运算符队他们进行计算,并将结果存入栈顶重复上述过程直到表达式最右端,最后运算得出的值即为表达式的计算结果。
      *在这里插入图片描述
逆波兰计算器

任务:

    1. 输入一个逆波兰表达式,使用栈(stack),计算其结果。
    2. 支持小括号和多位整数,计算器只是先对整数计算。
    3. 思路分析
    4. 代码完成
  • 代码实现:

    • import java.util.ArrayList;
      import java.util.List;
      import java.util.Stack;
      
      public class PolandNotation {
          public static void main(String[] args) {
              //定义一个逆波兰表达式
              String suffixExpression = "30 4 + 5 * 6 -";
              List<String> rpnList = getListString(suffixExpression);
              System.out.println(rpnList);
      
              int res = calculate(rpnList);
              System.out.println(res);
      
          }
          public static List<String> getListString(String suffixExpression) {
              String[] split = suffixExpression.split(" ");
              List<String> list = new ArrayList<String>();
              for (String ele : split) {
                  list.add(ele);
              }
              return list;
          }
          public static int calculate(List<String> ls) {
              Stack<String> stack = new Stack<String>();
              for (String item: ls) {
                  if (item.matches("\\d+")) {//匹配的是多位数
                      stack.push(item);
                  } else {
                      int n1, n2;
                      n1 = Integer.parseInt(stack.pop());
                      n2 = Integer.parseInt(stack.pop());
                      int res = 0;
                      if (item.equals("+")) {
                          res = n1 + n2;
                      } else if (item.equals("-")) {
                          res = n2 - n1;
                      } else if (item.equals("*")) {
                          res = n1 * n2;
                      } else if (item.equals("=")) {
                          res = n2 / n1;
                      } else {
                          throw new RuntimeException("运算符有问题");
                      }
                      stack.push("" + res);
                  }
              }
              return Integer.parseInt(stack.pop());
          }
      }
      
      
中缀表达式转后缀表达式
  • 1.在这里插入图片描述
    在这里插入图片描述
package com.atguigu.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. 因为直接对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,–]

        String expression = "1+((2+3)*4)-5";//注意表达式 
        List<String> infixExpressionList = toInfixExpressionList(expression);
        System.out.println("中缀表达式对应的List=" + infixExpressionList); // ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
        List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
        System.out.println("后缀表达式对应的List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–] 

        System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?
		
		
		
		/*
		
		//先定义给逆波兰表达式
		//(30+4)×5-6  => 30 4 + 5 × 6 - => 164
		// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / + 
		//测试 
		//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开
		//String suffixExpression = "30 4 + 5 * 6 -";
		String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76
		//思路
		//1. 先将 "3 4 + 5 × 6 - " => 放到ArrayList中
		//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算
		
		List<String> list = getListString(suffixExpression);
		System.out.println("rpnList=" + list);
		int res = calculate(list);
		System.out.println("计算的结果是=" + res);
		
		*/
    }



    //即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]  =》 ArrayList [1,2,3,+,4,*,+,5,–]
    //方法:将得到的中缀表达式对应的List => 后缀表达式对应的List
    public static List<String> parseSuffixExpreesionList(List<String> ls) {
        //定义两个栈
        Stack<String> s1 = new Stack<String>(); // 符号栈
        //说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出
        //因此比较麻烦,这里我们就不用 Stack<String> 直接使用 List<String> s2
        //Stack<String> s2 = new Stack<String>(); // 储存中间结果的栈s2
        List<String> s2 = new ArrayList<String>(); // 储存中间结果的Lists2

        //遍历ls
        for(String item: ls) {
            //如果是一个数,加入s2
            if(item.matches("\\d+")) {
                s2.add(item);
            } else if (item.equals("(")) {
                s1.push(item);
            } else if (item.equals(")")) {
                //如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
                while(!s1.peek().equals("(")) {
                    s2.add(s1.pop());
                }
                s1.pop();//!!! 将 ( 弹出 s1栈, 消除小括号
            } else {
                //当item的优先级小于等于s1栈顶运算符, 将s1栈顶的运算符弹出并加入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较
                //问题:我们缺少一个比较优先级高低的方法
                while(s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item) ) {
                    s2.add(s1.pop());
                }
                //还需要将item压入栈
                s1.push(item);
            }
        }

        //将s1中剩余的运算符依次弹出并加入s2
        while(s1.size() != 0) {
            s2.add(s1.pop());
        }

        return s2; //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List

    }

    //方法:将 中缀表达式转成对应的List
    //  s="1+((2+3)×4)-5";
    public static List<String> toInfixExpressionList(String s) {
        //定义一个List,存放中缀表达式 对应的内容
        List<String> ls = new ArrayList<String>();
        int i = 0; //这时是一个指针,用于遍历 中缀表达式字符串
        String str; // 对多位数的拼接
        char c; // 每遍历到一个字符,就放入到c
        do {
            //如果c是一个非数字,我需要加入到ls
            if((c=s.charAt(i)) < 48 ||  (c=s.charAt(i)) > 57) {
                ls.add("" + c);
                i++; //i需要后移
            } else { //如果是一个数,需要考虑多位数
                str = ""; //先将str 置成"" '0'[48]->'9'[57]
                while(i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57) {
                    str += c;//拼接
                    i++;
                }
                ls.add(str);
            }
        }while(i < s.length());
        return ls;//返回
    }

    //将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList中
    public static List<String> getListString(String suffixExpression) {
        //将 suffixExpression 分割
        String[] split = suffixExpression.split(" ");
        List<String> list = new ArrayList<String>();
        for(String ele: split) {
            list.add(ele);
        }
        return list;

    }

    //完成对逆波兰表达式的运算
	/*
	 * 1)从左至右扫描,将3和4压入堆栈;
		2)遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
		3)将5入栈;
		4)接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
		5)将6入栈;
		6)最后是-运算符,计算出35-6的值,即29,由此得出最终结果
	 */

    public static int calculate(List<String> ls) {
        // 创建给栈, 只需要一个栈即可
        Stack<String> stack = new Stack<String>();
        // 遍历 ls
        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("运算符有误");
                }
                //把res 入栈
                stack.push("" + res);
            }

        }
        //最后留在stack中的数据是运算结果
        return Integer.parseInt(stack.pop());
    }

}

//编写一个类 Operation 可以返回一个运算符 对应的优先级
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:
                System.out.println("不存在该运算符" + operation);
                break;
        }
        return result;
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值