http://data.biancheng.net/view/9.html

栈,线性表的一种特殊的存储结构。与学习过的线性表的不同之处在于栈只能从表的固定一端对数据进行插入和删除操作,另一端是封死的。

图1 栈结构示意图
由于栈只有一边开口存取数据,称开口的那一端为“栈顶”,封死的那一端为“栈底”(类似于盛水的木桶,从哪进去的最后还得从哪出来)。

栈的“先进后出”原则

使用栈存储数据元素,对数据元素的 “存” 和 “取” 有严格的规定:数据按一定的顺序存储到栈中,当需要调取栈中某数据元素时,需要将在该数据元素之后进栈的先出栈,该数据元素才能从栈中提取出来。

如图 1 ,栈中存放了 4 个数据元素,进栈的顺序是 A 先进栈,然后 B 进,然后 C 进,最后 D 进栈;当需要调取 A 时,首先 D 出栈,然后 C 出栈,然后 B 出栈,最后 A 才能出栈被调用。

就好比只有一个门的车库(每次仅允许一辆车通过),每辆车好比一个数据元素,只有离门最近的车先开出来,里边的车才能出来;最里边的车是最先开进去的,注定要最后出来。

栈操作数据元素的方法

栈操作数据元素只有两种动作:

  1. 数据元素用栈的数据结构存储起来,称为“入栈”,也叫“压栈”。
  2. 数据元素由于某种原因需要从栈结构中提取出来,称为“出栈”,也叫“弹栈”。

栈的两种表示方式

既然栈也是线性表,那么它就同样有线性表的两种表示形式: 顺序栈  和  链式栈 (简称 “链栈” )。

两者的区别在于存储的数据元素在物理结构上是否是相互紧挨着的。顺序栈存储元素预先申请连续的存储单元;链栈需要即申请,数据元素不紧挨着。

栈的“上溢”和“下溢”

栈存储结构调取栈中数据元素时,要避免出现“上溢”和“下溢”的情况:

“上溢”:在栈已经存满数据元素的情况下,如果继续向栈内存入数据,栈存储就会出错。

“下溢”:在栈内为空的状态下,如果对栈继续进行取数据的操作,就会出错。

栈的“上溢”和“下溢”,可以总结为:栈满还存会“上溢”,栈空再取会“下溢”。

对于栈的两种表示方式来说,顺序栈两种情况都有可能发生;而链栈由于“随时需要,随时申请空间”的存储结构,不会出现“上溢”的情况。

顺序栈

顺序栈的实现采用的是数组。

在顺序栈中设定一个随时指向栈顶元素的变量(一般命名为 top ),当 top 的值为 -1 时,说明数组中没有数据,即栈中没有数据元素,为 “空栈” ;只要数据元素进栈,top 就加 1 ;数据元素出栈, top 就减 1 。

例如,使用顺序栈的存储结构将(’a’,’b’,’c’,’d’)四个元素逐个压栈并输出栈顶元素。

实现代码:

  1. #include <stdio.h>
  2. //元素elem进栈
  3. int push(char* a,int top,char elem){
  4. a[++top]=elem;
  5. return top;
  6. }
  7. //数据元素出栈
  8. int pop(char * a,int top){
  9. if (top==-1) {
  10. printf("空栈");
  11. return -1;
  12. }
  13. printf("弹栈元素:%c\n",a[top]);
  14. top--;
  15. return top;
  16. }
  17. int main() {
  18. char a[100];
  19. int top=-1;
  20. top=push(a, top, 'a');
  21. top=push(a, top, 'b');
  22. top=push(a, top, 'c');
  23. top=push(a, top, 'd');
  24. top=pop(a, top);
  25. top=pop(a, top);
  26. top=pop(a, top);
  27. top=pop(a, top);
  28. top=pop(a, top);
  29. return 0;
  30. }

输出结果:

弹栈元素:d
弹栈元素:c
弹栈元素:b
弹栈元素:a
空栈

链栈

链栈,用线性表的链式存储结构实现。

链栈一般不需要创建头结点,头结点会增加程序的复杂性,只需要创建一个头指针就可以了。

用链表表示栈时,用链表头结点的一端作为栈的栈顶端,这样做的好处是当数据元素压栈或者弹栈时,直接使用头指针就可以完成,不需要增设额外的指针。

