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

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. }
运行结果:
({()})
括号匹配

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第一章 TCP/IP协议族

一、协议族体系结构 TCP/IP协议族分为四层协议系统&#xff0c;自底向下分别为数据链路层、网络层、传输层、应用层。 数据链路层常用ARP&#xff08;地址解析协议&#xff09;和RARP&#xff08;逆地址解析协议&#xff09;。在网络层使用IP寻址&#xff0c;而在数据链路层使用…

Linux多线程——使用信号量同步线程

http://blog.csdn.net/ljianhui/article/details/10813469/ 信号量、同步这些名词在进程间通信时就已经说过&#xff0c;在这里它们的意思是相同的&#xff0c;只不过是同步的对象不同而已。但是下面介绍的信号量的接口是用于线程的信号量&#xff0c;注意不要跟用于进程间通信…

C++11新特性之八——函数对象function

http://www.cnblogs.com/yyxt/p/3987717.html 详细请看《C Primer plus》(第六版中文版) http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html 备注&#xff1a; 函数对象&#xff1a; 尽管函数指针被广泛用于实现函数回调&#xff0c;但C还提供了一个重要的实现…

从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法

http://blog.csdn.net/jnu_simba/article/details/9500219 一、函数对象 1、函数对象&#xff08;function object&#xff09;也称为仿函数&#xff08;functor&#xff09; 2、一个行为类似函数的对象&#xff0c;它可以没有参数&#xff0c;也可以带有若干参数。 3、任何重载…

树状数组初步理解

学习树状数组已经两周了&#xff0c;之前偷懒一直没有写&#xff0c;赶紧补上防止自己忘记&#xff08;虽然好像已经忘得差不多了&#xff09;。 作为一种经常处理区间问题的数据结构&#xff0c;它和线段树、分块一样&#xff0c;核心就是将区间分成许多个小区间然后通过对大区…

Linux socket编程(二) 服务器与客户端的通信

http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html上一篇写了对套接字操作的封装&#xff0c;这一节使用已封装好的Socket类实现服务器与客户端的通信&#xff08;Socket的定义见上篇Socket.h) 服务器端&#xff1a; ServerSocket.h #ifndef SERVERSOCKET_H #defin…

UNIX网络编程:I/O复用技术(select、poll、epoll)

http://blog.csdn.net/dandelion_gong/article/details/51673085 Unix下可用的I/O模型一共有五种&#xff1a;阻塞I/O 、非阻塞I/O 、I/O复用 、信号驱动I/O 、异步I/O。此处我们主要介绍第三种I/O符复用。 I/O复用的功能&#xff1a;如果一个或多个I/O条件满足&#xff08;输…

解决iex -S mix报错

执行iex -S mix命令的时候会遇到如下错误&#xff1a; 执行 mix deps.get 然后就可以运行 iex -S mix了 其中&#xff0c;有可能会出现 按照其网站下载相应文件&#xff0c;复制到项目根目录下&#xff0c;然后执行命令&#xff08;mix local.rebar rebar ./rebar&#xff09;即…

Anker—工作学习笔记

http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html 1、基本知识 epoll是在2.6内核中提出的&#xff0c;是之前的select和poll的增强版本。相对于select和poll来说&#xff0c;epoll更加灵活&#xff0c;没有描述符限制。epoll使用一个文件描述符管理多个描述符&am…

Linux网络编程——tcp并发服务器(I/O复用之select

http://blog.csdn.net/lianghe_work/article/details/46519633 与多线程、多进程相比&#xff0c;I/O复用最大的优势是系统开销小&#xff0c;系统不需要建立新的进程或者线程&#xff0c;也不必维护这些线程和进程。 代码示例&#xff1a; [csharp] view plaincopy #include &…

Linux下的I/O复用与epoll详解

http://www.cnblogs.com/lojunren/p/3856290.html 前言 I/O多路复用有很多种实现。在linux上&#xff0c;2.4内核前主要是select和poll&#xff0c;自Linux 2.6内核正式引入epoll以来&#xff0c;epoll已经成为了目前实现高性能网络服务器的必备技术。尽管他们的使用方法不尽相…

数据结构--顺序栈和链式栈

http://www.cnblogs.com/jingliming/p/4602458.html 栈是一种限定只在表尾进行插入或删除操作,栈也是线性表表头称为栈的底部,表尾称为栈的顶部,表为空称为空栈&#xff0c;栈又称为后进先出的线性表,栈也有两种表示:顺序栈与链式栈顺序栈是利用一组地址连续的存储单元&#xf…

数据结构--双链表的创建和操作

http://www.cnblogs.com/jingliming/p/4602144.html#0-tsina-1-42616-397232819ff9a47a7b7e80a40613cfe1 一、双向链表的定义 双向链表也叫双链表&#xff0c;是链表的一种&#xff0c;它的每个数据结点中都有两个指针&#xff0c;分别指向直接后继和直接前驱。所以&#xff0c…

MYSQL错误代码#1045 Access denied for user 'root'@'localhost'

http://blog.csdn.net/lykezhan/article/details/70880845 遇到MYSQL“错误代码#1045 Access denied for user rootlocalhost (using password:YES)” 需要重置root账号权限密码&#xff0c;这个一般还真不好解决。 不过&#xff0c;这几天调试的时候真的遇到了这种问题&#x…

常量变量以及循环

常量 1.三目运算词 三字母词表达字符???([??)]??<{??>} 2.循环 1).数组元素以及变量在内存中的分配顺序 2)goto语句应用 //电脑关机程序 #include<stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> int ma…

Linux 环境 C语言 操作MySql 的接口范例

http://www.cnblogs.com/wunaozai/p/3876134.html 接上一小节&#xff0c;本来是计划这一节用来讲数据库的增删改查&#xff0c;但是在实现的过程中&#xff0c;出现了一点小问题&#xff0c;也不是技术的问题&#xff0c;就是在字符界面上比较不好操作。比如要注册一个帐号&a…

数组相关运算

数组的初始化 数组及指针在内存中的存储 一维数组在内存中的存储 有关数组的运算 //一维数组 int a[] {1,2,3,4}; printf("%d\n",sizeof(a));//16这里的a表示的是整个数组,计算出的是整个数组的大小,单位为byte printf("%d\n",sizeof(a 0));/*a没有单独…

gets fgets 区别

http://www.cnblogs.com/aexin/p/3908003.html 1. gets与fgets gets函数原型&#xff1a;char*gets(char*buffer);//读取字符到数组&#xff1a;gets(str);str为数组名。 gets函数功能&#xff1a;从键盘上输入字符&#xff0c;直至接受到换行符或EOF时停止&#xff0c;并将读取…

Shuffle'm Up——简单模拟

【题目描述】 A common pastime for poker players at a poker table is to shuffle stacks of chips. Shuffling chips is performed by starting with two stacks of poker chips, S1 and S2, each stack containing C chips. Each stack may contain chips of several diff…

Fire!——两个BFS

【题目描述】 【题目分析】 看到题目后很清楚是两个BFS&#xff0c;可是我觉得对于火的BFS可以转换成判断&#xff0c;我的做法是将火的位置全部记录下来&#xff0c;然后判断某个位置距离每个火的步数是否小于当前步数&#xff0c;可是错了&#xff0c;还不清楚为什么&#x…