百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

数据结构与算法之栈与队列java实现

toyiye 2024-06-21 11:54 10 浏览 0 评论

闻理似悟,遇境则迷

栈与队列来说也算是一种特殊的线性表,栈的特点是后进先出,队列的特点是先进先出。

栈的特点是后进先出,栈的操作只有出栈和入栈(也叫压栈),除此之外,还包含栈顶与栈底的指向以及栈的长度。
因此栈的定义如下

public class ZStack {
    /**
     * 栈顶指向
     */
    private int top = 0;
    /**
     * 栈底指向
     */
    private int bottom = 0;
    /**
     * 栈长度
     */
    public int length = 0;
    /**
     * 栈内数据
     */
    private Object[] datas;

    /**
     * 栈内最大空间
     */
    public int MAX_SIZE = 100;

    /**
     * 出栈
     *
     * @return
     */
    public Object pop() {
        // 栈顶与栈底指向同一个位置
        if (top == 0) {
            return null;
        } else {
            // 栈顶下移
            top--;
            // 取出栈顶数据
            Object data = datas[top];
            // 栈顶置为空
            datas[top] = null;
            length--;
            return data;
        }
    }

    /**
     * 入栈
     *
     * @param o
     */
    public void push(Object o) {
        if (top < MAX_SIZE){
            datas[top] = o;
            top++;
            length++;
        }else {
            throw new OutOfMemoryError("栈已满,不能再加了");
        }

    }

    // 初始化栈
    public ZStack() {
        // 默认栈长度为100
        datas = new Object[MAX_SIZE];
    }
}

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667

进制转换(十进制转二进制)

将十进制转为二进制
第一步,将该数除以二,取余,将余数入

第二步,重复第一步,直到最后除数为0或者1
第三步,依次出栈,最后的顺序就是二进制数
例如 11转为二进制
11 %2 = 5.,…1
5 % 2 = 2…1
2 % 2 =1…0
入栈顺序 1 1 0 1,最后输出 1 0 1 1,验证一下,1 + 2 +0 +8 = 11
核心代码如下

// 初始化栈
        ZStack zStack = new ZStack();
        while (num > 0) {
            // 余数入栈
            zStack.push(num % 2);
            num = num / 2;
            if (num == 1) {
                zStack.push(num);
                num = 0;
            }
        }
        // 出栈,打印出二进制内容
        StringBuffer sb =new StringBuffer();
        while (zStack.length > 0) {
            sb.append(zStack.pop());
        }
12345678910111213141516

中缀表达式转后缀表达式

