二、线性结构及算法

news2024/11/15 12:37:52

文章目录

  • 一、稀疏数组
    • 1.1 实际需求
    • 1.2 基本介绍
    • 1.3 应用实例
  • 二、队列
    • 2.1 引入
    • 2.2 基本介绍
    • 2.3 数组模拟队列
  • 三、链表
    • 3.1 链表介绍
    • 3.2 单链表的应用实例
    • 3.3 单链表面试题
    • 3.4 双向链表应用实例
    • 3.5 单向环形链表
  • 四、栈
    • 4.1 基本介绍
    • 4.2 数组模拟栈
    • 4.3 链表模拟栈
    • 4.4 栈实现综合计算器
    • 4.5 前缀、中缀、后缀表达式(逆波兰表达式)
    • 4.6 逆波兰计算器实现
    • 4.7 中缀表达式转后缀表达式(*)
  • 五、递归
    • 5.1 递归的应用场景
    • 5.2 递归的调用机制
    • 5.3 递归可以解决的问题
    • 5.4 递归需要遵守的重要规则
    • 5.5 迷宫问题
    • 5.6 八皇后问题

一、稀疏数组

1.1 实际需求

在这里插入图片描述

1.2 基本介绍

在这里插入图片描述

在这里插入图片描述

1.3 应用实例

在这里插入图片描述

在这里插入图片描述

package com.gyh.sparsearray;


import java.util.Arrays;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class SparseArray {
    public static void main(String[] args) {
        // 创建一个原始的二维数组 11*11
        // 0: 表示没子  1:表示黑子  2:表示蓝子
        int[][] chessArr = new int[11][11];
        chessArr[1][2] = 1;
        chessArr[2][3] = 2;
        chessArr[5][6] = 2;

        // 原始二维数组
        for (int[] cRaw : chessArr) {
            for (int c : cRaw) {
                System.out.print(c + "\t");
            }
            System.out.println();
        }

        // 稀疏转换后
        int[][] ints = ToSparseArray(chessArr, 0);
        for (int[] cRaw : ints) {
            for (int c : cRaw) {
                System.out.print(c + "\t");
            }
            System.out.println();
        }

        // 恢复数组结构
        int[][] ints1 = restoreArray(ints, 0);
        for (int[] cRaw : ints1) {
            for (int c : cRaw) {
                System.out.print(c + "\t");
            }
            System.out.println();
        }
    }

    /**
     * 还原稀疏数组
     *
     * @param sparseArray  稀疏数组
     * @param defaultValue 默认值
     * @return
     */
    public static int[][] restoreArray(int[][] sparseArray, int defaultValue) {
        // 1、根据第一行数据创建数组
        int[][] ints = new int[sparseArray[0][0]][sparseArray[0][1]];
        // 设置默认值
        for (int[] anInt : ints) {
            Arrays.fill(anInt, defaultValue);
        }
        // 2. 变量sparseArray还原数组
        for (int i = 1; i < sparseArray.length; i++) {
            ints[sparseArray[i][0]][sparseArray[i][1]] = sparseArray[i][2];
        }
        return ints;
    }

    /**
     * 二维数组转稀疏数组
     *
     * @param arr          原始数组
     * @param defaultValue 默认值
     * @return
     */
    public static int[][] ToSparseArray(int[][] arr, int defaultValue) {
        // 1、遍历原始二维数组(获取有多少种值)
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                if (arr[i][j] == defaultValue) continue;
                sum++;
            }
        }

        // 2. 创建稀疏数组
        int raw = 1;
        int[][] SparseArray = new int[sum + 1][3];
        SparseArray[0][0] = arr.length;
        SparseArray[0][1] = arr[0].length;
        SparseArray[0][2] = sum;

        // 给稀疏数组赋值
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {
                if (arr[i][j] == defaultValue) continue;
                SparseArray[raw][0] = i;
                SparseArray[raw][1] = j;
                SparseArray[raw][2] = arr[i][j];
                raw++;
            }
        }
        return SparseArray;
    }
}

二、队列

2.1 引入

在这里插入图片描述

2.2 基本介绍

在这里插入图片描述

2.3 数组模拟队列

在这里插入图片描述

在这里插入图片描述

  • 注:队列中 front指向队首元素的前面一个位置,rear指向队尾元素, maxSize表示队列的最大容量
package com.gyh.queue;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class Queue {
    public static void main(String[] args) {
        ArrayQueue arrayQueue = new ArrayQueue(5);
        arrayQueue.addQueue(1);
        arrayQueue.addQueue(2);
        arrayQueue.addQueue(3);
        arrayQueue.addQueue(4);
        arrayQueue.addQueue(5);

        arrayQueue.getQueue();
        arrayQueue.getQueue();
        arrayQueue.getQueue();
        arrayQueue.getQueue();
        arrayQueue.getQueue();

        // 出现伪溢出的现象(由于出队列后,空间不保存数据)
        arrayQueue.addQueue(6);
    }
}

