栈------java

        小编经历七七四十九天终于把数据结构这块全是牛马的大草坪滚了一遍,为了真正把它吃透,小编可能还需要再多滚几遍,在此之前,我先要做一些小总结而此篇是为了总结栈。

        栈是什么?其定义是一种先进后出的数据结构,也是一种特殊的数据结构。用抽象的生活思维来看栈,栈就好比是一个单开口圆柱形糖果盒,放入的红糖果就好比是数据且蓝糖果刚好够放入此容器,蓝糖果只能放在红糖果的上方,形成一层一糖果的情景,这对应着建栈的过程。除了暴力拆解这个容器,你只能从这个口放入糖果也只能从这个口拿出糖果而且会拿出最上面的糖果——栈顶,假设此容器透明,女主小拉发现自己最喜欢的一个口味在最后一个,那么小拉会倒出红糖果前所有糖,然后再倒出红糖果放入嘴里,这个过程是不是就对应着栈的遍历。若小拉喜欢最上面的糖果取出吃掉,是不是对应着获取栈顶元素,而她查看最上面的糖果是什么口味的则对应着 查看栈顶元素。以上这些过程是我们在建好栈后要做到操作,这里我们先提前预热一下。如下图:(小编鼠标画画技术不太行,多包涵~)

        现在,小编要用java语言把栈实现一二,(为什么是一二,因为小编技术还没到创造了栈的大牛的那种程度)。栈是线性表中一种,也就是说它可以由顺序实现或链式实现,此处小编选择顺序实现。既然要搭起一个栈显然需要先把栈中元素表示出来,小编采用顺序实现,栈中元素自然用数组(类似容器)中元素表示,要放入元素我们还需要一个标记,既可以从这个标记知道栈中元素的放入情况,也可以通过标记把元素放入栈中。目前,我们已知我们需要一个数组和一个整型标记,利用构造方法为数组划定内存,就只需要上面这些操作,便已经有了栈便有了形,但此时它还不能算为是真正的栈,因为它还需要实现栈的基本操作,如图是实现建栈的代码:

        在建好栈后,我们先要实现的是判栈满与空两个操作,在前文我们已做栈中标记,当标记为0初始值时表示栈中没有任何元素,而当标记等于数组长度时表明栈已满。如图:

       

