堆栈和队列都是特殊的线性表,其数据元素以及数据元素之间的逻辑关系完全相同,区别在于:线性表的插入和删除操作不受限制,堆栈只能在栈顶插入和删除,队列只能在队尾插入,在队头删除.
对于堆栈,作为参数的时候一般统一都对它其地址,尽管并不需要对它进行修改,加个const就好了(听老师说这样比较统一好看??)
- 堆栈的操作集合:
1.初始化
因为之前我是在网上找的博客,发现有很多人在初始化的时候都是将栈顶指针初始化为了-1,然后我觉得也挺有道理,这样正好指向的是当前最后进来的元素的位置.但是后来发现有些教材上会将栈指针初始化为0,也就是指向了最后进来的元素的下一个位置.我觉得还是初始化为0更好一点!
初始化其实也就是初始化栈指针.
2.判断是否非空
判断一个栈是不是空栈只需要看栈顶指针的值是不是0即可.
3.入栈
将元素放入指针现在所指向的位置,并且指针向后移动一个单位.
4.出栈
将指针向前移动一个单位,就相当于删掉了最后的一个元素.
5.得到栈顶元素
得到栈顶元素并不要求将它删除,只需要将最后一个元素作为函数返回值即可.
以下是一个集合了所有操作的小程序:
#include<stdio.h>
#define MaxSize 100
struct stack{
int nums[MaxSize];
int top;
};
void Stackinitiate(struct stack*Stack);
void StackPush(struct stack*Stack,int num);
int StackNotEmpty(const struct stack*Stack);
void StackPop(struct stack*Stack);
int get_Stack_top(const struct stack*Stack);
int main() {
struct stack Stack;
int num,result;
Stackinitiate(&Stack);
while(scanf("%d",&num)!=EOF) {
StackPush(&Stack,num);
result=get_Stack_top(&Stack);
if(StackNotEmpty(&Stack)) {
StackPop(&Stack);
}
printf("%d\n",result);
}
return 0;
}
void Stackinitiate(struct stack*Stack) {
Stack->top=0;
return;
}
void StackPush(struct stack*Stack,int num) {
if(Stack->top>=MaxSize) {
printf("The stack is full!\n");
return;
}
else {
Stack->nums[Stack->top]=num;
(Stack->top)++;
}
return;
}
int StackNotEmpty(const struct stack*Stack) {
if(Stack->top) {
return 1;
}
printf("The stack is empty!\n");
return 0;
}
void StackPop(struct stack*Stack) {
(Stack->top)--;
return;
}
int get_Stack_top(const struct stack*Stack) {
int result;
result=Stack->nums[(Stack->top)-1];
return result;
}
下面看一下堆栈的具体应用-----括号匹配问题.
因为最后面的括号要最先与开头匹配,所以可以利用堆栈后进先出的性质来解决.
算法:
顺序扫描一个字符串,如果遇到了三种类型括号的左括号,就让括号进栈.
如果遇到了右括号,就比较栈顶括号是否匹配,若不匹配则配对次序不正确,程序结束;若匹配则退栈继续扫描;
若右括号还没有匹配完成堆栈就空了或扫描完了整个数组堆栈仍非空,则说明括号的左右个数不匹配,程序结束.
代码实现:
#include<stdio.h>
#include<string.h>
#define MaxSize 100
struct stack{
char strings[MaxSize];
int top;
};
void Stackinitiate(struct stack*Stack);
void StackPush(struct stack*Stack,char single);
int StackNotEmpty(const struct stack*Stack);
void StackPop(struct stack*Stack);
int is_in_pairs(const char*str,struct stack*Stack);
int is_order(struct stack*Stack,char single);
int main() {
struct stack Stack;
char str[MaxSize]={0};
scanf("%s",str);
Stackinitiate(&Stack);
if(is_in_pairs(str,&Stack)) {
printf("Yes\n");
}
else {
printf("No\n");
}
return 0;
}
int is_in_pairs(const char*str,struct stack*Stack) {
int length;
int i;
length=strlen(str);
for(i=0;i<length;i++) {
if(str[i]=='('||str[i]=='{'||str[i]=='[') {
StackPush(Stack,str[i]);
}
else if(str[i]==')'||str[i]=='}'||str[i]==']') {
if(is_order(Stack,str[i])) {
StackPop(Stack);
}
else{
return 0;
}
}
}
if(StackNotEmpty(Stack)) {
return 0;
}
return 1;
}
int is_order(struct stack*Stack,char single) {
char top_element=Stack->strings[(Stack->top)-1];
if(!StackNotEmpty(Stack)) {
return 0;
}
if(single==')'&&top_element=='(') {
return 1;
}
else if(single==']'&&top_element=='[') {
return 1;
}
else if(single=='}'&&top_element=='{') {
return 1;
}
return 0;
}
void Stackinitiate(struct stack*Stack) {
Stack->top=0;
return;
}
void StackPush(struct stack*Stack,char single) {
if(Stack->top>=MaxSize) {
return;
}
else {
Stack->strings[Stack->top]=single;
(Stack->top)++;
}
return;
}
int StackNotEmpty(const struct stack*Stack) {
if(Stack->top) {
return 1;
}
return 0;
}
void StackPop(struct stack*Stack) {
(Stack->top)--;
return;
}
因为队列的操作涉及到两端的插入和删除,因此不同于堆栈只有一个栈顶指针,队列需要一个队头指针front,和一个队尾指针rear.
假如我们只看一个顺序的队列,很有可能会发生下面这种情况:
很显然,这个队列最多可以接收5个元素,但是在这时只有三个元素,但rear指针却越界了,这也就是经常说的"假溢出"的问题,即该溢出并不是因为存储空间不够而造成的.(如果队列定义的最大空间都存满了,但还有要入队的元素,这被称为真溢出)
为了解决这种问题,顺序循环队列出现了.
当front和rear到达队列的最后一个元素即maxsize-1时,再自增一后,其值变为0.在逻辑上实现了一个首尾相连的循环队列.(通过取模操作实现)
但是如果只对指针进行取模运算,存在着队空状态与队满状态相同,无法区分的情况.(也就是队空与队满都是front=rear的情况)
解决方法一般有两种:
1.在声明的时候多声明出一个存储单元,虽然并不使用,但是用它来区别队满与队空:
(rear+1)%length==front-----队满
rear==front-----队空
2.设置一个counter变量:
将counter初始化为0,如果入队成功,counter++;如果出队成功,counter- -.
counter==0----队空
counter= =length-1 / counter>0&&front= =rear—队满
(就我个人而言,我比较喜欢第一种方法)
- 队列的操作集合(只考虑顺序循环队列)
先声明:
#define maxsize 11
typedef struct que{
int numbers[maxsize];
int front;
int rear;
}que;
1.初始化
void queue_initiate(que*Queue) {
Queue->front=0;
Queue->rear=0;
return;
}
2.是否非空
int Que_not_empty(const que*Queue) {
if(Queue->rear==Queue->front) {
return 0;
}
return 1;
}
3.入队
void queue_append(que*Queue,int input) {
if(((Queue->rear)+1)%maxsize==Queue->front) {
printf("The queue is full!\n");
}
else {
Queue->numbers[Queue->rear]=input;
Queue->rear=(Queue->rear+1)%maxsize;
}
return;
}
4.出队
void queue_delete(que*Queue) {
Queue->front=(Queue->front+1)%maxsize;
return;
}
void queue_get(const que*Queue) {
printf("%d\n",Queue->numbers[Queue->front]);
return;
}
(注:以上函数均是自己摸索着写出来的,可能没有标准的函数规范,仅供参考~)