// 使用数组模拟队列---编写一个ArrayQueue
class ArrayQueue {
    private int maxSize; // 表示数组的最大容量
    private int front = -1; // 指向队列头(即:队首元素的前一个位置)
    private int rear = -1; // 指向队尾元素
    private int[] arr; // 该数组用于存放数据,模拟队列

    // 创建队列的构造器
    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        arr = new int[maxSize]; // 初始化数组
    }

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

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

    // 添加数据到队列
    public void addQueue(int data) {
        // 1、判断是否满
        if (isFull()) {
            System.out.println("队列满不能加入数据");
            return;
        }
        // 2. 添加数据
        arr[++rear] = data;
    }

    // 获取队列的数据,出队列
    public int getQueue(){
        // 判断是否为空
        if(isEmpty()){
            System.out.println("队列已空");
            throw new ArrayIndexOutOfBoundsException("队列为空");
        }

        return arr[++front];
    }

    // 显示当前队列的所有数据
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列为空");
            return;
        }
        for (int i : arr) {
            System.out.println(i);
        }
    }

}

在这里插入图片描述

在这里插入图片描述

package com.gyh.queue;

/**
 * @author Gao YongHao
 * @version 1.0
 * <p>
 * 考虑环状顺序(数组)队列
 */
public class ArrayQueue2 {
    public static void main(String[] args) {
        AnnularArrayQueue annularArrayQueue = new AnnularArrayQueue(5);
        annularArrayQueue.addQueue(1);
        annularArrayQueue.addQueue(2);
        annularArrayQueue.addQueue(3);
        annularArrayQueue.addQueue(4);

        annularArrayQueue.getQueue();
        annularArrayQueue.getQueue();
        annularArrayQueue.getQueue();
        
        annularArrayQueue.addQueue(5);
        annularArrayQueue.addQueue(6);
        annularArrayQueue.addQueue(7);


        annularArrayQueue.showQueue();

    }
}

class AnnularArrayQueue {
    private final int MAX_SIZE;
    private int front = 0; // 采用数据结构书中的用法,指向首个数据的位置
    private int rear = 0; // 采用数据结构书中的用法,指向最后一个数据的下一个
    private int[] arr;

    public AnnularArrayQueue(int maxSize) {
        this.MAX_SIZE = maxSize;
        arr = new int[maxSize];
    }

    public int getQueueLength() {
        return (rear - front + MAX_SIZE) % MAX_SIZE;
    }

    // 判断满
    public boolean isFull() {
        // 采用空出一个空间不填充值的方法,判断是否满
        return (rear + 1) % MAX_SIZE == front;
    }

    // 判断空
    public boolean isEmpty() {
        return front == rear;
    }

    // 添加数据
    public void addQueue(int data) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }

        arr[rear] = data;
        rear = (rear + 1) % MAX_SIZE;
    }

    // 出队
    public int getQueue() {
        if (isEmpty()) {
            throw new ArrayIndexOutOfBoundsException("队列已空");
        }
        int data = arr[front];
        front = (front + 1) % MAX_SIZE;
        return data;
    }

    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列为空");
            return;
        }
        if (rear > front) {
            for (int i = front; i < rear; i++) {
                System.out.println(arr[i]);
            }
        } else {
            for (int i = 0; i < getQueueLength(); i++) {
                System.out.println(arr[(front + i) % MAX_SIZE]);
            }
        }
    }

}

三、链表

3.1 链表介绍

在这里插入图片描述

在这里插入图片描述

3.2 单链表的应用实例

在这里插入图片描述

package com.gyh.Link;

import java.util.Comparator;
import java.util.Objects;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class SingleLink {
    public static void main(String[] args) {

        Hero hero = new Hero(1, "宋江", "及时雨");
        Hero hero1 = new Hero(2, "卢俊义", "玉麒麟");
        Hero hero2 = new Hero(3, "吴用", "智多星");
        Hero hero3 = new Hero(4, "林冲", "豹子头");
        SLink<Hero> heroSLink = new SLink<>(Comparator.comparingInt(Hero::getNo));
        // 无排序添加
        heroSLink.add(hero);
        heroSLink.add(hero3);
        heroSLink.add(hero1);
        heroSLink.add(hero2);
//        heroSLink.list();
        // 有排序添加
//        heroSLink.addByOrder(hero);
//        heroSLink.addByOrder(hero3);
//        heroSLink.addByOrder(hero1);
//        heroSLink.addByOrder(hero2);

        // 修改信息
//        heroSLink.update(new Hero(1, "gyh", "sw"));

        // 删除信息
        heroSLink.remove(new Hero(1, null, null));
        heroSLink.list();


    }
}

class SLink<T> {
    private int length = 0;
    private final Node<T> head = new Node<>(null, null); // 头指针(考虑有头结点的情况)
    private Comparator<T> compara;

    public SLink(Comparator<T> compara) {
        this.compara = compara;
    }

    // 修改结点
    public void update(T d) {
        Node<T> c = head;
        while (c.next != null) {
            int compare = compara.compare(c.next.getData(), d);
            if (compare == 0) {
                c.next.setData(d);
                break;
            }
            c = c.next;
        }

    }

