3. java数据结构与算法 ——第五章 栈(栈,逆波兰表达式,中缀后缀) --师承尚硅谷韩顺平

约瑟夫问题

单项环形链表:
尾部添加,添加进来的结点记得指向头结点实现环形
删除结点还是常规,始终找到删除节点的前一个结点

上代码:

package com.DataStructure._04LinkedList;

/**
 * 单向环形链表
 * 约瑟夫问题:
 * Josephu 问题为:设编号为1,2,...n的n个人围坐一圈,约定编号为k (1<=k<=n)的人从1开始报数,
 *         数到m的那个人出列,它的下一位接着数到m 的那个人又出列,
 *         依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
 */

public class JosepfuCircleSingleListDemo {
    public static void main(String[] args) {
        JosepfuCircleSingleList josepfuCircleSingleList = new JosepfuCircleSingleList();
        josepfuCircleSingleList.addBoy(5);
        josepfuCircleSingleList.showBoys();

        System.out.println("----出圈");
        josepfuCircleSingleList.countBoy(1,2,5);//2-4-1-5-3
    }
}

//创建一个单项环形链表
class JosepfuCircleSingleList {
    /**
     * 几个重要的点
     * first结点---第一个结点,固定不变的
     * curBoy结点---尾结点,指向新节点后定时挪动到尾部
     */

    private Boy first = null;

    public void addBoy(int num) {
        if (num < 1) {
            System.out.println("num数据不正确");
            return;
        }

        Boy curBoy = null;//临时指针
        for (int i = 1; i <= num; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                first.next = first;//闭环
                curBoy = first;//
            } else {
                curBoy.next = boy;
                boy.next = first;
                curBoy = boy;
            }
        }
    }

    //遍历
    void showBoys() {
        Boy tempBoy = first;
        while (true) {
            System.out.println(tempBoy);
            if (tempBoy.next == first) {
                break;
            }
            tempBoy = tempBoy.next;
        }
    }


    //报数和出列

    /**
     * @param startNo  开始小孩是几?
     * @param countNum 规定报数是?
     * @param nums     全部小孩的总数
     *                 first -- 出列
     *                 helper -- first前面一个(要删除结点的前一个位置)
     */
    public void countBoy(int startNo, int countNum, int nums) {
        //数据校验
        if (startNo < 1 || first == null || startNo > nums) {
            System.out.println("参数错误!");
            return;
        }
        //first定位到startNo
        for (int j = 0; j < startNo - 1; j++) {
            //first默认是第一个小孩,移动startNo-1次 到达要求的starNo的位置
            first = first.next;
        }
        //创建辅助结点helper,起始位置在first前一个
        // (first就是要删除的结点,本质就是删除结点的前一个结点),后面一个一直保证helper在first前一个位置
        Boy helper = first;
        while (true) {
            if (helper.next == first) {
                break;
            }
            helper = helper.next;
        }
        //按照规则出列,直到只有一个结点
        while (helper != first) {
            //根据报数,移动countNum-1
            for (int j = 0; j < countNum - 1; j++) {
                first = first.next;
                helper = helper.next;
            }
            //出列(删除结点)
            System.out.println(first);
            first = first.next;
            helper.next = first;
        }
        System.out.println(helper);//最后一个小孩节点
    }
}

//小孩的结点
class Boy {
    public int no;
    public 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;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "no=" + no +
                '}';
    }
}

栈(stack)

在这里插入图片描述

特点:先入后出
入栈push 出战pop

1.栈的快速入门

1.用数组实现

上代码:

package com.DataStructure._05Stack;

import java.util.Scanner;

/**
 * 数组模拟栈!
 * 小结:
 * 问题:
 * 1.遇到一个有意思的问题,scanner的next() nextLine()的问题
 * next() 以空格回车tab为结束判断依据
 * nextLine() 以回车 为结束
 * 如果代码中现有next()后有nextLine(),然后前一个以回车结束,,
 * --后面的nextLine()会不执行,但不代表没数据!他的数据就是回车!
 */

