声明
作为一名业余爱好者,最近C语言的基础知识基本学习完毕,于是在众多前辈的指点下开始发奋学习数据结构和算法的相关知识。今天将自己学习数据结构栈的相关知识整理出来权当一份笔记,跟坛子里类似我的业余选手共同学习。不能不说数据结构是众位前辈的智慧结晶,以前看书总是觉得不得其门而入,近日有所悟处,黄口小儿拿来娱乐,且听各大牛指点迷津,不胜感激,是为记。
1. 什么是栈
栈其实就是一种线性表。只不过它有点特殊,它的特殊性主要体现在它是一种后进先出(Last In First Out)的线性表,跟队列刚好相对应,队列刚好是先进先出(First In First Out)的在栈中只能在一端进行操作,就是说保存数据和取出数据只能从线性表的一端进行,一般地操作端我们称之为栈顶,另一端则称为栈底。
2. 栈的基本操作
栈的基本操作只有两个:
入栈(push):即将数据保存在栈顶。进行该操作前,先修改栈顶指针,使其向上移动一个元素位置,然后将数据保存在栈顶指针所指向的位置。
出栈(pop):即将栈顶的数据弹出,然后修改栈顶指针,使其指向栈中的下一个元素。
在栈中,只有栈顶元素是可以访问的。
3. 两种栈
一般地,栈使用两种存储表示方法。
顺序栈:使用一组连续的内存单元依次保存栈中的数据。一般情况下使用数组作为顺序栈,序号为0的元素就是栈底,再定义一个变量来保存栈顶的序号即可。
链式栈:使用链表形式来保存栈中个元素的值,链表尾部(指向地址NULL)为栈底,链表的首部(head指针所指向的元素)为栈顶。
两种栈的最大区别就是顺序栈是分布在一片连续的内存单元里,而链式栈一般是分布在零散的内存单元当中。
接下来我们使用顺序栈来实现一个保存学生姓名和年龄的小实例。下面我们来一步一步的缕一下整个思路。
1.要使用栈,那么我们首先应该定义一个栈结构
//定义顺序栈的结构
typedef struct stack
{
student data[SIZE+1]; //数据元素
int top; //栈顶
}SeqStack;
其中栈结构里面包含了2个元素,一个是结构体数组,数组名为data,其类型为student结构体,用来保存学生的姓名和年龄。另一个整形变量top用来表示栈顶的序号。
2.定义好了栈结构,接下来就应该初始化栈。
//初始化栈
SeqStack * SeqStackInit()
{
SeqStack *p;
if (p = (SeqStack *)malloc(sizeof(SeqStack))) //申请栈内存
{
p->top = 0;
return p;
}
return NULL; //申请内存失败返回空值
}
顺序栈的初始化需要做2件事情:首先需要申请一片合适的内存来保存栈中的数据,然后设置栈顶指针的值为0,表示这是一个空栈。
3. 释放栈内存
//释放栈内存
void SeqStackFree(SeqStack *s)
{
if(s)
free(s);
}
使用完毕,应该及时释放栈内存,否则有可能造成内存泄漏。
4. 判断栈的当前状态
//判断栈的状态
int SeqStackIsEmpty(SeqStack *s) //判断栈是否为空
{
return(s->top == 0);
}
void SeqStackClear(SeqStack *s) //清空栈
{
s->top = 0;
}
void SeqStackIsFull(SeqStack *s) //判断栈是否已满
{
return(s->top == SIZE);
}
在对栈进行操作之前通常先要判断一下栈的当前状态,再决定如何进行操作。入栈前应该先判断栈是否已满,出栈前应该判断栈是否为空。
5. 入栈
//入栈
int SeqStackPush(SeqStack *s, student data) //入栈操作
{
if ((s->top+1) > SIZE)
{
printf("栈溢出!\n");
return 0;
}
s->data[++s->top] = data; //将元素入栈
return 1;
}
首先判断top是否大于等于SIZE,如果结果为真,那么给出溢出提示。
第二设置top=top+1,栈顶指针加1,指向入栈地址,在这里我们可以想一想前面栈的基本操作当中所说的入栈需要做些什么事情。
最后将入栈元素保存到top指向的位置。在这里注意++和->这两个运算符,其中++是个前缀运算符,他的优先级低于->运算符。请注意前缀++和后缀++的优先级是不一样的。
6. 出栈
//出栈
student SeqStackPop(SeqStack *s) //出栈操作
{
if (s->top == 0)
{
printf("栈为空!\n");
exit(0);
}
return (s->data[s->top--]); //弹出栈顶元素
}
首先判断top是否小于等于0.如果结果为真,则提示栈为空。
第二将栈顶指针top所指向位置的元素返回。
最后设置top=top-1,使栈顶减1,指向栈的下一个元素。
在这里同样可以参考一下上面栈的基本操作当中关于出栈的操作。
7. 获取栈顶元素
//获取栈顶的元素
student SeqStackPeek(SeqStack *s) //读取栈顶元素
{
if (s->top == 0) //判断栈是否为空
{
printf("栈为空!");
exit(0);
}
return (s->data[s->top]); //返回栈顶元素
}
使用出栈函数操作后,原来栈顶的元素就被弹出了,也就是说出栈后原来栈顶的元素就不存在了。但是在有些时候我们可能会需要获取栈顶的元素,但又需要继续保留该元素在栈顶,这是就用到了这个函数,注意它与SeqStackPop函数的区别,该函数只是返回当前栈顶的元素,并不修改栈顶指针,所以获取栈顶元素后,原来的栈顶依然存在。
有了上面足够的知识,我们开始在main函数中实现一下这个小实例的具体操作。
以下是SeqStackTest.c的代码
#include
#include
#define SIZE 50
typedef struct
{
char name[15];
int age;
}student;
#include "SeqStack.h"
int main()
{
SeqStack *stack;
student pushstudent,popstudent; //定义栈操作的元素
stack = SeqStackInit(); //初始化栈
printf("入栈操作!\n");
printf("输入姓名 年龄进行入栈操作:\n");
scanf("%s%d",pushstudent.name,&pushstudent.age);
SeqStackPush(stack,pushstudent); //入栈
printf("输入姓名 年龄进行入栈操作:\n");
scanf("%s%d",pushstudent.name,&pushstudent.age);
SeqStackPush(stack,pushstudent); //入栈
printf("\n出栈操作:\n按任意键进行出栈操作:\n");
getchar();
popstudent = SeqStackPop(stack); //出栈
printf("出栈的数据是:\n");
printf("%10s%10s\n","姓名","年龄");
printf("%10s%10d\n",popstudent.name,popstudent.age);
printf("再按任意键进行出栈操作:");
getchar();
popstudent = SeqStackPop(stack); //出栈
printf("出栈的数据是:\n");
printf("%10s%10s\n","姓名","年龄");
printf("%10s%10d\n",popstudent.name,popstudent.age);
SeqStackFree(stack); //释放栈所用的内存
getchar();
return 0;
}
下面是SeqStack.h的代码
//定义顺序栈的结构
typedef struct stack
{
student data[SIZE+1]; //数据元素
int top; //栈顶
}SeqStack;
//初始化栈
SeqStack * SeqStackInit()
{
SeqStack *p;
if (p = (SeqStack *)malloc(sizeof(SeqStack))) //申请栈内存
{
p->top = 0;
return p;
}
return NULL; //申请内存失败返回空值
}
//释放栈内存
void SeqStackFree(SeqStack *s)
{
if(s)
free(s);
}
//判断栈的状态
int SeqStackIsEmpty(SeqStack *s) //判断栈是否为空
{
return(s->top == 0);
}
void SeqStackClear(SeqStack *s) //清空栈
{
s->top = 0;
}
void SeqStackIsFull(SeqStack *s) //判断栈是否已满
{
return(s->top == SIZE);
}
//入栈
int SeqStackPush(SeqStack *s, student data) //入栈操作
{
if ((s->top+1) > SIZE)
{
printf("栈溢出!\n");
return 0;
}
s->data[++s->top] = data; //将元素入栈
return 1;
}
//出栈
student SeqStackPop(SeqStack *s) //出栈操作
{
if (s->top == 0)
{
printf("栈为空!\n");
exit(0);
}
return (s->data[s->top--]); //弹出栈顶元素
}
//获取栈顶的元素
student SeqStackPeek(SeqStack *s) //读取栈顶元素
{
if (s->top == 0) //判断栈是否为空
{
printf("栈为空!");
exit(0);
}
return (s->data[s->top]); //返回栈顶元素
}