    // 获取链表长度
    public int getLength() {
        return length;
    }

    //
    public void addByOrder(T d) {
        Node<T> c = head;
        boolean flag = true; // 标注最后是否进行赋值
        while (c.next != null) {
            int compare = compara.compare(c.next.getData(), d);
            if (compare > 0) { // 第一次遍历到比当前值大的值(c为当前值的前一个结点)
                break;
            } else if (compare == 0) { // 已经存在,不能添加
                flag = false;
                break;
            }
            c = c.next;
        }
        if (flag) {
            c.next = new Node<>(d, c.next);
            length++;
        } else
            System.out.println("已经存在不能添加");
    }

    // 当不考虑标号的顺序时添加数据
    public void add(T d) {
        Node<T> c = head;
        // 找到最后的结点p
        while (c.next != null) {
            c = c.next;
        }
        c.setNext(new Node<>(d, null));
        length++;
    }

    // 删除
    public void remove(T d) {
        Node<T> c = head;
        int com;
        while (c.next != null) {
            com = compara.compare(c.next.getData(), d);
            if (com == 0) {
                c.next = c.next.next;
                break;
            }
            c = c.next;
        }

    }

    // 显示遍历链表
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        Node<T> c = head.next;
        while (c != null) {
            System.out.println(c.data);
            c = c.next;
        }

    }

}

class Node<T> {
    T data;
    Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Node<T> getNext() {
        return next;
    }

    public void setNext(Node<T> next) {
        this.next = next;
    }
}

class Hero {
    private int no;
    private String name;
    private String nickName;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Hero hero = (Hero) o;
        return no == hero.no &&
                Objects.equals(name, hero.name) &&
                Objects.equals(nickName, hero.nickName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(no, name, nickName);
    }

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

    public int getNo() {
        return no;
    }

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

    public String getName() {
        return name;
    }

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

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
}

3.3 单链表面试题

在这里插入图片描述

package com.gyh.Link;

import java.util.Stack;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class Test {
    public static void main(String[] args) {
        SLinkList<Hero> heroSLinkList = new SLinkList<>();
        Hero hero = new Hero(1, "宋江", "及时雨");
        Hero hero1 = new Hero(2, "卢俊义", "玉麒麟");
        Hero hero2 = new Hero(3, "吴用", "智多星");
        Hero hero3 = new Hero(4, "林冲", "豹子头");

        heroSLinkList.add(hero);
        heroSLinkList.add(hero1);
        heroSLinkList.add(hero2);
        heroSLinkList.add(hero3);

//        heroSLinkList.reverseLink();
        heroSLinkList.printReverseLinkList();
//        heroSLinkList.list();

    }
}

class SLinkList<T> {
    private int length = 0;
    private final Node<T> head = new Node<>(null, null); // 头指针(考虑有头结点的情况)

    public void add(T d) {
        Node<T> c = head;
        // 找到最后的结点p
        while (c.next != null) {
            c = c.next;
        }
        c.setNext(new Node<>(d, null));
        length++;
    }

    // 求单链表汇总有效结点的个数(不计头结点)
    public int getLength() {
        Node<T> c = head;
        if (c.next == null) return 0;
        int n = 0;
        while (c.next != null) {
            n++;
            c = c.next;
        }
        return n;
    }

    // 查找单链表中的倒数第k个结点
    // 1. 编写一个方法,接收head结点,同时接收一个index
    // 2. index 表示是倒数第index个结点
    // 3. 先把链表从头到尾遍历,得到链表的总长度 size
    // 4. 得到 size 后,我们从链表的第一个开始遍历(size - index)
    // 5. 如果找到了则返回该结点,否则返回null
    public T getNode(int index) {
        Node<T> c = head;
        int length = 0;
        while (c.next != null) {
            length++;
            c = c.next;
        }

        if (length == 0) return null;

        c = head.next;
        for (int i = 0; i < length - index; i++) {
            c = c.next;
        }
        return c.getData();
    }

    // 单链表的反转
    // 先定义一个结点 reverseHead=new Node
    // 从头到尾遍历原来的链表,每遍历一个结点,就将其取出,并放在新的链表的最前端(头插法)
    // 原来的链表的 head.next = reverseHead.next
    public void reverseLink() {
        // 1. 没有数据或只有一个数据,就不操作
        if (head.next == null || head.next.next == null) return;


        // 设置反转链表的头结点
        Node<T> reverseHead = new Node<>(null, null);
        Node<T> cur = head.next;// 指向当前的结点
        Node<T> next; // 指向当前结点[cur]的下一个结点
        while (cur != null) {
            // 暂时保存当前结点的下一个结点
            next = cur.next;
            // 头插法
            cur.next = reverseHead.next;
            reverseHead.next = cur;
            // 更新当前结点位置
            cur = next;
        }
        head.next = reverseHead.next;
    }


