目录

一、栈的定义

1.1相关概念与特点

(1)相关概念

(2)相关特点

(3)栈的运算

(4)顺序栈和链式栈的区别

二、栈的表示和实现

2.1顺序栈的表示和实现

(1)结构体定义

(2)建立空栈

(3)入栈操作

(4)出栈操作

2.2链式栈的表示和实现

(1)结构体定义

(2)初始化操作

(3)入栈操作

(4)出栈操作

(5)输出栈中所有元素

(6)判断是否为空栈

(7)清空栈

(8)销毁栈

三、栈与递归

3.1 函数嵌套应用过程

3.2 函数递归应用过程

四、经典实例

4.1 汉诺塔问题

4.2 回文问题

4.3 多进制转换问题

4.4 括号匹配问题

4.5表达式求值问题(重要!)


一、栈的定义

1.1相关概念与特点

(1)相关概念

栈(Stack)是只允许在一端进行插入或删除操作的线性表。(仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈)

图1.1-1 现实生活中栈的实例

图1.1-2 双栈与超栈

(2)相关特点

 先进后出(FILO)或后进先出(LIFO)

(3)栈的运算

置空栈 取栈顶元素 判空栈 入栈 出栈

(4)顺序栈和链式栈的区别

和顺序栈相比,链栈有一个比较明显的优势是:通常不会出现栈满的情况。

因为顺序栈用数组实现,必须事先确定栈的大小,对内存的使用效率并不高,无法避免因数组空间用光而引起的溢出问题;而链栈因为动态申请内存,一般不会出现栈满情况。

二、栈的表示和实现

2.1顺序栈的表示和实现

(1)结构体定义

这些代码实现其实比顺序表链表还要更简单。

代码实现如下:

typedef int ElemType
typedef struct {ElemType* base;ElemType* top;int stacksize;
}sqStack;

定义一个结构体,base是栈底指针,top是栈顶指针,stacksize是当前站的容量

(2)建立空栈

图2.1-1 建立空栈

代码实现如下:

#define STACK_INIT_SIZE 100
int Init_Stack(sqStack* s)
{s->base = (ElemType*)malloc(STACK_INIT_SIZE * sizeof(ElemType));//申请一段空间if (!s->base)//倘若空间申请失败则返回错误return -1;s->top = s->base;//最开始栈底就是栈顶,设置初始栈顶指针s->stacksize = STACK_INIT_SIZE;//设置顺序栈容量return 0;
}

(3)入栈操作

图2.1-2 入栈操作

代码实现如下:

#define STACKINCRINREMENT 10//每次增加的容量
//若栈s存在,则插入e到s中并成为栈顶元素
int Push(sqStack* s, ElemType e)
{if (s->top - s->base >= s->stacksize){s->base = (ElemType*)realloc(s->base ,s->stacksize + STACKINCRINREMENT);s->top = s->base + s->stacksize;s->stacksize += STACKINCRINREMENT;}*(s->top) = e;s->top++;return 1;
}

(4)出栈操作

图2.1-3 出栈操作

代码实现如下:

int Pop(sqStack* s, ElemType* e)
{if(s->stacksize == 0) return -1;*e = *(s->top);s->top--;return 1;
}

整个过程相对而言比较容易,看懂会自己实现即可。

2.2链式栈的表示和实现

链栈是指采用链式储存结构实现的栈。通常链栈是用单链表来表示。链栈的节点结构与单链表的结构相同。

思路基本和顺序栈相同,此处相关流程不再赘述,直接放代码。

(1)结构体定义

typedef int Status;
typedef struct Node{int data;          //结点的数据域struct Node *next;  //结点的指针域
}Node,*LinkList;

(2)初始化操作

Status Init_Stack(LinkList &S)
{S = (LinkList)malloc(sizeof(Node));if(!S)return -1;S->next = NULL;return 1;
}

(3)入栈操作

Status Push_Stack(LinkList &S,int e)
{LinkList p = (LinkList)malloc(sizeof(Node));if(!p)return -1;p->data = e;p->next = S->next;S->next = p;return 1;
}

(4)出栈操作

    Status Pop_Stack(LinkList &S){LinkList p = S->next;if(!p)return -1;S->next = p->next;free(p);return 1;}

(5)输出栈中所有元素

Status All_Stack(LinkList S)
{int i = 0;LinkList p = S->next;if(!p)return -1;while(p){printf("第%d个元素为:%d\n",++i,p->data);p = p->next;}return 1;
}

(6)判断是否为空栈

Status qwe(LinkList &S)
{LinkList p = S->next;if(!p)return 0;else return 1;
}

