本文主要讨论C语言数据结构的又一重要结构——栈
一、栈的理论知识
1. 定义和特点
定义:只允许在一端插入和删除的线性表;允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。
特点:后进先出 (LIFO)
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
使用通俗的话语形容就像压入子弹夹的子弹,先压入的反而在后面击发出去;
2.栈的主要C语言操作
int Push (stack S, StackData x); //进栈,stackS指针域,StackData x数据域
int Pop (stack *S, StackData &x); //出栈
int GetTop (stack *S, StackData &x); //取栈顶
void InitStack (stack *S); //置空栈
int StackEmpty (stack *S); //判栈空否
int StackFull (stack *S); //判栈满否
3.栈的具体图示
- 入栈
- 在进栈之前要先检查是否是空栈
- 出栈
退栈之前也要检查是否空栈
就好比手持步枪与敌人拼杀时,射击之前必须要确定膛内是否有子弹可以击杀敌人;与敌人拼刺刀时要检查膛内子弹是否退完避免误伤队友
二、栈的实现
由表的分类自然而然地可以联想到栈可以分类为顺序栈及链式栈两类
使用简单的例子进行示例,大多数链表建立的过程可表示为以下过程,而栈,队列的建立,大体结构也可以依此建立
——————————————————
类型名 : 简单链表
类型属性: 可以储存的一系列项
类型操作: 1、初始化链表
————— 2、确定链表为空
————— 3、确定链表已满
————— 4、确定链表中项数
————— 5、在链表末尾添加项
————— 6、遍历链表,处理链表中的项
————— 7、清空链表
————————————————————
1. 顺序栈
头文件及结构体定义
#include<stdio.h>
#include<stdlib.h>
#define SIZE 10
enum ret_val{FULL_OK=100,FULL_NO,EMPTY_OK,EMPTY_NO,PUSH_OK,PUSH_NO,POP_OK,POP_NO};
//枚举在“枚举”类型的定义中列举出所有可能的取值,
//在枚举值表中应罗列出所有可用值。这些值也称为枚举元素
struct stack_node //定义一个栈结构体
{
int stack_data[SIZE]; //定义栈的大小
int top; //定义栈顶
};
typedef struct stack_node Stack; //typedef的作用是为已有的数据类型定义一个新名字,
//方便后面写起来简单
分配内存空间及设置栈顶
void create_stack(Stack **stack)
{
*stack = (Stack *)malloc(sizeof(Stack)); //让栈点指向新建节点创建的内存空间
}
void init_stack(Stack * stack)
{
stack->top = -1; //定义栈顶,为判断栈是否为空作准备
}
进栈
int is_full(Stack * stack) //定义进栈的判断值,
//判断是否进栈
{
if(stack->top >=SIZE) //判断栈是否为空
{
return FULL_OK;
}
return FULL_NO;
}
int push_stack(Stack *stack ,int num) //进栈数值(即第几次进栈)
{
if (FULL_NO == is_full(stack))
{
//stack->top++;
stack->stack_data[++stack->top] = num; //栈顶加一,进栈成功
return PUSH_OK;
}
printf("stack is full!\n");
return PUSH_NO;
}
出栈
int is_empty(Stack * stack) //定义出栈的判断值
//判断是否出栈
{
if(stack->top == -1) //判断栈是否为空
{
printf("stack is empty!\n");
return EMPTY_OK;
}
return EMPTY_NO;
}
int pop_stack(Stack *stack ) //出栈数值
{
if (EMPTY_NO == is_empty(stack))
{
return stack->stack_data[stack->top--] ; //栈顶减一,出栈成功
}
else
{
return POP_NO;
}
}
主函数
int main()
{
Stack * stack = NULL; //野指针
int i;
int num;
create_stack(&stack);
init_stack(stack);
for (i = 0; i < 10; i++) //入栈的具体操作
{
if (PUSH_OK == push_stack(stack,i+1))
{
printf("push stack success!\n");
}
else
{
printf("push stack fail!\n");
}
}
for (i = 0; i < 15; i++) //出栈的具体操作
{
num =pop_stack(stack);
if (POP_NO != num) //pop_stack(stack)
{
printf("%d\n",num);
}
else
{
printf("pop stack fail!\n");
}
}
free(stack); //释放分配的栈空间
return 0;
}
2、链式栈
链式栈无栈满问题,空间可扩充
插入与删除仅在栈顶处执行
链式栈的栈顶在链头
适合于多栈操作
头文件及结构体定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node //定义链表节点
{
int num;
char name[20];
struct node *next;
};
struct node_stack //定义栈
{
int num; //
char name[20];
};
typedef struct node * Link;
typedef struct node_stack Stack;
enum result {MALLOC_OK = 100,MALLOC_NO,EMPTY_OK,EMPTY_NO,PUSH_OK,PUSH_NO,POP_NO,POP_OK};
分配空间及设置判定
int create_node(Link * new_node) //创建链式栈,让指针指向栈
{
*new_node = (Link)malloc(sizeof(struct node)); //让栈节点指向新建节点创建的内存空间
if(*new_node == NULL) //判断内存空间是否创建成功
{
printf("malloc error!\n");
return MALLOC_NO;
}
return MALLOC_OK;
}
void create_stack(Link *stack) //定义栈,复查链式栈
{
int ret;
ret = create_node(stack); //承接创建成功的标准,拿这个去对比,
//就好比二分类,1是男,0是女,出现什么数字就判断为什么性别
if(ret == MALLOC_NO) //判断链式栈是否创建成功
{
printf("create stack fail!\n");
exit(-1);
}
(*stack)->next = NULL; //创建栈头(节点)设置为空
}
进栈
int push_stack(Link stack,Stack node) //进栈
{
Link new_node;
int ret;
ret = create_node(&new_node);
if(ret == MALLOC_NO) //判断栈是否为空
{
printf("push error!\n");
return PUSH_NO;
}
//p->data (结构体里变量)= data(输入的数据);
new_node->num = node.num;
strcpy(new_node->name,node.name);
//p->next (插入节点的后继指针)=(指向) top->next(头节点的后继指针);
new_node->next = stack->next;
//top->next (头节点的后继指针)= p(插入的节点);
stack->next = new_node;
return PUSH_OK;
}
出栈
int is_empty(Link stack) //定义出栈的判断值
{
if(stack->next == NULL) //判断栈是否为空
{
printf("stack is empty!\n");
return EMPTY_OK;
}
return EMPTY_NO;
}
int pop_stack(Link stack,Stack * node) //出栈
{
Link p;
p = stack->next; //在栈后插入节点
if(is_empty(stack) == EMPTY_OK)
{
return POP_NO;
}
else
{
//将结构体里的变量还给节点;
node->num = p->num;
strcpy(node->name,p->name);
stack->next = p->next; //头节点的后继指针指向被删除节点的后继指针
free(p); //释放p的空间
return POP_OK;
}
}
主函数
int main()
{
Link stack;
Stack node;
int i;
int ret;
create_stack(&stack);
for(i = 0;i < 5; i++)
{
node.num = i + 1;
scanf("%s",node.name); //输入数据域中的内容
push_stack(stack,node);
}
for(i = 0; i < 5;i++)
{
ret = pop_stack(stack,&node);
if(ret == POP_NO)
{
printf("POP fail!\n");
}
else
{
printf("num = %d\n",node.num);//输出数据域中的内容
printf("name is :%s\n",node.name);
}
}
return 0;
}
三、栈的一个小应用
栈在表达式计算过程中的应用
实际上就例如(a+b)*c(这里只考虑加减乘除和括号这几个符号)这样一个表达式,你怎么用栈来进行计算
首先新建两个栈(链式栈的优点),一个储存数字,一个储存运算符
数字一律进栈,字符需要考虑运算级,两个运算符先后进栈,比较,等级高的进栈,低的,先计算该运算符然后把计算好的数字代替运算符两侧的数字进栈,该运算符不进栈
左括号一律进运算符栈,右括号一律不进运算符栈,取出运算符栈顶运算符和操作数栈顶的两个操作数进行运算,并将结果压入操作数栈,直到取出左括号为止。
头文件及结构体
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
struct operand //定义第一个栈结构体,存储数字
{
int data[MAX]; //定义栈元素数组
int top; //定义栈顶
};
struct operator_ch //定义第二个栈,存储运算符
{
int top;
char data[MAX];
};
typedef struct operand OPND; //重定义
typedef struct operator_ch OPCH;
分配空间及设置判定
void init_OPND_stack(OPND*stack)
//void表示空类型,它跟int,float是同地位的,
//一般用在没有返回值的函数中
{
stack->top = -1;
}
void init_OPCH_stack(OPCH*stack) //初始化函数
{
stack->top= -1; //定义栈顶,为判断是否为空作准备
}
int is_empty_OPND(OPND*stack) //定义判断数字栈为空的函数
{
if (stack->top == -1)
{
return -1;
}
return 0;
}
int is_empty_OPCH(OPCH*stack) //定义判断字符栈为空的函数
{
if (stack->top == -1)
{
return -1;
}
return 0;
}
int get_OPND_top(OPND*stack) //定义数字栈的判断函数
{
if (is_empty_OPND(stack) == -1)
{
return -1;
}
return stack->data[stack->top]; //形似a[],返回栈数组的值
}
char get_OPCH_top(OPCH*stack) //定义字符栈的判断函数
{
if (is_empty_OPCH(stack) == -1)
{
return -1;
}
return stack->data[stack->top]; //形似a[],返回栈数组的值
}
进出栈函数
void push_OPND(OPND*stack,int num) //定义数字栈进栈函数
{
stack->data[++(stack->top)] = num;
printf("push%d\n",stack->data[stack->top]);
}
void push_OPCH(OPCH*stack,char ch) //定义字符栈进栈函数
{
stack->data[++(stack->top)] = ch;
}
int pop_OPND(OPND*stack) //定义函数栈出栈函数
{
if (is_empty_OPND(stack) == -1)
{
return -1;
}
int num = stack->data[stack->top];
(stack->top)--;
return num;
}
char pop_OPCH(OPCH*stack) //定义字符栈出栈函数
{
if (is_empty_OPCH(stack) == -1)
{
return -1;
}
char ch = stack->data[stack->top];
(stack->top)--;
return ch;
}
获得字符函数及表达式函数
int get_priority(char ch) //定义获得字符函数
{
if (ch == '+' || ch == '-')
{
return 1;
}
if (ch == '*' || ch == '/')
{
return 2;
}
}
int compare_priority(char op_ch,char ch) //定义比较字符优先级的函数
{
if (get_priority(op_ch)>=get_priority(ch))
{
return 1;
}
return -1;
}
int count(int a,int b,char ch) //定义计算表达式的函数
{
int result;
switch(ch)
{
case '+':
{
result = a+b;
break;
}
case '-':
{
result = a-b;
break;
}
case '*':
{
result = a*b;
break;
}
case '/':
{
result = a/b;
break;
}
}
return result;
}
主函数
int main() //主函数
{
char ch;
char op_ch;
int a,b;
int opr_num;
int result_cal;
OPND * opnd_stack;
OPCH * opch_stack;
opnd_stack = (OPND *)malloc(sizeof(OPND)); //分配内存空间
opch_stack = (OPCH *)malloc(sizeof(OPCH));
init_OPND_stack(opnd_stack); //初始化栈
init_OPCH_stack(opch_stack);
while((ch = getchar()) != '\n') //从栈里取字符
{
if(ch >= '0' && ch <= '9') //数字入栈
{
// push_stack1();
opr_num = ch - '0';
push_OPND(opnd_stack,opr_num);
}
else
{
if(ch == '+' || ch == '-' || ch == '*' || ch == '/')
{
//op_ch = pop_stack2();
do
{
op_ch = get_OPCH_top(opch_stack); //得到栈顶元素
if(op_ch == -1) //如果栈顶元素为空
{
push_OPCH(opch_stack,ch); //字符入栈
break;
}
else
{
if(compare_priority(op_ch,ch) < 0) //如果先前字符优先级小于新进运算符优先级,输入新进运算符
{
// push_stack2(ch);
push_OPCH(opch_stack,ch);
break;
}
else
{
a = pop_OPND(opnd_stack);
b = pop_OPND(opnd_stack);
op_ch = pop_OPCH(opch_stack);
if(a == -1 || b == -1 || op_ch == -1) //如果数字栈或字符栈其中一个为空,输出栈为空
{
printf("stack is empty!\n");
}
// printf("%d%c%d",a,op_ch,b);
// printf("\n");
result_cal = count(b,a,op_ch);
push_OPND(opnd_stack,result_cal); //已计算的结果进栈
}
}
}while(compare_priority(op_ch,ch) > 0);
// push_OPCH(opch_stack,ch);
}
}
} //第一遍计算,只是把数字和字符入栈,没有计算总结果
while(is_empty_OPND(opnd_stack) != -1 && is_empty_OPCH(opch_stack) != -1)
{
a = pop_OPND(opnd_stack);
b = pop_OPND(opnd_stack);
op_ch = pop_OPCH(opch_stack);
result_cal = count(b,a,op_ch);
push_OPND(opnd_stack,result_cal); //两个栈都不为空时,计算总结果并入栈
}
// printf("%d%c%d",a,op_ch,b);
// printf("\n");
printf("result is %d\n",result_cal);
free(opnd_stack); //释放栈空间
free(opch_stack);
return 0;
}