    // 从尾到头打印单链表[要求 方式1:反向遍历。方式2:Stack栈]
    // 上面的题的要求就是逆序打印单链表
    // 方式1:先将单链表进行反转操作,然后再遍历即可,这样做的问题就是会破坏原来的单链表的结构,不建议
    // * 方式2:可以利用栈这个数据结构,将各个结点压入栈中,然后利用栈的先进后出的特点,就实现了逆序打印
    public void printReverseLinkList() {
        if (head.next == null) return;

        Stack<T> stack = new Stack<>();
        // 遍历压栈
        Node<T> cur = head.next;
        while (cur != null) {
            stack.push(cur.getData());
            cur = cur.next;
        }
        // 出栈打印
        while (stack.size() > 0) {
            System.out.println(stack.pop());
        }
    }
    
    
    
    // 显示遍历链表
    public void list() {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        Node<T> c = head.next;
        while (c != null) {
            System.out.println(c.data);
            c = c.next;
        }

    }
}

3.4 双向链表应用实例

在这里插入图片描述

在这里插入图片描述

package com.gyh.Link;

import java.util.LinkedList;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class DoubleLinkedListDemo {
    public static void main(String[] args) {
    }
}

class DoubleLinkedList<T> {
    // 头结点
    private final DuNode<T> head = new DuNode<>();

    // 删除
    public void remove(T d) {
        // 判断空
//        ....
        DuNode<T> cur = head;
        while (cur.next != null) {
            if (d.equals(cur.next.data)) {
                // 删除操作
                cur.next = cur.next.next;
                if (cur.next != null) cur.next.pre = cur;
            }
            cur = cur.next;
        }
    }

    // 添加
    public void add(T d) {
        DuNode<T> cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        // cur为最后一个元素
        cur.next = new DuNode<>(d, cur, null);

    }

}

class DuNode<T> {
    T data;
    DuNode<T> pre;
    DuNode<T> next;

    public DuNode() {
    }

    public DuNode(T data, DuNode<T> pre, DuNode<T> next) {
        this.data = data;
        this.pre = pre;
        this.next = next;
    }
}

3.5 单向环形链表

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.gyh.Link;

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

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class SingleCircularLinkedListDemo {
    public static void main(String[] args) {
        SingleCircularLinkedList<Integer> integerSingleCircularLinkedList = new SingleCircularLinkedList<>();
        for (int i = 0; i < 10; i++) {
            integerSingleCircularLinkedList.add(i);
        }

        List<Integer> josephu = integerSingleCircularLinkedList.josephu(4, 6);
        josephu.forEach(System.out::println);

    }
}

/**
 * 1. 创建尾结点
 * 2. 每次定位至数到 m 的结点的上一个结点位置(只有这样才可以更新信息)
 *
 * @param <T>
 */
class SingleCircularLinkedList<T> {
    // 不设置头结点(设置指向尾元素的指针)
    private Node<T> last;

    // 判断空
    public boolean isEmpty() {
        return last == null;
    }

    // 添加数据
    public void add(T d) {
        // 为空则直接创建
        if (isEmpty()) {
            last = new Node<>(d, null);
            last.setNext(last);
            return;
        }
        last.next = new Node<T>(d, last.next);
        last = last.next;
    }

    // 链表长度
    public int getLength() {
        // 为空则返回
        if (isEmpty()) return 0;
        int n = 1;
        Node<T> cur = last.next;
        while (cur != last) {
            n++;
            cur = cur.next;
        }
        return n;
    }

    // 约瑟夫问题
    public List<T> josephu(int k, final int m) {
        // 1 <= k <= n
        int n = getLength();
        assert k >= 1 && k <= n;
        if (n == 1) return Collections.singletonList(last.getData());

        // 循环出值
        List<T> out = new ArrayList<>();

        // pre初始为最后一个元素
        Node<T> pre = last;
        // 定位到 编号为 k 的前面一个人
        for (int i = 1; i < k; i++) {
            pre = pre.next;
        }
        // 报数到m
        while (true) {
            if (last.next == last) { // 只有一个元素
                out.add(last.getData());
                last = null;
                break;
            }

            // 获取数到m的结点的前面一个结点
            for (int i = 1; i < m; i++) {
                pre = pre.next;
            }

            // 从循环链表中删除该结点
            if (pre.next == last) last = pre; // 如果删除的是最后一个结点则将last指向pre
            out.add(pre.next.getData()); // 将删除的结点值保存
            pre.next = pre.next.next;
        }
        return out;

    }
}

四、栈

4.1 基本介绍

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2 数组模拟栈

在这里插入图片描述

// 数组实现栈
package com.gyh.stack;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class StackDemo {
    public static void main(String[] args) {
        ArrayStack arrayStack = new ArrayStack(5);
        for (int i = 0; i < 5; i++) {
            arrayStack.push(i);
        }
        arrayStack.list();
        System.out.println(arrayStack.pop());
        System.out.println(arrayStack.pop());
        System.out.println(arrayStack.pop());
        System.out.println(arrayStack.pop());
        System.out.println(arrayStack.pop());

    }
}

class ArrayStack {
    private final int maxSize;
    private int[] arr;
    private int stackTop; // stackTop表示最后一个元素的下一个位置

