栈(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;而在数据链路层使用…

二分(三分)+快速幂

之前学习的二分&#xff0c;现在感觉突然理解许多&#xff0c;补一下总结 首先&#xff0c;二分能够解决什么样的问题呢&#xff0c;个人认为&#xff0c;二分能够快速解决已经知道答案范围&#xff08;线性&#xff09;但是不知道确切答案的问题&#xff0c;例如在一个有序序列…

pthread_cleanup_push与pthread_cleanup_pop的目的 作用

http://blog.csdn.net/slj_win/article/details/7267483 首先你必须知道pthread_cleanup_push与pthread_cleanup_pop的目的(作用)是什么。 比如thread1: 执行 pthread_mutex_lock(&mutex); //一些会阻塞程序运行的调用&#xff0c;比如套接字的accept&#xff0c;等待客…

动态规划浅谈

接触动态规划这么久了&#xff0c;简单谈一下自己对动态规划的理解。 动态规划名字听起来好像比比较高大上&#xff0c;可是事实上&#xff0c;人家就是比较高大上。&#xff08;抖个机灵&#xff09; 刚开始接触动态规划的时候觉得好可怕&#xff0c;这么复杂的问题我怎么能想…

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

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

linux下安装erlang

1.安装Erlang编译依赖: yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget 2.下载Erlang&#xff1a; wget http://www.erlang.org/download/otp_src_19.3.tar.gz 3.解压并安装 tar -xzvf otp_src_19.3.tar.gz cd otp_src_19.3 ./configure --…

Linux 线程同步的三种方法

http://blog.csdn.net/zsf8701/article/details/7844316 线程的最大特点是资源的共享性&#xff0c;但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步&#xff0c;最常用的是互斥锁、条件变量和信号量。 一、互斥锁(mutex) 通过锁机制实现线程…

Elixir特性

iex 退出&#xff1a;Ctrl-C 或Ctrl-G再输入q 回车。 帮助文档&#xff1a;h 查看辅函数列表 h IO 查看IO模块帮助 h IO.puts 查看IO模块中的puts函数的文档 编译和运行&#xff1a;创建一个hello.exs的文件。IO.puts "hello world"    //输出hello world 使用el…

Elixir基础

值类型 整数&#xff0c;包括十进制&#xff08;1234&#xff09;、十六进制&#xff08;0xcafe&#xff09;、八进制&#xff08;0o765&#xff09;和二进制&#xff08;0b1010&#xff09; 浮点数 原子&#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还提供了一个重要的实现…

分块思想

今天学习了一个算法&#xff08;这个应该叫做算法吧&#xff1f;&#xff09;叫做分块&#xff08;和莫队&#xff0c;但是莫队还没有搞懂&#xff0c;搞懂再来写吧&#xff09; 听起来很高级&#xff0c;蒟蒻表示瑟瑟发抖。但是学完发现怎么那么像是一种变相的暴力呢。 分块思…

从零开始学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;核心就是将区间分成许多个小区间然后通过对大区…

命名函数

函数体是代码块 代码块do...end是一种表达式的组织方式。 # ./times.exs下defmodule Times dodef doule(n) don * 2end end 函数调用与模式匹配 代码如下&#xff1a; # ./factorial.exs    计算阶层 defmodule Factorial dodef of(0), do: 1          #终止条件…

STL运用的C++技术(6)——函数对象

http://blog.csdn.net/wuzhekai1985/article/details/6658940?_t_t_t0.20427969420870595 STL是C标准库的重要组成部分之一&#xff0c;它不仅是一个可复用的组件库&#xff0c;更是一个包含算法与数据结构的软件框架&#xff0c;同时也是C泛型编程的很好例子。STL中运用了许多…

列表与递归

头部和尾部 [head | tail ] [1] #head 1 tail [] [head | tail ] [1, 2, 3] #head 1 tail [2, 3] [head | tail ] [] #报错 创建映射函数 我们可以使用一个函数来处理列表中的各个元素&#xff0c;如此可以接受更加复杂的处理&#xff0c;也可以…

优先队列小结

不像栈和队列&#xff0c;虽然STL有较好实现但是我们自己也可以很方便的实现&#xff0c;优先队列自己实现起来就比较复杂&#xff0c;比较浪费时间&#xff08;而且自己目前也不会233&#xff09;而优先队列因为其较好的特性经常被使用&#xff0c;因此对它的熟练掌握是做题的…

字典:散列表、散列字典、关键字列表、集合与结构体

字典 散列表和散列字典都实现了Dict的行为。Keyword模块也基本实现了&#xff0c;不同之处在于它支持重复键。 Eunm.into可以将一种类型的收集映射转化成另一种。 defmodule Sum dodef values(dict) dodict |> Dict.values |> Enum.sumend endhd [ one: 1, two: 2, thre…

C++11 学习笔记 lambda表达式

http://blog.csdn.net/fjzpdkf/article/details/50249287 lambda表达式是C11最重要也最常用的一个特性之一。lambda来源于函数式编程的概念&#xff0c;也是现代编程语言的一个特点。 一.函数式编程简介 定义&#xff1a;简单说&#xff0c;“函数式编程”是一种“编程范式”。…

Cutting Codeforces Round #493 (Div. 2)

Cutting There are a lot of things which could be cut — trees, paper, “the rope”. In this problem you are going to cut a sequence of integers. There is a sequence of integers, which contains the equal number of even and odd numbers. Given a limited bud…