public class ArrayStackDemo {
    public static void main(String[] args) {
        //搞个菜单测试 栈
        ArrayStack2 arrayStack = new ArrayStack2(5);
        String key = "";
        boolean flag = true;
        Scanner sc = new Scanner(System.in);
        while (flag) {
            System.out.println("******** 栈 ********");
            System.out.println("--show(s)");
            System.out.println("--push(1)");
            System.out.println("--pop(2)");
            System.out.println("--exit(e)");
            System.out.print("--输出选择:");
            key = sc.next();
            switch (key) {
                case "s": {
                    arrayStack.show();
                    break;
                }
                case "1": {
                    try {
                        System.out.print("输入添加的数:");
                        int value = sc.nextInt();
                        arrayStack.push(value);
                        break;
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                }
                case "2": {
                    try {
                        int value = arrayStack.pop();
                        System.out.println(value);
                        break;
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                }
                case "e": {
                    sc.close();
                    flag = false;
                    break;
                }
                default: {
                    System.out.println("error input!!");
                }
                break;
            }
        }
        System.out.println("程序退出了!");
    }
}

//栈操作
class ArrayStack {
    private int maxsize;
    private int[] stack;
    private int top = -1;

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

    //栈空?
    boolean isEmpty() {
        return top == -1 ? true : false;
    }

    //栈满?
    boolean isFull() {
        return top == maxsize - 1 ? true : false;
    }

    //入栈
    public void push(int value) {
        if (isFull()) {
            throw new RuntimeException("栈满!");
        }
        top++;
        stack[top] = value;
    }

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

    //遍历
    public void show() {
        //从栈顶还是遍历
        for (int i = top; i >= 0; i--) {
            System.out.println(stack[i]);
        }
    }
}

2.用链表实现栈 (我这里用的尾插法,后来想想头插法效率更高

上代码:

package com.DataStructure._05Stack;

import org.junit.Test;

import java.util.List;
import java.util.Scanner;

/**
 * 栈--单链表实现
 */
public class LinkedListStackDemo {
    public static void main(String[] args) {
        ListNode listNode1 = new ListNode(1);
        ListNode listNode2 = new ListNode(2);
        ListNode listNode3 = new ListNode(3);
        ListNode listNode4 = new ListNode(4);
        ListNode listNode5 = new ListNode(5);

        LinkedListStack linkedListStack = new LinkedListStack();
        linkedListStack.push(listNode1);
        linkedListStack.push(listNode2);
        linkedListStack.push(listNode3);
        linkedListStack.push(listNode4);
        linkedListStack.push(listNode5);

        System.out.println("---12345入栈");
        linkedListStack.show();

        System.out.println("-----54321逐个出栈");
        for (int i = 0; i < 5; i++) {
            int value = linkedListStack.pop();
            System.out.println(value);
        }

        //linkedListStack.show();
    }
}


class LinkedListStack {
    ListNode head = new ListNode(0);

    public LinkedListStack() {
    }

    //栈空?栈满?
    boolean isEmpty() {
        return head.next == null ? true : false;
    }
    //boolean isFull(){} 链表应该没有栈满的说法

    //入栈
    public void push(ListNode Node) {

        //temp指向最后一个结点 准备尾插法
        ListNode temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = Node;
    }

    //pop出栈--删除结点
    int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空!");
        }
        //temp记录尾结点,tempfer记录temp的前一个结点
        ListNode temp = head.next;
        ListNode tempfer = head;
        while (temp.next != null) {
            temp = temp.next;
            tempfer = tempfer.next;
        }
        int no = temp.no;//出栈的值

        //删除
        tempfer.next = null;

        return no;
    }

    //遍历
    public void show() {
        if (isEmpty()) {
            throw new RuntimeException("栈空!");
        }
        ListNode temp = head.next;
        while (temp.next != null) {
            System.out.println(temp);
            temp = temp.next;
        }
        System.out.println(temp);//最后一个
    }
}

class ListNode {
    int no;
    public ListNode next;

    public ListNode() {
    }

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