    public ArrayStack(int size) {
        this.maxSize = size;
        this.arr = new int[size];
        this.stackTop = 0;
    }


    // 判满
    public boolean isFull() {
        return stackTop == maxSize;
    }

    // 判空
    public boolean isEmpty() {
        return stackTop == 0;
    }

    // 压栈
    public void push(int n) {
        if (isFull()) {
            System.out.println("已满");
            return;
        }
        arr[stackTop++] = n;
    }

    // 出栈
    public int pop() {
        if (isEmpty()) throw new RuntimeException("栈已空");
        return arr[--stackTop];
    }

    // 遍历栈
    public void list(){
        if(isEmpty()) {
            System.out.println("栈空");
            return;
        }

        for (int i : arr) {
            System.out.println(i);
        }
    }
}

4.3 链表模拟栈

// 链表实现栈
package com.gyh.stack;


/**
 * @author Gao YongHao
 * @version 1.0
 */
public class StackDemo2 {
    public static void main(String[] args) {
        LinkedStack<Integer> integerLinkedStack = new LinkedStack<Integer>();
        for (int i = 0; i < 5; i++) {
            integerLinkedStack.push(i);
        }
        integerLinkedStack.list();

        System.out.println(integerLinkedStack.pop());
        System.out.println(integerLinkedStack.pop());
        System.out.println(integerLinkedStack.pop());
        System.out.println(integerLinkedStack.pop());
        System.out.println(integerLinkedStack.pop());

    }
}

class LinkedStack<T> {
    // 设置栈顶指针
    private Node<T> stackTop;

    // 判断空
    public boolean isEmpty() {
        return stackTop == null;
    }

    // 压栈(使用头插法)
    public void push(T d) {
        // 第一个元素
        if (stackTop == null) {
            stackTop = new Node<>(d, null);
            return;
        }
        stackTop = new Node<>(d, stackTop);
    }

    // 出栈
    public T pop() {
        if (isEmpty()) throw new RuntimeException("栈空");
        T d = stackTop.data;
        stackTop = stackTop.next;
        return d;
    }

    // 遍历
    public void list() {
        if (isEmpty()) System.out.println("栈空");
        Node<T> cur = stackTop;
        while (cur != null) {
            System.out.println(cur.data);
            cur = cur.next;
        }
    }

}

class Node<T> {
    T data;
    Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }
}

4.4 栈实现综合计算器

在这里插入图片描述

在这里插入图片描述

package com.gyh.stack;


import java.util.Arrays;
import java.util.List;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class CalculatorDemo {
    public static void main(String[] args) {

        String expression = "70*2*2-5+1-5+3-4";

        // 创建数栈
        LinkedStack<Double> numStack = new LinkedStack<>();

        // 创建操作符栈
        LinkedStack<Character> operStack = new LinkedStack<>();

        double n1, n2, res = 0;
        char oper = ' ', c;
        char[] chars = expression.toCharArray();
        int length = chars.length;
        StringBuffer numBuf = new StringBuffer();

        for (int i = 0; i < length; i++) {
            c = chars[i];
            // 符号栈不为空
            if (Calculator.isOper(c) && !operStack.isEmpty()) {
                // 栈顶的操作符
                oper = operStack.pop();
                if (Calculator.priority(oper) >= Calculator.priority(c)) { // 需要计算
                    n1 = numStack.pop();
                    n2 = numStack.pop();
                    res = Calculator.cal(n1, n2, oper);
                    // 将计算结果压栈
                    // 当前符号在后面压栈
                    numStack.push(res);
                } else {
                    // 不操作则把操作符放回去,并执行下面的压栈
                    operStack.push(oper);
                }
            }


            // 如果是符号则压入符号栈中
            if (Calculator.isOper(c)) {
                operStack.push(c);
            } else { // 压入数栈
                // 观察当前字符的下一个位置是否也为数字(考虑多位数的情况)
                numBuf.append(c);
                if (i == length - 1 || Calculator.isOper(chars[i + 1])) {
                    numStack.push(Double.parseDouble(numBuf.toString()));
                    numBuf = new StringBuffer();
                }
            }
        }

        // 遍历符号栈将最终的结果输出
        while (operStack.size() > 0) {
            n1 = numStack.pop();
            n2 = numStack.pop();
            res = Calculator.cal(n1, n2, operStack.pop());
            numStack.push(res); // 结果压栈
        }
        System.out.println(expression + "=" + res);


    }
}

class Calculator {
    private static final List<Character> opters = Arrays.asList('+', '-', '*', '/');


    public static double cal(double num1, double num2, int oper) {

        double res = 0; // 用于存放计算的结果
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = num2 - num1;
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                res = num2 / num1;
                break;
            default:
                throw new RuntimeException("操作符异常");

        }
        return res;


    }

    public static boolean isOper(int oper) {
        return isOper((char) oper);
    }

    public static boolean isOper(char oper) {
        return opters.contains(oper);
    }


    public static int priority(int oper) {
        if (!isOper(oper)) {
            return -1;
        }

        switch (oper) {
            case '*':
            case '/':
                return 1;
            case '+':
            case '-':
                return 0;
            default:
                return -1;
        }

    }
}