(7)清空栈

Status Clear_Stack(LinkList &S)
{LinkList p = S->next;if(!p)return -1;while(p){LinkList q = p;p = p->next;free(q);}//最后这一步,切记不可忘S->next = NULL;return 1;
}

(8)销毁栈

Status DestoryStack(LinkList &S)
{LinkList p=S;while(p){LinkList q = p;p = p->next;free(q);}
}

三、栈与递归

递归是一种重要的程序设计方法。简单地说,若在一个函数、过程或数据结构的定义中又应 用了它自身,则这个函数、过程或数据结构称为是递归定义的,简称递归。

它通常把一个大型的复杂问题层层转化为一个与原问题相似的规模较小的问题来求解,递归 策略只需少量的代码就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。 但在通常情况下,它的效率并不是太高。

3.1 函数嵌套应用过程

我们先来了解一下函数嵌套应用的过程:

图 3.1-1 函数嵌套栈应用的全过程

当一个函数在运行期间调用另一个函数时,在运行被调用之前,系统则需完成3件事:

1.将所有的实在参数,返回地址等信息传递给被调用函数保存
2.为被调函数的局部变量分配存储区域
3.将控制转移到调用函数入口

而从被调函数返回调用函数之前,系统也会完成3件事:

1.保存被调函数的计算结果
⒉释放被调函数的数据区
3.依照被调函数保存的返回地址将控制转移到调用函数上

当有多个函数结构嵌套调用时,按照“后调用先返回”的原则,上述函数之间的信息传递和控制转移必须通过“栈”来实现。即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区每当一个函数退出时,就释放它的存储区,则当前正运行的函数的数据区必须在栈顶。

3.2 函数递归应用过程

图 3.2-1 函数递归栈应用的全过程

1、当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需先完成3件事:

(1)将所有的实在参数、返回地址等信息传递给被调用函数保存。

(2)为被调用函数的局部变量分配存储区。

(3)将控制转移到被调函数的入口。

2、从被调函数返回调用函数之前,系统也应该完成3件工作:

(1)保存被调函数的计算结果。

(2)释放被调函数的数据区。

(3)依照被调函数保存的返回地址将控制转移到调用函数。

3、一个递归函数的运行过程类似于多个函数的嵌套调用,只是调用函数和被调函数是同一个函数,因此,和每次调用相关的一个重要的概念是递归函数运行的“层次”。

举个例子:

这里以斐波那契数列举例。

/* 斐波那契的递归函数 */
int Fbi(int i) {if (i <2)return i == 0 ? 0 : 1;/* 这里的 Fbi 就是函数自己,它在调用自己 */return Fbi(i - 1) + Fbi(i - 2);
}
int main() {int i;for (i = 0; i < 40; i++)printf("%d", Fbi(i));return 0;
}

递归过程分为两步“递”和“归”,对应着栈的两种操作“进栈”和“出栈”。

图3.2-2 n=5时斐波那契数列递归图

(1)首先n=5时,符合递归条件,放入栈中

(2)由于返回的是Fbi(i - 1) + Fbi(i - 2)。故先判断4,在n=4时符合递归条件,放入栈中。

(3)同理,栈中从n=5一直放到n=1

(4)判断n=1不满足递归条件,进行出栈。返回fib(2)在n=0时继续入栈,n=0也不满足条件,进行出栈,fib(2)进行出栈返回fib(3)指向的右端的fib(1)继续判断,直至全部结束。

四、经典实例

4.1 汉诺塔问题

问题描述:有塔A,B,C;塔A上有64个碟子,按从大到小的顺序从塔底堆放至塔顶.有另外两个塔B,C.将A上碟子移动到塔C上去,期间借助塔B的帮助.每次只能移动一个碟子,任何时候不能把碟子放在比它小的碟子上面。

对于此问题,可以通过以下三个步骤完成:

1)将塔A上n-1个碟子,借助塔C先移动到塔B上;

2)将塔A上最后一个碟子移动到塔C上;

3)借助塔A将塔B上n-1个碟子移动到塔C上.

显然,这是一个递归求解的过程:

void Move(char A, char B)
{printf("%c -> %c\r\n", A, B);
}
void Hanoi(int n, char A, char B, char C)
{if (n == 1){Move(A, C);}else{Hanoi(n-1, A, C, B);Move(A,C);Hanoi(n-1, B, A, C);}
}int main()
{int m;printf("请输入总数:");scanf("%d", &n);printf("移动步骤为:\n");Hanoi(n, 'A', 'B', 'C');return 0;
}

4.2 回文问题