    @Override
    public String toString() {
        return "ListNode{" +
                "no=" + no +
                '}';
    }
}

简单计算机(个位数加减乘除)

上代码:

package com.DataStructure._05Stack;

public class CalculatorDemo {
    public static void main(String[] args) {

        // 3 + 2 * 6 - 2 = ?
        String experssion = "3+2*6-2";
        //创建两个栈,数栈,符号栈
        ArrayStack2 numStack = new ArrayStack2(10);
        ArrayStack2 operStack = new ArrayStack2(10);

        int index = 0;
        int num1 = 0;  //
        int num2 = 0;
        int oper = 0;
        int res = 0;

        char ch = ' ';

        while (true) {
            ch = experssion.substring(index, index + 1).charAt(0);
            //判断是运算符?  否则是数字
            if (operStack.isOper(ch)) {
                if (!operStack.isEmpty()) {
                    //非空,判断优先级
                    if (operStack.priority(ch) >= operStack.priority(operStack.peek())) {
                        //优先级比站内优先级低,取出书栈的两个数,和符栈的一个运算符,计算后的结果入数栈;随后该符号ch入栈
                        num1 = numStack.pop();//先出栈1
                        num2 = numStack.pop();//后出栈2
                        oper = operStack.pop();
                        res = operStack.cal(num1, num2, oper);

                        //计算的结果入数栈
                        numStack.push(res);

                        //该符号ch入符栈
                        operStack.push(ch);
                    } else {
                        //ch的优先级高,直接入栈
                        operStack.push(ch);
                    }
                } else {
                    //operStack为空,直接入符栈
                    operStack.push(ch);
                }

            } else {
                numStack.push(ch - 48);//ascall表个 字符数字比对应的数字大48
            }

            //判断索引,防止超出字符串长度
            index++;
            if (index >= experssion.length() ) {
                break;
            }
        }

        //剩下的栈 完成计算
        while (!operStack.isEmpty()) {
            num1 = numStack.pop();//先出栈1
            num2 = numStack.pop();//后出栈2
            oper = operStack.pop();
            res = operStack.cal(num1, num2, oper);

            //计算的结果入数栈
            numStack.push(res);
        }

        int result = numStack.pop();
        System.out.println(result);


    }
}

//创建栈 --这里使用前面创建好的数组栈
//栈操作
class ArrayStack2 {
    private int maxsize;
    private int[] stack;
    private int top = -1;

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

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

    //栈满?
    boolean isFull() {
        return top == maxsize - 1;
    }

    //入栈
    public void push(int value) {
        if (isFull()) {
            System.out.println("栈满!");
            return;
        }
        top++;
        stack[top] = value;
    }

    //出栈
    int pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈空~ 没有数据~~");
        }
        int value = stack[top];
        top--;
        return value;
    }

    //遍历
    public void show() {
        //从栈顶还是遍历
        for (int i = top; i >= 0; i--) {
            System.out.println(stack[i]);
        }
    }

    //peek看一眼栈顶,但不取出
    public int peek() {
        return stack[top];
    }

    //优先级规定,优先级越高,数字越小
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 2;
        } else {
            return -1;//假定只有+ - * / 运算
        }
    }

    //判断运算符?数字?
    boolean isOper(char val) {
        if (val == '+' || val == '-' || val == '*' || val == '/') {
            return true;
        }
        return false;
    }

    //计算方法
    public int cal(int num1, int num2, int oper) {//1先出栈,2后出栈的顺序
        int res = 0;
        switch (oper) {
            case '+': {
                res = num1 + num2;
                break;
            }
            case '-': {
                res = num2 - num1;
                break;
            }
            case '*': {
                res = num1 * num2;
                break;
            }
            case '/': {
                res = (num2 / num1);
                break;
            }
            default:
                break;
        }
        return res;
    }
}

2.栈 进阶

1.中缀, 后缀, 相互转换

前缀用的不多说了
中缀就是日常人的运算行为: 1+((23+3) * 4)-5
后缀(又称为逆波兰表达式)计算机最喜欢的:1 2 3 + 4 * + 5 -

中缀转后缀,(使用栈) 完成整数计算:
思路
在这里插入图片描述

上代码:

package com.DataStructure._05Stack;

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

/**
 * 逆波兰表达式 ---- 后缀表达式
 * <p>
 * 中缀转后缀步骤:
 */

/**
 * 中缀表达式 转化 后缀表达式
 * 说明: 1+((2+3)*4)-5  =>   1 2 3 + 4 * + 5 -
 */
public class PolandNotationDemo {
    public static void main(String[] args) {
/*

        String suffixExperssion = "3 4 + 5 * 6 -";//-->29  空格隔开,方便处理

        List<String> list = getListString_suffix(suffixExperssion);
        System.out.println("---后缀表达式如下");
        System.out.println(list);

        System.out.println("---计算结果");
        int re = calculator(list);
        System.out.println(re);
*/

        System.out.println("---------------");

        String infixExperssion = "1+((23+3)*4)-5";
        List<String> list_infix = getListString_infix(infixExperssion);
        System.out.println(infixExperssion + "\n" + "--->中缀表达式转成集合..");
        System.out.println(list_infix);

        List<String> suffixlist = parseSuffixExpressionList(list_infix);//中转后
        System.out.println("------中缀转换后缀表达式,如下....");
        System.out.println(suffixlist);
        int calculator = calculator(suffixlist);
        System.out.println("后缀表达式计算结果---> " + calculator);

    }

