数据结构括号匹配代码_数据结构(一):栈

什么是数据结构

栈是一种数据结构。那什么是数据结构呢?我这里不给出严格的定义,因为对于完全没有基础的新人而言,严格的定义说了等于没说。我只从一个简单的角度举例说明一下。在初学阶段,你可以认为数据结构就是关于数据在计算机中如何组织的一门课程。

比如,我要往一个数组中,存1,3,2这三个整数,那么,我实际存的时候,是按照从小到大排着存呢,还是从大到小存,还是没有顺序随便存,这要根据实际的需求来决定。根据需求决定数据存储的方式。这就是数据结构要研究的内容。

什么是栈

以前打了粮食,会放到一个筒形的容器里,这个筒形的容器,大家叫它栈。它的特点是先放进去的粮食要最后才能取出来,后放进去的粮食是最先被取出来。所以中文就用栈这个词翻译英文的stack了。

想一想,生活中,还有很多栈的例子。比如叠在一起的碗盘,叠的时候我们是从底往高处叠,但是取的时候,我们是从最上面的一个依次向下取。这也是一个典型的栈:后进先出,先进后出。

那么Java语言中如何模拟这一个过程呢?首先,我们定义一个名叫Stack的类:

public class Stack { private int[] data; private int size; private int top = 0; // 指向栈的顶部 public Stack(int size) { this.size = size; this.data = new int[size]; } }

因为我们的课程里还没有涉及到泛型,为了简单起见,我先用整型代替了。如果有些同学,已经有一定的基础了,也可以看一下泛型版的代码:

import java.util.ArrayList;public class Stack { private ArrayList list; private int size; public Stack(int size) { this.size = size; this.list = new ArrayList(size); }  public static void main(String args) { Stack s = new Stack(10); } }

本文中后面的例子,我还是会使用简单版本的。

OK,接下来,就是要往类里添加相关的操作了。先定义一个可以往栈里写数据的函数,叫做push:

 public void push(int num) { data[top++] = num; }

这个函数的意义就是,把数据num存到数组里,并且把游标向后指一位。逻辑比较简单。

相对应的,我们还可以继续定义以下三个方法,分别是取栈顶元素(getTop),出栈(pop),判断栈是否为空(isEmpty)。

 public int pop(int num) { return data[--top]; }   public int getTop(int num) { return data[top-1]; }  public boolean isEmpty() { return top == 0; }

好了,一个栈就定义完了(当然,这里是有问题的,因为我们在push里,没有检查top是否越过了size规定的数组,出栈的时候,也没有判断top是否等于0。这个做为今天的第一个作业,请读者自行添加。)

栈的应用

这个数据结构看上去好简单啊。肯定有很多人会这样想,但其实,栈在计算机编程中可以说是最基础也是最重要的数据结构了。其功能之强大,可能出乎很多人的意料。我们先通过一个小例子来体会一下。看这样一道题目:

输入一组括号,请判断这些括号的匹配是否合法。例如

(()],不合法,左边的小括号与右边的中括号不能匹配。

{[()]},合法的,所有的括号都可以正确匹配。

{(}),不合法,顺序是错的。

((()),不合法,右括号少了一个。

大家可以先自己想一下,这个问题怎么解决,自己写一写,看看能不能搞得定。如果不使用栈,自己穷举所有情况,逐个去处理的话,好像有点麻烦。

我们来分析一下。如果遇到第一个右括号,那与之配对的一定是离它最近的那个左括号,如果离它最近的左括号的类型与这个右括号的类型是一样的(比如都是小括号),那这就是一次成功的配对。把一组成功配对的括号从括号序列中删去,不会影响原来序列是否合法这个属性,就是说原来合法的,仍然合法,原来不合法的,仍然不合法。通过这样的办法就可以化简题目了。

算法是有了,怎么实现呢?遇到右括号,只去检查离它最近的,如果匹配上了,就把左右括号一起删掉,这不就是栈吗?每次都只检查栈顶的左括号,如果与右括号匹配上了,就把左括号出栈(删掉)。

好了,数据结构有了,算法有了,写成程序就太简单了:

 public static void main(String args[]) throws IOException { byte[] buf = new byte[128]; int length = System.in.read(buf); Stack s = new Stack(128); for (int i = 0; i < length; i++) { if (buf[i] == '(' || buf[i] == '[' || buf[i] == '{') { s.push(buf[i]); }  else if (buf[i] == ')') { if (s.getTop() == '(') s.pop(); else { System.out.println("1 unmatch!"); System.exit(1); }  }  else if (buf[i] == ']') { if (s.getTop() == '[') s.pop(); else { System.out.println("2 unmatch!"); System.exit(1); }  } else if (buf[i] == '}') { if (s.getTop() == '{') s.pop(); else { System.out.println("3 unmatch!"); System.exit(1); } } } if (!s.isEmpty()) System.out.println("4 unmatch!"); else System.out.println("matched~"); }

虽然代码很长,但其实逻辑非常简单易懂。这里漏了一种情况,那就是左括号少,右括号多的情况。这个也留做作业(其实就是getTop和pop的时候要做检查)

Java虚拟机的实现是基于栈的

Java虚拟机规范里定义了Java所使用的字节码。我们知道Java文件要先编译成字节码文件,也就是class文件,但是大家有没有想过,class文件里都是些什么呢?class文件的结构,我以后会专门讲。今天只演示一个简单的例子,让大家有个初步的感觉。先看这样一个源文件:

public class Main { // Main.java public static int add(int a, int b){  return a + b; } }

执行以下命令:

javac Main.javajavap -c Main

可以看到这样的输出:

 public static int add(int, int); Code: 0: iload_0 1: iload_1 2: iadd 3: ireturn

add 函数被编成了四条字节码指令,这四条字节码是什么意思呢?

你可以认为Java的每一个函数都有一个操作数栈,每条指令就是在对这个操作数栈进行操作。比如 iload_0,就代表把第一个参数 push 进操作数栈,iload_1代表把第二个参数 push 进操作数栈,而 iadd 代表,从栈上连续pop两次,取两个数,将其相加,再把结果送到栈上。ireturn 则表示把栈顶的值做为返回值传给调用者。Java字节码的整个执行过程都是在这样一个栈上的。如果用图来表示,这四个步骤就是:

iload_0,使a先进栈

57e21a0cbb28bbc9058604bbdc40939d.png

iload_1, 使b进栈

e3cb01a0cf8e8ea0d796894b4f24d015.png

iadd,做了三件事情,b 和 a 出栈,计算 a+b,将计算结果入栈:

8b8e5af13084ba4549f78f6188e6d0d6.png

ireturn 将当前栈顶的值返回出去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值