例如,用链栈实现将(’a’,’b’,’c’,’d’)四个数据元素压栈,再依次弹栈:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef struct lineStack{
  4. char data;
  5. struct lineStack * next;
  6. }lineStack;
  7. lineStack* push(lineStack * stack,char a){
  8. lineStack * line=(lineStack*)malloc(sizeof(lineStack));
  9. line->data=a;
  10. line->next=stack;
  11. stack=line;
  12. return stack;
  13. }
  14. lineStack * pop(lineStack * stack){
  15. if (stack) {
  16. lineStack * p=stack;
  17. stack=stack->next;
  18. printf("弹栈元素:%c ",p->data);
  19. if (stack) {
  20. printf("栈顶元素:%c\n",stack->data);
  21. }else{
  22. printf("栈已空\n");
  23. }
  24. free(p);
  25. }else{
  26. printf("栈内没有元素");
  27. return stack;
  28. }
  29. return stack;
  30. }
  31. int main() {
  32. lineStack * stack=NULL;
  33. stack=push(stack, 'a');
  34. stack=push(stack, 'b');
  35. stack=push(stack, 'c');
  36. stack=push(stack, 'd');
  37. stack=pop(stack);
  38. stack=pop(stack);
  39. stack=pop(stack);
  40. stack=pop(stack);
  41. stack=pop(stack);
  42. return 0;
  43. }

输出结果:

弹栈元素:d 栈顶元素:c
弹栈元素:c 栈顶元素:b
弹栈元素:b 栈顶元素:a
弹栈元素:a 栈已空
栈内没有元素

栈的实际应用

实际生活中,使用手机(无论是 iPhone 还是安卓)时,屏幕页面的跳转使用的就是栈。当你选择换一个页面浏览时,之前的页面被存储到栈中;每次做回退操作时,会回到上一个页面,这是进栈的页面逐个出栈的效果。

另外,在求 n! 时,有一种算法会涉及到函数的递归(函数自己调用自己的过程),这个过程的底层实现就用到了栈。

除此之外,数制转换和括号匹配问题,也可以用栈来解决。

数制转换

实际生活中主要使用 10 进制,而计算机只认识 2 进制,在解决数制转换的问题上使用栈结构会事半功倍。

例如,将十进制转化成二进制,代码实现:

  1. #include <stdio.h>
  2. int top=-1;//top变量时刻表示栈顶元素所在位置
  3. void push(int * a,int elem){
  4. a[++top]=elem;
  5. }
  6. void pop(int * a){
  7. if (top==-1) {
  8. return ;
  9. }
  10. printf("%d",a[top]);
  11. top--;
  12. }
  13. int main() {
  14. int a[30];
  15. int div = 0;
  16. scanf("%d",&div);
  17. while (div/2) {
  18. push(a,div%2);
  19. div/=2;
  20. }
  21. push(a,div%2);
  22. while (top!=-1) {
  23. pop(a);
  24. }
  25. }

运行结果:
15
1111

2 进制和 10 进制之间的转换方法为:15 = 1*2+ 1*21 + 1*22 + 1*23

括号匹配的检查