回文指正读和反读都相同的字符序列为“回文”,如“abba”、“abccba”、12321、123321是“回文”,“abcde”和“ababab”则不是“回文”。

接下来我们会用栈的思想判断一个数是不是回文数。(当然也有数学方法,例如双指针法,感兴趣可以去看我的另一篇博客,c语言力扣第九题之回文数)。

相关思路也比较简单

        1.读入字符串
        2.去掉空格(原串)
        3.压入栈
        4.出栈字符与原串字符依次比较。若不等,非回文;若直到栈空都相等,回文。

#include <stdio.h>
#include <string.h>//数组长度的头文件
int main() {char a[101], s[101];int i, j, mid, top,len;gets(a);//读入一行字符串len = strlen(a);//求字符串的长度mid = len / 2 - 1;//求字符串的中点top = 0;//初始化栈顶//初始化栈for (i = 0; i <= mid; i++)s[++top] = a[i];//判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的起始下标if (len % 2 == 0)j = mid + 1;elsej = mid + 2;//逐个比较for (j; j < len; j++) {if (s[top] != a[j])break;top--;}//如果比较完毕输出yes,否则noif (top == 0)printf("Yes");elseprintf("No");getchar();getchar();return 0;}

4.3 多进制转换问题

十进制怎么转为二进制?

十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为小于1时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。其实不难发现这也是个用栈解决的问题

#include<stdio.h>
#include<malloc.h>
#include<math.h>
#include<string.h>
#include "process.h"
#define SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;typedef struct
{int a;
} SElemType;typedef struct
{SElemType *base;SElemType *top;int stacksize;
} SqStack;SqStack S; //定义全局变量Status InitStack(SqStack *S)
{S->base=(SElemType *)malloc(SIZE*sizeof(SElemType));if(!S->base) exit(OVERFLOW);S->top=S->base;S->stacksize=SIZE;return OK;
}Status Push(SqStack *S,SElemType e)
{if(S->top-S->base>=S->stacksize){S->base=(SElemType *)malloc((S->stacksize+STACKINCREMENT)*sizeof(SElemType));if(!S->base) exit(OVERFLOW);S->top=S->base+S->stacksize;S->stacksize+=STACKINCREMENT;}*S->top++=e;//printf("%dwww\n",*--S->top);return OK;
}Status Stackempty(SqStack *S)
{if(S->top==S->base)return TRUE;elsereturn FALSE;
}Status Pop(SqStack *S,SElemType *e)
{if(S->top==S->base) return ERROR;*e=*--S->top;return OK;
}Status DtoBTrans(int N,SqStack *S)
{SElemType e;while(N){e.a=N%2;Push(S,e);N=N/2;}while(!Stackempty(S)){Pop(S,&e);printf("%d",e);}return OK;
}void main()
{int x;InitStack(&S);printf("请输入十进制数:");scanf("%d",&x);DtoBTrans(x,&S);
}

4.4 括号匹配问题

给定一个只包括 (){}[] 的字符串,判断字符串是否有效。
有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

输入: “()”
输出: true

输入: “()[]{}”
输出: true

输入: “([)]”
输出: false

1:遍历输入的括号序列,如果是左括号,进入2,如果是右括号,进入3
2:如果当前遍历到左括号,则入栈
3:如果当前遍历到右括号,则出栈一个元素,看其是否与当前的右括号组成一对,如果不是,则匹配失败。或者在出栈过程中发生异常(从空栈中出栈),也匹配失败
4:若能顺利遍历完成,检查栈中是否还有剩余元素,如果有,则匹配失败;如果没有,则匹配成功。

以下为栈的相关定义

typedef struct
{char *base;char *top;int stacksize;
}Stack;void InitStack(Stack &s)//初始化栈
{s.base = (char *)malloc(STACK_SIZE * sizeof(char));s.top = s.base;s.stacksize = STACK_SIZE;
}void Push(Stack &s,char e )//入栈
{//默认100的空间,相对来说够了,不再分配空间*s.top++ = e;
}int pop(Stack &s, char &e)//出栈
{if (s.base == s.top)return 0;else{e = *--s.top;return 1;}
}int Gettop(Stack &s,char &e)//查看栈顶元素
{if (s.top == s.base)return 0;elsee = *(s.top - 1);return 1;
}bool IsEmpty(Stack s)//判断栈是否为空
{if (s.base == s.top)true;elsereturn false;
}

以下为括号匹配算法和主函数

int BMT(Stack &s,char str[])
{int lenth = strlen(str);int i;char topelem;//储存栈顶元素char e;for (i = 0; i < lenth; i++){if (IsEmpty(s) || str[i] == '[' || str[i] == '(')Push(s, str[i]);//字符串字符逐一进栈else{Gettop(s, topelem);if (topelem == '('&& str[i] == ')'){pop(s, e);//合法出栈}else if (topelem == '['&& str[i] == ']'){pop(s, e);//合法出栈}elsebreak;//不合法,结束循环,不用继续进栈了}}if (IsEmpty(s)){printf("匹配");}else{printf("不匹配");}return 0;
}int main()
{Stack  s1;InitStack(s1);char str[100];gets(str);BMT(s1, str);return 0;
}

4.5表达式求值问题(重要!)

任何一个表达式都是由操作数(operand)、运算符(operator)和界限符(delimiter)组成。
由于算术运算的规则是:先乘除后加减、先左后右和先括弧内后括弧外,则对表达式进行运算不能按其中运算符出现的先后次序进行。

先了解几个概念:

后缀表达式,运算符在两个操作数的后面,如ab+

前缀表达式,运算符在两个操作数在前面,如+ab

例如:若 Exp=a×b+(c-d / e)×f  
则它的前缀式为:+×a b×-c / d e f
中缀式为:a×b+c-d / e×f
后缀式为:a b × c d e / -f×+

综合比较它们之间的关系可得下列结论:

1.三式中的 “操作数之间的相对次序相同”;

2.三式中的 “运算符之间的的相对次序不同”;

3.   后缀式的运算规则为:
·运算符在式中出现的顺序恰为表达式的运算顺序;
·每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式;

对于机器来说,后缀运算符更容易阅读,我们要求解某个表达式,首先要将中缀表达式转化成后缀表达式,再对后缀表达式进行入栈出栈等操作。

转化后缀表达式流程:

1.定义栈后,设表达式的结束符为“#”,预设运算符栈的栈底为“#”;

2.若当前字符是操作数,则直接发送给后缀式;

3.若当前字符为运算符且优先数大于栈顶运算符,则进栈,否则退出栈顶运算符发送给后缀式;

4.若当前字符是结束符,则自栈顶至栈底依次将栈中所有运算符发送给后缀式;

5.若当前为左括号,输入后缀表达式,若当前为右括号,则将栈中的表达式输入后缀表达式直至遇到左括号。

现在我们得到了后缀表达式,具体对后缀表达式的操作如下:

1、从左往右扫描下一个元素,直到处理完所有元素

2、若扫描到操作数则压入栈,并回到1,否则执行3

3、若扫描到运算符,则弹出两个栈顶元素,执行相应运算(先出栈的是右操作数,涉及到运算顺序),运算结果压回栈顶,回到1

最后如果表达式合法则最后栈中只会留下一个元素,就是最终结果。

具体主函数实现如下:

int main()
{stack Pol;//构造后缀表达式所用的栈,为char类型stackint S;//计算后缀表达式的值所用到的栈,为float类型ElemType temp1;ElemType temp2;ElemType temp3;ElemType temp4;//临时变量float a, b,d;float c;int length=0;char PolRear[1000];//后缀表达式,此处不能使用栈结构表示后缀表达式,仔细理解其构造过程和运算过程可知,这应该是个先入先出结构int temp = 0;InitStack(Pol);//初始化polrear(Pol, PolRear,temp1,temp2,length);for (int i = 0; i < length; i++)printf("%c", PolRear[i]);initstack(S);do//计算后缀表达式的值{if (temp+1!=length){temp1 = PolRear[temp];temp++;}elsebreak;c = temp1 - 48;//asc码数字与真实数字相差48if ('0' <= temp1 && '9' >= temp1){push(S, c);//进栈}else//注意运算顺序,先出栈的为被操作数{pop(S,&a);  //出栈pop(S,&b);if (temp1 == '+')c = a + b;else if (temp1 == '-')c = b -a;else if (temp1 == '*')c = a * b;else if (temp1 == '/')c = b / (a * 1.0);push(S, c);}} while (1);printf("%f", c);return 0;
}

考研复习之数据结构笔记(五)栈和队列(上)(包含栈的相关内容)相关推荐

  1. 考研复习之数据结构笔记(十二)图(下)(图的应用,包含最小生成树、最短路径、拓扑排序、关键路径以及单元小结)

    目录 一.图的应用 1.1 最小生成树 (1)基本概念与问题引入 (2)Prim(普里姆)算法 (3)Kruskal(克鲁斯卡尔)算法 1.2 最短路径 (1)基本概念与问题引入 (2)Dijkstr ...

  2. 考研复习之数据结构笔记(九)树(上)(树和二叉树的概念、特征、性质及相关实例)

    目录 一.树的基本概念和术语 1.1 基本定义与特点 (1)基本定义: (2)基本特点 1.2 树的基本术语 1.3 树的基本性质 二.二叉树 2.1 二叉树的定义与性质 (1)基本概念 (2)与树的 ...

  3. sdut 3335 数据结构实验之栈与队列八:栈的基本操作

    数据结构实验之栈与队列八:栈的基本操作 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Discuss Problem Descri ...

  4. 【数据结构】栈和队列OJ练习(栈和队列相互实现+循环队列实现)

    目录 前言 1.用队列实现栈 2.用栈实现队列 3.循环队列 前言 前面在学习了栈和队列的实现之后,相信大家对栈和队列的结构和使用方式都有了一些理解. 下面我们就来进行一些练习,这这章的练习相对于原来 ...

  5. 考研复习之数据结构(六)栈和队列(下)(包含队列的相关内容、栈和队列的对比以及总结)

    目录 一.队列的定义 1.1 相关概念和特点 (1)基本概念 (2)队列特点 (3)案例引入 2.1 队列分类 二.队列的表示和实现 2.1 顺序队列的表示和实现 (1)结构体定义 (2)初始化操作 ...

  6. C语言数据结构(大话数据结构——笔记2)第四章:栈与队列

    文章目录 第四章:栈与队列(115) 栈顶与栈底,空栈,后进先出 Last in first out(LIFO结构)(117) 进栈.压栈.入栈:栈的插入操作:出栈.弹栈:栈的删除操作(118) pu ...

  7. Java数据结构(1.1):数据结构入门+线性表、算法时间复杂度与空间复杂度、线性表、顺序表、单双链表实现、Java线性表、栈、队列、Java栈与队列。

    数据结构与算法入门 问题1:为什么要学习数据结构          如果说学习语文的最终目的是写小说的话,那么能不能在识字.组词.造句后就直接写小说了,肯定是不行的, 中间还有一个必经的阶段:就是写作 ...

  8. 用Java描述数据结构之栈和队列,以及栈和队列的常用方法

    一般都是在学完线性表(顺序表和链表)之后,才会去学习栈和队列,因此可能会觉得栈和队列是一种新的数据结构,其实不然.它们逻辑上还是一对一的关系,所以说它们本质还是线性表,只不过是加了一定限制的线性表,具 ...

  9. 栈与队列7——单调栈结构(初阶问题)

    题目 一个不含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比arr[i]小的位置,返回所有相应的信息. 举例:arr={3,4,1,5,6,2,7},返回如下的二维数组作为结果:{ ...

最新文章

  1. 使用AD5933测量元器件的谐振特性
  2. 双边滤波器在灰度和彩色图像处理中的应用
  3. 实战SSM_O2O商铺_12【商铺注册】View层之前台页面
  4. 【机器学习基础】GBDT 与 LR 的区别总结
  5. JavaBean的命名规则
  6. java.lang.IllegalStateException: getOutputStream() has already been called for this response
  7. Springboot(java)程序部署到k8s
  8. 【转】赶集网mysql开发36军规
  9. 微软发布2016年5月安全补丁 提醒及时修复
  10. Eclipse注释快捷键、如何生成API以及可能遇到的问题解决
  11. SpringBoot实战之文件上传微软云(Azure Storage)
  12. GWR模型报错汇总(arcgis与GWR4)
  13. 自加载宏让你的Excel支持正则处理函数
  14. 简述er图的作用_用例图、ER图、功能结构图
  15. 简单说一下寄存器寻址
  16. Java跨年祝福语代码_[商业跨年祝福语]跨年祝福语贺词大全
  17. 搞笑新闻联播之老公岗位制度(中)铃声 搞笑新闻联播之老公岗...
  18. day55 虚拟机 centos linux命令 搭建linux架构师必背命令
  19. pq分解法matlab程序,基于MATLAB软件的PQ分解法潮流计算
  20. 如何生成git的公钥和私钥

热门文章

  1. python decimal_Python中的decimal模块执行精确的浮点运算
  2. teraterm使用ttl文件连接服务器
  3. 关于RemoteView的一点愚见(RemoteView在AppWidget中的工作流程)
  4. 计算机网络技术 教案,《计算机网络技术》教案高教社中职
  5. 10月国内电脑分辨率十强:1920*1080占比破15%
  6. [StackExchange]Redis 发布订阅
  7. 激荡 20 年:IE 浏览器的辉煌与落寞
  8. 树莓派(十一)树莓派驱动开发入门:从读懂框架到自己写驱动(上)
  9. springboot 导出CSV
  10. web——md5 collision(NUPT_CTF)(100)——Bugku