    //中缀表达式放在集合中(遍历字符串,放在集合)  1+((2+3)*4)-5
    public static List<String> getListString_infix(String s) {

        ArrayList<String> list = new ArrayList<>();

        int i = 0;
        while (i < s.length()) {
            //判断是数字or运算符?
            char c = s.charAt(i);
            if (c < 48 || c > 57) {//运算符
                list.add("" + c); //转成字符串并且加入到list

            } else {//数字
                //考虑多位数
                int j = i + 1;
                String cc = "" + c;
                while (j < s.length() && s.charAt(j) >= 48 && s.charAt(j) <= 57) {
                    cc += s.charAt(j);//cc就是多位数字符串
                    j++;
                }
                list.add(cc);
                i = j - 1;//下次遍历,跳过这个多位数
            }
            i++;
        }
        return list;
    }

    //中缀转成后缀list  1+((2+3)*4)-5  =>   1 2 3 + 4 * + 5 -
    public static List<String> parseSuffixExpressionList(List<String> ls) {
        //两个栈符栈和中间结果栈
        //讲解的时候两个栈理解,实操的时候有序中间结果栈没有pop出栈操作,最后还需要倒叙输出,所以换成数组存储
        Stack<String> s1 = new Stack<>();
        //Stack<String> s2 = new Stack<>();
        List<String> s2 = new ArrayList<>();

        for (String item : ls) {
            if (item.matches("\\d+")) {
                //如果是一个数,入s2
                s2.add(item);
            } else if (item.equals("(")) {
                //如果是左括号,入栈s1
                s1.push(item);
            } else if (item.equals(")")) {
                //如果右括号,一次弹出s1栈顶的运算符,压入s2,知道遇到左括号位置,然后这对括号作废
                while (true) {
                    String pop = s1.pop();
                    if (pop.equals("(")) { //基本的问题 字符串的比较不能用"=="
                        break;
                    }
                    s2.add(pop);
                }
            } else {
                //如果是运算符@
                //比较当前的运算符和s1栈顶的优先级

                if (s1.size() == 0 || s1.peek().equals("(") ||
                        OperationPriority.getValue(s1.peek()) > OperationPriority.getValue(item)) {
                    //1.如果s1为空或者,栈顶是(,则直接将此运算符入栈s1
                    //2.否则,如果当前优先级高,则入栈s1

                    s1.push(item);
                } else {
                    // 当前运算符的优先级别 小于或者等于 栈顶运算符
                    //  否则将s1栈顶的运算符弹出压入s2,再次循环重复运算符的判断@
                    while (true) {

                        String pop = s1.pop(); //栈顶元素
                        s2.add(pop);

                        if (s1.size() == 0 || s1.peek().equals("(") ||
                                OperationPriority.getValue(s1.peek()) < OperationPriority.getValue(item)) {//不要想复杂了,直接是重新判断的
                            break;
                        }
                    }

                    s1.push(item);//经过上面的循环,这里s1要么是空的了,要么是栈顶元素是左括号,要么是优先级问题,就可以当前的item压进去了
                }
            }
        }

        //将s1剩余的 依次 压入 s2
        if (s1.size() != 0) {
            s2.add(s1.pop());
        }

        return s2;//按照顺序输出就是对应的后缀表达式
    }


    //后缀表达式装进集合中,方便配合栈
    public static List<String> getListString_suffix(String suffixExperssion) {
        String[] ss = suffixExperssion.split(" ");
        ArrayList<String> list = new ArrayList<>();
        for (String value : ss) {
            list.add(value);
        }
        return list;
    }

    //计算 -- 取后缀表达式集合元素,通过栈处理,
    public static int calculator(List<String> ls) {

        Stack<String> stack = new Stack<>();
        for (String item : ls) {
            if (item.matches("\\d+")) {//正则表达式,匹配多位数
                //入栈
                stack.push(item);
            } else {//运算符
                //pop两个数字,计算后入栈
                int num1 = Integer.parseInt(stack.pop()); //先出
                int num2 = Integer.parseInt(stack.pop()); //后出
                int res = 0;
                if (item.equals("+")) {
                    res = num1 + num2;
                } else if (item.equals("-")) {
                    res = num2 - num1;
                } else if (item.equals("*")) {
                    res = num2 * num1;
                } else if (item.equals("/")) {
                    res = num2 / num1;//可能有问题,先不做处理了
                } else {
                    throw new RuntimeException("运算符有问题~~~");
                }

                //res入栈
                stack.push("" + res);//新方法 把整数转成字符串
            }
        }

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

//判断优先级
class OperationPriority {
    public OperationPriority() {
    }

    public static int getValue(String ch) {
        switch (ch) {
            case "+":
                return 1;
            case "-":
                return 1;
            case "*":
                return 2;
            case "/":
                return 2;
            default:
                System.out.println("运算符有误!");
        }
        return 0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值