在编写代码的时候,经常会用到两种括号:圆括号 “()” 和大括号 “{}” 。不管使用哪种括号,程序编译没有问题的一个考虑因素就是所使用的括号是否能够匹配上,在编写程序时,括号可以嵌套,即: “({()})” 这种形式,但 “({)” 或者 “({}” 都不符合要求。

编写程序判断括号匹配问题的时候,使用栈结构会很容易:

  • 如果碰到的是左圆括号或者左大括号,直接压栈;
  • 如果碰到的是右圆括号或者右大括号,就直接和栈顶元素配对:如果匹配,栈顶元素弹栈;反之,括号不匹配;

实现代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int top=-1;//top变量时刻表示栈顶元素所在位置
  4. void push(char * a,int elem){
  5. a[++top]=elem;
  6. }
  7. void pop(char* a){
  8. if (top==-1) {
  9. return ;
  10. }
  11. top--;
  12. }
  13. char visit(char * a){
  14. //调取栈顶元素,不等于弹栈,如果栈为空,为使程序不发生错误,返回空字符
  15. if (top!=-1) {
  16. return a[top];
  17. }else{
  18. return ' ';
  19. }
  20. }
  21. int main() {
  22. char a[30];
  23. char bracket[100];
  24. scanf("%s",bracket);
  25. int length=(int)strlen(bracket);
  26. for (int i=0; i<length; i++) {
  27. //如果是左括号,直接压栈
  28. if (bracket[i]=='('||bracket[i]=='{') {
  29. push(a, bracket[i]);
  30. }else{
  31. //判断与栈顶元素是否匹配,如果匹配,栈顶元素弹栈,程序继续运行;否则,发现括号不匹配,输出结果直接退出
  32. if (bracket[i]==')') {
  33. if (visit(a)=='(') {
  34. pop(a);
  35. }else{
  36. printf("括号不匹配");
  37. return 0;
  38. }
  39. }else{
  40. if (visit(a)=='{') {
  41. pop(a);
  42. }else{
  43. printf("括号不匹配");
  44. return 0;
  45. }
  46. }
  47. }
  48. }
  49. //如果所有括号匹配完成,栈内为空,说明所有括号全部匹配成功
  50. if (top!=-1) {
  51. printf("括号不匹配");
  52. }else{
  53. printf("括号匹配");
  54. }
  55. }

运行结果:

({()})
括号匹配


栈(Stack),轻松解决数制转换和括号匹配问题!相关推荐

  1. 利用栈进行数制转换和括号匹配(C语言)

    数制转换:将十进制数转换为任意进制数.由数制转换的计算公式 N = (N / d) × d + N % d (其中N为想转换的十进制数,d为想转换的进制) 我们可以知道其转换后的结果是逆序输出,故可以 ...

  2. 第三章:顺序栈及其应用之三---数制转换

    #include <stdio.h> typedef struct LNode {int x;LNode *next; }LNode,*LinkList; LinkList L; void ...

  3. 栈和队列常见oj题(括号匹配问题、栈实现队列、队列实现栈、设计循环队列)

    一.括号匹配问题 1.题目要求: 2.大体思路 遍历这个字符串,如果是左括号就让它入栈,如果是右括号就让它和栈顶元素进行匹配(前提是栈中有元素),匹配成功的话就让栈顶元素出栈,匹配失败就返回false ...

  4. C#栈的实现(数制转换)

    using System ; public class Stacks { private object [] _array; //存放元素的数组: private const int _default ...

  5. -20c语言转换,用C语言解决数制转换问题_百度文库(20页)-原创力文档

    M 进制转十进制:从该 M 进制数的最后一位开始算,依次列为第 0 盘形制动器是火车常用的一种制动器.,在制动过程中1火车的刹车芯盘会产生磨损,在使用了一段时间后,2...n,降低成本,常用堆焊方法对 ...

  6. c语言中栈的作用,栈(Stack)的概念和应用及C语言实现

    线性表的一种特殊的存储结构.与学习过的 由于栈只有一边开口存取数据,称开口的那一端为"栈顶",封死的那一端为"栈底"(类似于盛水的木桶,从哪进去的最后还得从哪出 ...

  7. 栈的应用-数制转换(C语言数据结构)

    数制转换 在计算机中经常面对不同数制的转换问题,如将一个十进制数N转换为d进制B.数制转换的解决方法很多,其中一个简单的转换算法是重复下述两步.直到N等于零为止. x = N mod d N = N ...

  8. 栈和队列应用之数制转换

    数制转换是将任意一个非负的十进制数转换为其他进制的数,一般的方法是采用辗转相除法.参考<C#数据结构> N          N/8        N%8 5142        642  ...

  9. 各种实用的音频文件在线工具,一款解决所有转换难题

    每次在工作中,不管是在职场新人,还是职场老油条啊 都会经常碰到有人在问啥有比较好的办法把音频文件互相转换啊, 或者是谁能把QSV转MP4,等等这些问题,每次听到这些问题,我就笑了, 这些办公文件格式转 ...

最新文章

  1. ubuntu linux 编译 opencv
  2. 一个函数让你看懂 'Why 0.1+0.2!=0.3'
  3. IDEA使用Maven打包时如何去掉测试阶段
  4. matlab矩阵除以一个数字,matlab矩阵中每一行数除以一个数 | 学步园
  5. 从RSS Feed和YQL创建数据表
  6. Java高级语法笔记-语法支持的异常
  7. mysql convert报错_部署mysql版本项目问题记录
  8. Java基础学习总结(154)——Synchronized与Volatile、Synchronized与ReentrantLock概念及区别
  9. java单元测试笔记
  10. git pull 提示 There is no tracking information for the current branch
  11. JavaSE,JavaEE,JavaME区别
  12. linux 数位板内核,在Linux操作系统下使用高漫、绘王、Wacom等数位板的方法
  13. ftps协议怎么连接服务器,浅谈 FTP、FTPS 与 SFTP的区别
  14. java 怎么去JTF边框_求助 java 如何编写JFrame窗体右上角红色打叉关闭按钮的事件?...
  15. HashData:守护数据安全 筑牢数字经济底座
  16. 选择与循环:剪刀石头布_剪刀石头布十大奢侈家具,创造高端精致生活就是这么简单!...
  17. Ps钢笔工具抠图、更换背景及边缘优化步骤
  18. u3d android 优化
  19. enti下载器_短跑enti策略:如何在不破坏软件的情况下改进软件
  20. 融云IM商用版冰点促销 助程序员的十二时辰躺赢

热门文章

  1. 转载大神的一篇文章----【如何选择开源许可证?】
  2. QT信号和槽函数学习笔记
  3. ManualResetEvent用法
  4. mysql 设置client char_mysql编码问题:show variables like “%char%”
  5. 向量外积_解析几何 -向量
  6. 烟草局计算机笔试,2020年广西南宁烟草局什么时候笔试?
  7. 2020暨阳学院园林计算机考研考场,【图片】2020考研,老学长教你如何规划!【计算机考研吧】_百度贴吧...
  8. windows2012同步linux时间,Windows server2012时间同步NTP配置
  9. 允许服务与桌面交互_vivo 正式推出 Origin OS,融合自然设计与全新交互
  10. MongoDB学习3——mongoDB的一些基本使用