4.5 前缀、中缀、后缀表达式(逆波兰表达式)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.6 逆波兰计算器实现

在这里插入图片描述

package com.gyh.stack;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class InversePolishExpressionDemo {
    public static void main(String[] args) {
        String expression = "4 5 * 8 - 60 + 8 2 / +"; // 使用空格区分多位数
        // 创建数栈
        LinkedStack<Integer> numStack = new LinkedStack<>();
        // 从左至右扫描
        String[] chars = expression.split(" ");
        int n1, n2, res = 0;
        for (String c : chars) {
            // 判断是否为操作符
            if (Calculator.isOper(c.charAt(0))) {
                n1 = numStack.pop();
                n2 = numStack.pop();
                res = Calculator.cal(n1, n2, c.charAt(0));
                numStack.push(res); // 计算结果压栈
            } else { // 为数
                numStack.push(Integer.parseInt(c));
            }
        }
        System.out.println(expression + "=" + res);
    }
}

4.7 中缀表达式转后缀表达式(*)

在这里插入图片描述

在这里插入图片描述

package com.gyh.stack;

import java.util.Arrays;

/**
 * @author Gao YongHao
 * @version 1.0
 * <p>
 * 中缀表达式转后缀表达式
 */
public class MidToReverseDemo {
    public static void main(String[] args) {
        // 中缀表达式
        String expression = "1 + ( ( 2 + 3 ) * 4 ) - 5";
        // 按空格读取各字符
        String[] strs = expression.split(" ");


        // 创建保存运算符的栈和中间栈
        LinkedStack<String> linkedStack = new LinkedStack<>();
        LinkedStack<String> tempStack = new LinkedStack<>();

        // 遍历字符
        for (String str : strs) {

            if (!isNumeric(str)) { // 为运算符
                while (true) {
                    // 当前栈中没有元素或存在元素 ( , 直接压栈
                    if (str.equals("(") || linkedStack.isEmpty() || linkedStack.peek().equals("("))
                        linkedStack.push(str);
                    else if (str.equals(")")) { // 如果是右括号则依次出栈至 '(' 位置并压入中间栈
                        while (!linkedStack.peek().equals("(")) {
                            tempStack.push(linkedStack.pop());
                        }
                        linkedStack.pop(); // 将对应的左括号弹出


                    } else if (Calculator.priority(str.charAt(0)) > Calculator.priority(linkedStack.peek().charAt(0))) { // 如果当前的字符优先级大于存在的元素则压栈
                        linkedStack.push(str);
                    } else { // 将当前栈顶元素出栈并压入中间栈,并重新将当前的字符作判定
                        tempStack.push(linkedStack.pop());
                        continue;
                    }
                    break;
                }

            } else { // 为数
                // 压入中间栈
                tempStack.push(str);
            }
        }

        // 若字符栈非空则依次出栈并压入中间栈(此处为便于反向遍历,将中间栈的数据压入运算符栈)
        while (!tempStack.isEmpty()) {
            linkedStack.push(tempStack.pop());
        }

        // 打印得到后缀表达式
        while (!linkedStack.isEmpty()) {
            System.out.print(linkedStack.pop() + " ");
        }


    }

    public static boolean isNumeric(String str) {
        for (int i = str.length(); --i >= 0; ) {
            int chr = str.charAt(i);
            if (chr < 48 || chr > 57)
                return false;
        }
        return true;
    }
}

五、递归

5.1 递归的应用场景

在这里插入图片描述

5.2 递归的调用机制

在这里插入图片描述

在这里插入图片描述

5.3 递归可以解决的问题

在这里插入图片描述

5.4 递归需要遵守的重要规则

在这里插入图片描述

5.5 迷宫问题

在这里插入图片描述

package com.gyh.recursion;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class MiGong {
    public static void main(String[] args) {

        // 构建迷宫
        int[][] matrix = new int[8][7];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (i == 0 || i == matrix.length - 1 || j == 0 || j == matrix[i].length - 1) {
                    matrix[i][j] = 1;
                }
            }
        }
        matrix[2][2] = 1;
        matrix[3][1] = 1;
        matrix[3][2] = 1;
        // 打印迷宫
        for (int[] ints : matrix) {
            for (int anInt : ints) {
                System.out.print(anInt + "\t");
            }
            System.out.println();
        }
        System.out.println("*******************");
        find(matrix, 1, 1);
        // 打印路径
        for (int[] ints : matrix) {
            for (int anInt : ints) {
                System.out.print(anInt + "\t");
            }
            System.out.println();
        }
    }

    /**
     * 寻找迷宫出口的方法
     * 说明
     * 1. map  表示地图
     * 2. i,j  表示从地图的哪个位置开始出发
     * 3. 如果小球能到 map[6][5] 的位置,则表示通路找到
     * 4. 约定:当 map[i][j] 为 0 时,表示该点没有走过;当为 1 表示是墙;2 表示通路可以走;3 表示该位置已经走过但是走不通
     * 5. 在走迷宫时,需要确定一个策略(方法)下->右->上->左
     *
     * @param map 迷宫矩阵
     * @param i   横坐标值
     * @param j   纵坐标值
     */
    public static boolean find(int[][] map, int i, int j) {
        if (map[6][5] == 2) { // 设置终点已找到时
            return true;
        }

        // 墙、前面已经走过(还在测试中)、前面已走过(走不通)
        if (map[i][j] == 1 || map[i][j] == 2 || map[i][j] == 3) {
            return false;
        }


        // 假定该点可以走通
        map[i][j] = 2;
        // 按照策略 下 -> 右 -> 上 -> 左
        if (find(map, i + 1, j)) {
            return true;
        } else if (find(map, i, j + 1)) {
            return true;
        } else if (find(map, i - 1, j)) {
            return true;
        } else if (find(map, i, j - 1)) {
            return true;
        } else {
            map[i][j] = 3;
            return false;
        }
    }
}