逆波兰表达式就是后缀表达式,举个例子吧,我们要计算1+(2-3)*4,转为后缀表达式就是 1 2 3 - 4 * +,怎么来的呢
计算规则如下:
第一步,如果是数字,直接输出
第二步,
如果是表达式符号,入栈,入栈需要对符号优先级进行判断,如果当前运算符优先级小于栈顶元素,需要先出栈顶元素,然后再入栈
第三步,如果是(,直接入栈,
第四步,如果是),依次出栈,直到遇到(或者栈为空
第五步,将栈内剩余符号出栈
例子,
1>>> 栈 [],输出(1)
+>>>栈[+],输出(1)
(>>>栈[+ ,( ],输出(1)
2>>>栈[+ ,( ],输出(1 2)
->>>栈[+ ,( ,- ],输出(1 2)
3>>>栈[+ ,(, - ],输出(1 2 3)
)>>> 栈[+],输出(1 2 3 -)
× >>> 栈[+,×],输出(1 2 3 -)
4 >>> 栈[+,×],输出(1 2 3 - 4)
最后输出(1 2 3 - 4 + ×)

1+(2*3 - 4)/ 1
1>>> 栈 [],输出(1)
+>>>栈[+],输出(1)
(>>>栈[+ ,( ],输出(1)
2>>>栈[+ ,( ],输出(1 2)
× >>>栈[+ ,(,× ],输出(1 2)
3 >>>栈[+ ,(,× ],输出(1 2 3)
_ >>>栈[+ ,(,- ],输出(1 2 3 ×)注意,×出栈了
4 >>>栈[+ ,(,- ],输出(1 2 3 × 4)
) >>>栈[+ ],输出(1 2 3 × 4 -)
/ >>>栈[+ ,/ ],输出(1 2 3 × 4 -)
1>>>栈[+ ,/ ],输出(1 2 3 × 4 -1)
最后输出1 2 3 × 4 -1/+
验证


核心代码

private static String parseSuffixExpression(String next) {
        StringBuffer suffixSb = new StringBuffer();
        char[] chars = next.toCharArray();
        ZStack zStack = new ZStack();
        for (int i = 0; i < chars.length; i++) {
            char thisChar = chars[i];
            // 判断字符是否是数字,如果是数字,直接输出
            if (Character.isDigit(thisChar)) {
                suffixSb.append(thisChar);
            } else if (thisChar == ')') {
                /**
                 * 当入栈时是)时,栈为空时依次出栈,栈不为空依次出栈,知道遇到(停止
                 */
                // 栈为空
                if (zStack.length == 0) {
                    zStack.push(thisChar);
                } else {
                    Object pop = zStack.pop();
                    while (pop != null && !String.valueOf(pop).equals("(")) {
                        suffixSb.append(pop);
                        pop = zStack.pop();
                    }
                }
            } else if (thisChar == '+' || thisChar == '-') {
                /**
                 * 当为+,或者-入栈时,需要与栈顶元素的优先级比较,优先级高的需要先出栈,直到遇到(或者栈为空
                 */
                // 栈为空,直接push
                if (zStack.length == 0) {
                    zStack.push(thisChar);
                } else {

                    Object pop = zStack.pop();
                    while (pop!=null && !String.valueOf(pop).equals("(")){
                            suffixSb.append(pop);
                        pop = zStack.pop();
                    }
                    /**
                     * (出栈了,需要重新入栈,重点
                     */
                    if (String.valueOf(pop).equals("(")){
                        zStack.push("(");
                    }
                    zStack.push(thisChar);
                }
            } else if (thisChar == '*' || thisChar == '/' || thisChar == '(') {
                /**
                 * 为* ,/,( 直接入栈
                 */
                zStack.push(thisChar);
            } else {
                System.err.println("输入错误");
            }
        }
        /**
         * 输出栈内其它元素
         */
        while (zStack.length!=0) {
            suffixSb.append(zStack.pop());
        }

        return suffixSb.toString();
    }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263

RPN(逆波兰表达式)

上面介绍了中缀表达式转后缀表达式(波兰表达式),这里主要是讲波兰表达式如何计算为我们想要的值,以上面例子讲解计算规则
一句来说就是,
数字入栈,遇到运算符将栈顶两个元素出栈,运算后再入栈。
例 1 2 3 × 4 -1/+
前3位数字
1 2 3
× >>> 栈1 6
4 >>>>
1 6 4
->>> 1 2
1>>> 1 2 1
/ >>>1 2
+ 1+2 = 3(结果)
与上图结果吻合
核心代码

/**
     * 逆波兰表达式计算器,输入的是一个波兰表达式
     * @param next
     */
    private static void rpn(String next) {
        if (!next.isEmpty()) {
            ZStack zStack = new ZStack();
            char[] chars = next.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                char thisChar = chars[i];
                // 判断字符是否是数字,如果是数字,就入栈
                if (Character.isDigit(thisChar)) {
                    zStack.push(thisChar);
                } else {
                    // 先出的是后面的操作数
                    Double behind = Double.parseDouble(String.valueOf(zStack.pop()));
                    Double front = Double.parseDouble(String.valueOf(zStack.pop()));
                    switch (thisChar) {
                        case '+':
                            zStack.push(front + behind);
                            break;
                        case '-':
                            zStack.push(front - behind);
                            break;
                        case '*':
                            zStack.push(front * behind);
                            break;
                        case '/':
                            if (behind == 0) {
                                throw new NumberFormatException("被除数不能为0");
                            }
                            zStack.push(front / behind);
                            break;
                        default:
                            break;
                    }
                }
            }
            System.out.println(next + "计算结果为:" + zStack.pop());

        }
    }
123456789101112131415161718192021222324252627282930313233343536373839404142

这里还有很多bug,例如,只支持10以内的计算(一个字符),还有一些特殊输入没有判断,反正我是比较满意的,哈哈。

队列

队列是一种先进先出的数据结构。就像我们在食堂打饭排队一样,每次入队列都在队尾操作,每次出队列就在队头操作。使用java代码实现如下,队列使用链式存储结构实现比较好,我这里采用的是顺序存储结构,通过队头队尾形成一个环状队列。

/**
 * 队列类实现(顺序存储实现)
 */
public class ZQueue {
    // 当前队列长度
    private int length = 0;
    // 队列最大长度
    private int MAX_SIZE = 5;
    // 队列数据
    private Object[] datas;

    /**
     * 队头索引
     */
    private int head = 0;
    /**
     * 队尾索引
     */
    private int tail = 0;

    /**
     * 出队列操作
     *
     * @return
     */
    public Object delete() {
        Object returnObj = new Object();
        if (head == tail) {
            if (datas[head] == null) {
                System.err.println("队列已空,不能出队列");
                ;
            } else {
                returnObj = datas[head];
                datas[head] = null;
            }
        } else {
            /**
             * 队列头置为空,将头往后移
             */
            returnObj = datas[head];
            datas[head] = null;
            head = (head + 1) % MAX_SIZE;
        }
        length--;
        return returnObj;
    }

    /**
     * 入队列操作
     */
    public void add(Object obj) {
        if (length == MAX_SIZE) {
            System.err.println("队列已满,不能入队");
        } else {
            // 队尾累加,如果到顶了,头尾相接,再从头开始,形成一个循环队列
            datas[tail++ % MAX_SIZE] = obj;
            length++;
        }
    }

    public ZQueue() {
        this.datas = new Object[MAX_SIZE];
    }
}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465

主类

ZQueue zQueue = new ZQueue();
        zQueue.add("1");
        zQueue.add("2");
        zQueue.delete();
        zQueue.add("3");
        zQueue.add("4");
        zQueue.add("5");
        zQueue.add("6");
        Object delete = zQueue.delete();
        zQueue.add("7");
12345678910

运行结果


以上就是栈与队列相关的操作,最后附上git地址: https://gitee.com/zhoujie1/data-structure-and-algorithm.git
顺便提一个有趣的事情,昨天一前端同事问道将一堆数组平均分成3份的问题,当时我脑海里闪过通过通过快慢指针的方式,定义3个指针,一个指针每次+1,一个指针每次+2,一个指针每次+3,当走得最快的指针到达数组结尾,剩余两个指针的位置就将整个数据分成了3份,从而达到了目的。此时我也深深的感受到了算法的魅力,也坚定了我往下走的决心。

相关推荐

基于 Kubernetes 的 Serverless PaaS 稳定性建设万字总结

作者:许成铭(竞霄)数字经济的今天,云计算俨然已经作为基础设施融入到人们的日常生活中,稳定性作为云产品的基本要求,研发人员的技术底线,其不仅仅是文档里承诺的几个九的SLA数字,更是与客户切身利益乃...

跟老韩学Ubuntu Linux系列-sysctl 帮助文档

sysctl一般用于基于内核级别的系统调优,man帮助手册如下。...

如何在 Linux/Unix/Windows 中发现隐藏的进程和端口

unhide是一个小巧的网络取证工具,能够发现那些借助rootkit、LKM及其它技术隐藏的进程和TCP/UDP端口。这个工具在Linux、UNIX类、MS-Windows等操作系统下都...

跟老韩学Ubuntu Server 2204-Linux性能管理-uptime指令帮助手册

uptime指令是每个从事Linux系统工作的相关同学必知必会的指令之一,如下是uptime指令的帮助手册。UPTIME(1)...

Openwrt+Rclone+emby+KODI搭建完美家庭影音服务器

特别声明:本篇内容参考了波仔分享,在此表示感谢!上一篇《Openwrt+emby+KODI搭建家庭影音服务器》只适用影音下载到本地的情形,不能播放云盘中的影音,内容较少,缺少了趣味性,也不直观。...

Linux Shell脚本经典案例(linux shell脚本例子)

编写Shell过程中注意事项:开头加解释器:#!/bin/bash语法缩进,使用四个空格;多加注释说明。命名建议规则:变量名大写、局部变量小写,函数名小写,名字体现出实际作用。默认变量是全局的,在函数...

解决 Linux 性能瓶颈的黄金 60 秒

如果你的Linux服务器突然负载暴增,告警短信快发爆你的手机,如何在最短时间内找出Linux性能问题所在?来看Netflix性能工程团队的这篇博文,看它们通过十条命令在一分钟内对机器性能问题进行诊断。...

跟老韩学Ubuntu Server 2204-Linux性能管理-vmstat指令帮助手册

vmstat可查看ubuntlinux的综合性能,是每个从事Linux人员必知必会、需掌握的核心指令之一。vmstat指令帮助手册如下。VMSTAT(8)...

Python 可视化工具包(python常见的可视化工具)

喜欢用Python做项目的小伙伴不免会遇到这种情况:做图表时,用哪种好看又实用的可视化工具包呢?本文将介绍一些常用的Python可视化包,包括这些包的优缺点以及分别适用于什么样的场景。这篇文章...

Python的GPU编程实例——近邻表计算

目录技术背景...

python算法体验-3.python实现欧式距离的三种方式

欧式距离也称欧几里得距离,是最常见的距离度量,衡量的是多维空间中两个点之间的绝对距离。欧式距离源自N维欧氏空间中两点...

python实现Lasso回归分析(特征筛选、建模预测)

实现功能:...

python语言检测模块langid、langdetect使用

本文首发地址:https://blog.csdn.net/Together_CZ/article/details/86678423欢迎关注我的博客【Together_CZ】,我是沂水寒城!之前使用数据...

7天学会Python最佳可视化工具Seaborn(一):可视化变量间的关系

众所周知,Seaborn“可能”是Python下最友好、易用的可视化工具了,可视化效果也非常好。但是截止目前,并没有一份中文教程供广大国内Python使用者查阅学习。怎么能因为语言的问题,让大家错过这...

在Python中使用K-Means聚类和PCA主成分分析进行图像压缩

各位读者好,在这篇文章中我们尝试使用sklearn库比较k-means聚类算法和主成分分析(PCA)在图像压缩上的实现和结果。压缩图像的效果通过占用的减少比例以及和原始图像的差异大小来评估。图像压...

取消回复欢迎 发表评论:

请填写验证码