紧接着最重要的是入栈与出栈,先是入栈,何时入栈?如何入栈?入栈之后又如何?我们先讨论何时入栈,当然是栈不满的时候,才可入栈,那么栈满呢?那就扩容数组。如何入栈呢?那就利用标记作为下标把元素存入数组。入栈之后,整型标记自增。以上就完成了入栈过程,如图:

        入栈解决后就是出栈了,同样对应几个问题,什么情况下可以出栈?出栈之后该如何做?首先,出栈前要判栈是否为空,抛异常直接终止程序,这下可拿出栈顶元素了,取对应标记减一(因为小编在入栈那一部分是先入栈后标记自增故标记始终比栈顶元素的下标大1)作为下标处的数组元素,既然是出栈,少了一个元素标记必须要自减。返回拿出值,如图:

        入栈与出栈解决了,就要解决获取栈顶元素了。这就更好理解了,要获取前可以先判空若为空抛出异常否则直接取数组最后一个元素,由于只是获取得知栈顶元素是什么,不需真正把元素拿出,标记就不用动。如图:

        以上的步骤走完后,顺序栈的已经真正成形了,它入栈出栈的时间复杂度为O(1)。栈作为线性表的一种,会被后面的非线性结构使用,这里不过多赘述。在栈这块还有一个容易搞混的问题,栈、栈帧、虚拟机栈之间的区别?栈是一种数据结构,栈帧是运行方法时开辟的一块内存,虚拟机栈是jvm的一块内存。

          上面我们讨论了顺序栈,接下来我想想链式实现,用单链表实现,1.尾插法入栈时间复杂度为O(n)在无尾指针时,若有变为O(1),但出栈时间复杂度为O(n),你可以想到是要找到最后一个元素须遍历故为O(n);2.头插法,入栈与出栈的时间复杂度为O(1),因为它操作的都是头节点。采用头插法实现入栈出栈操作较好。双向链表实现栈,因为有前驱后继且有头尾标记,头插尾插  时间复杂度为O(1)。

        我们来说一下栈的常见应用,1.利用栈可以改变元素原本的序列,对应的常考题:给你一组元素入栈,进栈时可出栈,让你判断选项哪个序列是正确的或错误的出栈序列。踏实的解法:画出栈,看每一选项,从它们序列的第一个元素开始,自己模拟放入放出,若在这个过程中不违背栈的取放元素的规则(栈顶入栈顶出,出的一定是栈顶元素),则这个序列是出栈顺序的一种至于聪明的解法,小编还没摸清,若你知晓,麻烦告知小编。

        2.取代递归的方式逆序打印链表,解法:把链表中的节点存入栈,只要栈不为空,就一直出栈且打印元素即可。

        3.括号匹配问题:给定一个含只含‘(’,‘)’,‘{’,‘}’,‘[’,']'字符串s,判字符串是否有效满足左括号与同类型的右括号闭合,且括号顺序正确例如:{[ ]},右括号与同型的左括号相匹配。反例:(( )、{()]、())等。根据题目我们可以推出有三种情况不匹配:1.括号顺序不匹配 2.左括号多于右括号 3.右括号多于左括号,接下来你会想这和栈有什么关系?因为它是括号匹配问题,左括号和右括号对称,为了为他们彼此很快找到另一半,不一对一对的找,而是把左括号统一存入栈中,当串中找不到左括号时入栈的操作停止,接下来是获取栈顶括号与未入栈的右括号比对若匹配不上,则返回false表示它不是个有效字符串。若匹配的上弹出栈继续比对下一个,且在比对前判栈是否为空,为空表明右括号多于左括号返回false,以上循环到直到遍历串的每个字符结束,此时栈未为空,表明左括号剩余,返回false。以上为大概思路,以下为实现:

        4.逆波兰表达式求值问题:给你一个后缀表达式让求值。【计算机计算时并不认识优先级它会把我们输入在中缀表达式通常转换为后缀表达式即逆波兰表达式求值】说道在这你会疑问,中缀表达式是什么?后缀表达式是什么?甚至会想有没有前缀表达式。有的,兄弟,包有的。中缀表达式是我们平时学习中普遍见到的,就是运算符放在两个操作数中间,例如:(1+2)*2。后缀表达式是运算符紧跟在与它直接相关的操作数之后【运算符作为后缀】,例如:(1+2)*2化为后缀表达式1 2+2*【这里*直接相关的是2、(1+2)两部分所以放在它们两后,+与1、2直接相关,+放在1、2后】,中缀化后缀的小技巧是找到每个运算符直接关联的两部分操作数,把运算符放在它们之后即可,建议可从作为中缀表达式最先需要计算的地方开始,防止涵盖操作数少的被放在涵盖操作数多的后面,之后所有括号不需要保留。前缀表达式(波兰表达式)与后缀表达式同理但它是把运算符直接放在它的直接关联操作数的前面。例如:(2+3)*4转换后*+2 3 4。中缀化前缀的小技巧是找到每个运算符直接关联的两部分操作数,把运算符放在它们之前即可,建议可从作为中缀表达式最先需要计算的地方开始,防止涵盖操作数少的被放在涵盖操作数多的后面,之后所有括号不需要保留。计算机可以用中缀表达式转为前缀表达式求值,但它更常用符合它处理数据的常规顺序的后缀表达式求值,这里感兴趣的可以自行查阅。在介绍完上面三种表达式,我们回归正题:后缀表达式求值,这里思路:利用栈求值,从前遍历这个表达式,遇到操作数就把操作数放入栈,遇到运算符就从栈中弹出两个数,用遇到的运算符把这两个数值求出来并放入栈中,直到这个表达式被遍历完,弹出栈中的值就是这个表达式的结果。实现代码如图:

        5.出栈入栈的次序匹配:输入两个整数序列,第一个序列表达栈压入顺序,判断第二个序列是不是它的弹出顺序,例如:1234->4321【弹出序列之一】返回true。这个题的思路是把序列1的每一个元素放入辅助栈,同时在栈不为空,序列2的所有元素没有被遍历完的条件下获取其栈顶元素与序列2的元素进行是否相等的比较,若相等,表示此元素就是在这个时候出栈的,故栈顶元素出栈,遍历序列2的下一个元素,在序列1 的每个元素都被遍历完,返回判栈是否为空的方法的值,若为假表示栈中还有元素,第二个序列不是她的弹出序列之一,若为真则反之。本质:看入栈同时获取栈顶元素看是否与出栈顺序相同,相同弹出继续比较,否则继续入栈,直到入栈完所有元素,发现这时栈为空那么说明出栈的顺序匹配上了。接下是实现代码:

        6.设计一个最小栈,支持push、pop、top操作且可在常数时间内找到最小元素的栈。思路:既然是存有最小元素为顶的栈那么其它元素就需要另一个栈来存,这就需要两个栈,主栈和最小栈。元素都要放入主栈,那么最小栈什么时候放?1.当最小栈为空的时候,普通栈放入最小栈也要放入 2.放入普通栈元素放入后获取与最小栈的栈顶元素比较,若小于最小栈的栈顶元素则放入它,表示它是目前主栈中所有元素的最小元素,若相反,则不用放入最小栈,若相同也要放入最小栈因为主栈是与辅助栈同进退的也就是主栈弹出与最小栈栈顶相同的元素,存储弹出元素与剩余元素的最小值 【最小值在最小栈栈顶】,故辅助栈也必须要弹出栈顶元素。防止辅助栈不能对应当前主栈的最小值。接下来就是实现,如图:

        以上所有就是小编对栈的总结了,各位客官您慢用!若对栈食有什么想法或建议都可以同小编留言哦!小编还想进步,感谢~

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值