5.6 八皇后问题

在这里插入图片描述

package com.gyh.recursion;

/**
 * @author Gao YongHao
 * @version 1.0
 */
public class EightQueenProblem {
    // 定义一个max表示共有多少个皇后
    static int max = 8;
    // 定义数组array,保存皇后放置位置的结果,比如 arr = {0,4,7,5,2,6,1,3}
    static int[] array = new int[max];

    static int count = 0;

    // 编写一个方法,放置第n个皇后
    // 特别注意:check 是每一个递归时,进入 check 中都有 for (int i = 0; i < max; i++) ,因此会回溯(会把所有情况走一遍)
    private static void check(int n) {
        if (n == max) { // n=8 其实第八个皇后就放好了
            printArray();
            count++; // 解法+1
            return;
        }

        for (int i = 0; i < max; i++) {
            // 从第一列的情况开始遍历
            array[n] = i;
            if (!isComplicit(n)) {
                // 不冲突则继续 check
                check(n + 1);
            }
            // 冲突,就继续执行 下一情况
        }


    }

    // 表示是否冲突
    private static boolean isComplicit(int n) {

        for (int i = 0; i < n; i++) {
            // 条件一:比较是否存在同列
            // 条件二:比较是否存在 行差与列差 相同的元素(即同对角线)
            if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
                return true;
            }
        }
        return false;
    }

    private static void printArray() {
        for (int a : array) {
            System.out.print(a + "\t");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // 打印最后的摆放结果
        check(0);
        System.out.println("一共有" + count + "种算法");
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2120558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PMP--一、二、三模--分类--14.敏捷--技巧--宣言

文章目录 技巧一模14.敏捷--宣言--重视实在的--工作的软件大于流程和文档19、 [单选] 一个关键干系人坚持团队用正式的文档广泛地记录软件代码。产品负责人解释说&#xff0c;虽然一定数量的文档是必要的&#xff0c;但团队成员最好把时间花在开发软件上&#xff0c;因为这对客…

Java 每日一刊(第2期):搭建开发环境

文章目录 JVM、JRE、JDKJVM&#xff08;Java Virtual Machine&#xff0c;Java 虚拟机&#xff09;JRE&#xff08;Java Runtime Environment&#xff0c;Java 运行时环境&#xff09;JDK&#xff08;Java Development Kit&#xff0c;Java 开发工具包&#xff09;JVM、JRE、JD…

1765asp.net古镇旅游网站VS开发sqlserver数据库web结构c#编程web网页设计

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的可以…

Python | Leetcode Python题解之第395题至少有K个重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution:def longestSubstring(self, s1: str, k: int) -> int:if k 1: return len(s1)n len(s1)res 0for c in range(1, len(set(s1)) 1):# 滑窗中字母种类个数恰好为 cfreq Counter()l cnt tcnt 0 for r, ch in enu…

安防监控/视频汇聚平台EasyCVR无法启动并报错“error while loading shared libraries”,如何解决?

安防监控/视频汇聚平台EasyCVR视频管理系统以其强大的拓展性、灵活的部署方式、高性能的视频能力和智能化的分析能力&#xff0c;为各行各业的视频监控需求提供了优秀的解决方案。通过简单的配置和操作&#xff0c;用户可以轻松地进行远程视频监控、存储和查看&#xff0c;满足…

AI基础 L10 Adversarial Search I 对抗性搜索

Multiagent Environments In multiagent environments, each agent must: — Consider everyone else’s actions — Coordinate in order to act coherently 多个智能体&#xff08;agent&#xff09;相互作用&#xff0c;每个智能体都具有自己的目标和行动策略。在多智能体环…

C++ | Leetcode C++题解之第396题旋转图像

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxRotateFunction(vector<int>& nums) {int f 0, n nums.size();int numSum accumulate(nums.begin(), nums.end(), 0);for (int i 0; i < n; i) {f i * nums[i];}int res f;for (int i …

【经纬度坐标系、墨卡托投影坐标系和屏幕坐标系转换详解】

地图坐标系转换详解 1. 引言2. 坐标系定义2.1 经纬度坐标系2.2 墨卡托投影坐标系3.3 屏幕坐标系 2. 坐标系间的转换2.1 经纬度坐标系到墨卡托投影坐标系2.2 墨卡托投影坐标系到经纬度坐标系2.3 墨卡托投影坐标系到屏幕坐标系2.4 屏幕坐标系到墨卡托投影坐标系2.5 经纬度坐标系到…

mfc140u.dll文件错误的相关修复方法,4种方法修复mfc140u.dll

当面对基于Microsoft Visual C开发的应用程序出现启动或运行失败时&#xff0c;mfc140u.dll文件错误往往是罪魁祸首之一。这个动态链接库&#xff08;DLL&#xff09;文件对于许多Windows软件来说是必不可少的&#xff0c;因为它包含了重要的编程代码和数据。如果发现此文件损坏…

数据结构(7.1)——查找的基本概念

基本概念 查找——在数据结构集合中寻找满足某种条件的数据元素的过程称为查找 查找表&#xff08;查找结构&#xff09;——用于查找的数据集合称为查找表&#xff0c;它由同一类型的数据元素(或记录)组成 关键字——数据元素中唯一标识该元素的某个数据项的值&#xff0c;…

【C++二分查找 贪心】1488. 避免洪水泛滥

本文涉及的基础知识点 C二分查找 贪心&#xff1a;决策包容性 LeetCode1488. 避免洪水泛滥 你的国家有无数个湖泊&#xff0c;所有湖泊一开始都是空的。当第 n 个湖泊下雨前是空的&#xff0c;那么它就会装满水。如果第 n 个湖泊下雨前是 满的 &#xff0c;这个湖泊会发生 洪…

【Canvas与艺术】菊花孔雀螺旋

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>菊花孔雀螺旋</title><style type"text/css">…

HTML 揭秘:HTML 编码快速入门

HTML 揭秘&#xff1a;HTML 编码快速入门 一 . 前端知识介绍二 . HTML 介绍三 . HTML 快速入门四 . HTML 编辑器 - VSCode4.1 插件安装4.2 修改主题配色4.3 修改快捷键4.4 设置自动保存4.5 创建 HTML 文件4.5 书写 HTML 代码4.6 常见快捷键 五 . 基础标签5.1 字体标签5.1.1 col…

SpringCloud之配置中心git示例

SpringCloud之配置中心git示例 随着线上项目变的日益庞大&#xff0c;每个项目都散落着各种配置文件&#xff0c;如果采用分布式的开发模式&#xff0c;需要的配置文件随着 服务增加而不断增多。 某一个基础服务信息变更&#xff0c;都会引起一系列的更新和重启&#xff0c;…

探索有向图与无向图中深度优先搜索(DFS)的边类型——3×3 网格分析

探索有向图与无向图中深度优先搜索(DFS)的边类型——33 网格分析 一、基本概念二、有向图中的 DFS 边类型分析三、有向图 DFS 的 C 代码实现在图的深度优先搜索(DFS)过程中,边的分类对于理解算法的执行流程及其复杂性至关重要。在有向图和无向图中,DFS 过程中遇到的边可以…

【Kafka】分区与复制机制:解锁高性能与容错的密钥

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《大数据前沿&#xff1a;技术与应用并进》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、kafka简介 2、kafka使用场景 二、Kafka消息可…

【短距离通信】【WiFi】精讲WiFi P2P技术特点及拓扑组成

1. WiFi P2P技术特点 1.1 WiFi P2P定义 WiFi P2P&#xff08;WiFi Peer-to-Peer&#xff09;&#xff0c;也被称为WiFi Direct&#xff0c;是是WiFi联盟发布的一种无线通信技术&#xff0c;允许设备直接相互连接&#xff0c;而无需通过传统的WiFi接入点&#xff08;AP&#x…

【Python篇】matplotlib超详细教程-由入门到精通(下篇)

文章目录 前言第六部分&#xff1a;保存与导出图表6.1 保存为图片文件示例&#xff1a;保存图表为 PNG 文件解释&#xff1a;关键点&#xff1a; 6.2 保存为高分辨率图片示例&#xff1a;保存为高分辨率图片解释&#xff1a; 6.3 保存为不同文件格式示例&#xff1a;保存为不同…

SpringCloudAliaba生成式JavaAI应用开发文生问答音像

采用SpringCloudAliabaAI型式大模型LLM&#xff0c;进行生成式JavaAI应用开发&#xff0c;实现文生问答、图像和语音合成&#xff0c;Web应用页面交互展现。SpringBootGradle软件框架&#xff0c;Idea集成开发环境&#xff0c;API_Post嵌入插件一体测试。 1 工效展示[文生-答/图…

牛津大学:自动发现跨领域高阶抽象泛化框架

随着LLM等技术的快速发展&#xff0c;进一步的抽象和泛化问题亦被看作是未来AI甚至AGI发展的关键。 然而鉴于当前不论是LLM下的自回归AR条件预测Gen方法还是CV领域的diffusion扩散Gen方法&#xff0c;甚至于传统DNN的无监督学习模式&#xff0c;在所涉及的更多通用或领域场景下…