1.栈的特点
先进后出或者说后进先出。
2.基于顺序表实现栈的相关操作
2.1 头文件seqstack.h
//头文件只被编译一次
#pragma once
//宏定义一个标识符,用于打印函数名
#define HEADER printf("==================%s==================\n",__FUNCTION__);
//定义栈中元素的数据类型
typedef char SeqStackType;
//顺序表实现栈时定义结构体
typedef struct SeqStack
{
//用于存放栈的元素,之所以定义为指针类型而不是指定大小的数组类型,是为了方便动态开辟内存
SeqStackType* data;
//用来表示栈的有效元素个数
size_t size;
//用来表示栈首次申请的空间的最大长度
size_t capacity;
}SeqStack;
/*=========函数声明========*/
//1.初始化
void SeqStackInit(SeqStack* stack);
//2.销毁
void SeqStackDestroy(SeqStack* stack);
//3.打印
void SeqStackPrintChar(SeqStack* stack,char* msg);
//4.入栈
void SeqStackPush(SeqStack* stack,SeqStackType value);
//5.扩容
SeqStackType* SeqStackResize(SeqStack* stack);
//6.出栈
int SeqStackPop(SeqStack* stack);
//7.取栈顶元素
int SeqStackTop(SeqStack* stack,SeqStackType* value);
2.2 初始化
//思路:将有效元素个数size初始化为0,动态开辟内存的最大长度capacity设一个指定值1000,动态开辟data内存空间
void SeqStackInit(SeqStack* stack){
//非法输入
if(stack==NULL)
return;
//初始化有效元素个数为0
stack->size=0;
//初始化动态开辟内存的最大长度为1000
stack->capacity=1000;
//动态开辟空间
stack->data=(SeqStackType*)malloc((stack->capacity)*sizeof(SeqStackType));
}
2.3 销毁
//思路:将有效元素个数size置为0,动态开辟内存的最大长度capacity置为0,释放动态开辟的内存data
void SeqStackDestroy(SeqStack* stack){
//非法输入
if(stack==NULL)
return;
//将栈中有效元素个数置为0
stack->size=0;
//将最大长度置为0
stack->capacity=0;
//释放动态开辟的内存data
free(stack->data);
}
2.4 入栈
//思路:分为两种情况:1.初始化动态开辟的内存空间已满时需要扩容再入栈 2.初始化动态开辟的内存未满时,直接入栈
void SeqStackPush(SeqStack* stack,SeqStackType value){
//非法输入
if(stack==NULL)
return;
//判断栈是否已满
if(stack->size>=stack->capacity)
{
//扩容
stack->data=SeqStackResize(stack);
}
//入栈操作
stack->data[stack->size]=value;
stack->size++;
}
//扩容//思路:1.将stack->capacity设置的更大一点2.开辟新的空间3.将原有数据搬运到新的内存空间
SeqStackType* SeqStackResize(SeqStack* stack){
//非法输入
if(stack==NULL)
return NULL;
//判断是否已满
if(stack->size<stack->capacity)
return stack->data;
//已满时,进行扩容
stack->capacity=2*(stack->capacity)+1;
//开辟新的空间
SeqStackType* new_stack=(SeqStackType*)malloc((stack->capacity)*sizeof(SeqStackType));
//将原有数据进行搬运
size_t i=0;
for(i=0;i<stack->size;i++)
{
new_stack[i]=stack->data[i];
}
//释放旧内存空间
free(stack->data);
//返回新开辟的空间位置
return new_stack;
}
2.5 出栈
//思路:将有效元素个数减1即可
int SeqStackPop(SeqStack* stack){
//非法输入
if(stack==NULL)
return 0; //返回0表示出栈失败
//空栈时无法出栈
if(stack->size==0)
return 0;
stack->size--;
//出栈成功返回1
return 1;
}
2.6 取栈顶元素
//思路:让该函数返回两个有效信息:1.是否可以出栈,该参数作为返回参数2.出栈的元素,该参数作为输出参数返回
int SeqStackTop(SeqStack* stack,SeqStackType* value){
//非法输入
if(stack==NULL||value==NULL)
return 0; //返回0表示出栈失败
//空栈
if(stack->size==0)
return 0;
//取栈顶元素
*value=stack->data[stack->size-1];
return 1; //返回1表示出栈成功
}
2.7 测试代码是否正确
为了测试代码的方便需要写打印栈中元素的函数
//思路:进行遍历栈去打印,两个参数:1.用于接收栈的地址 2.用于打印一行字符串
void SeqStackPrintChar(SeqStack* stack,char* msg){
//非法输入
if(stack==NULL||msg==NULL)
return;
//打印字符串
printf("%s\n",msg);
//遍历打印栈中元素
size_t i=0;
for(i=0;i<stack->size;i++)
{
printf("%c ",stack->data[i]);
}
printf("\n");
}
//1.测试SeqStackInit
void Test_SeqStackInit(){
HEADER;
SeqStack stack;
SeqStackInit(&stack);
printf("stack.size expected 0,actual %d\n",stack.size);
printf("stack.capacity expected 1000,actual %d\n",stack.capacity);
}
//2.测试SeqStackDestroy
void Test_SeqStackDestroy(){
HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackDestroy(&stack);
printf("stack.size expected 0,actual %d\n",stack.size);
printf("stack.capacity expected 0,actual %d\n",stack.capacity);
}
//3.测试SeqStackPush
void Test_SeqStackPush(){
HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
SeqStackPrintChar(&stack,"入栈'a''b''c'");
}
//4.测试SeqStackPop
void Test_SeqStackPop(){
HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
int ret=SeqStackPop(&stack);
SeqStackPrintChar(&stack,"出栈一个元素");
printf("expected 1,actual %d\n",ret);
ret=SeqStackPop(&stack);
SeqStackPrintChar(&stack,"出栈两个元素");
printf("expected 1,actual %d\n",ret);
ret=SeqStackPop(&stack);
SeqStackPrintChar(&stack,"出栈三个元素");
printf("expected 1,actual %d\n",ret);
ret=SeqStackPop(&stack);
SeqStackPrintChar(&stack,"尝试对空栈出栈");
printf("expected 0,actual %d\n",ret);
}
//5.测试SeqStackTop
void Test_SeqStackTop(){
HEADER;
SeqStack stack;
SeqStackType value;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
size_t ret=SeqStackTop(&stack,&value);
SeqStackPrintChar(&stack,"取栈顶元素");
printf("expected 1,actual %d\n",ret);
printf("expected 'c',actual %c\n",value);
SeqStackPop(&stack);
ret=SeqStackTop(&stack,&value);
SeqStackPrintChar(&stack,"出栈一次后再取栈顶元素");
printf("expected 1,actual %d\n",ret);
printf("expected 'b',actual %c\n",value);
SeqStackPop(&stack);
SeqStackPop(&stack);
ret=SeqStackTop(&stack,&value);
SeqStackPrintChar(&stack,"出栈三次后尝试对空栈取栈顶元素");
printf("expected 0,actual %d\n",ret);
}
//主函数
int main(){
Test_SeqStackInit();
Test_SeqStackDestroy();
Test_SeqStackPush();
Test_SeqStackPop();
Test_SeqStackTop();
return 0;
}
3.基于链表实现栈的相关操作
3.1 头文件linkstack.h
//头文件只被编译一次
#pragma once
//宏定义一个标识符,用于打印函数名
#define HEADER printf("==============%s============\n",__FUNCTION__);
//为了方便用户修改其节点元素的数据类型,将其进行宏定义
#define LinkStackType char
//定义链表实现栈的结构体
typedef struct LinkStack
{
//存放链表元素
LinkStackType data;
//存放下一节点的地址
struct LinkStack* next;
}LinkStack;
3.2 初始化
//思路:将头指针的指向置为空即可
void LinkStackInit(LinkStack** pstack){
//非法输入
if(pstack==NULL)
return;
//修改*pstack的指向
*pstack=NULL;
}
3.3 销毁
//思路:遍历栈去销毁每一个节点
void LinkStackDestroy(LinkStack** pstack){
//非法输入
if(pstack==NULL)
return;
//空栈时,无需销毁
if(*pstack==NULL)
return;
//遍历栈
LinkStack* cur=*pstack;
while(cur!=NULL)
{
LinkStack* to_delete=cur;
cur=cur->next;
free(to_delete);
}
//将*pstack的指向置为空
*pstack=NULL;
}
3.4 入栈
//思路:头插实现入栈,新建节点并修改头指针的指向
void LinkStackPush(LinkStack** pstack,LinkStackType value){
//非法输入
if(pstack==NULL)
return;
//创建新节点
LinkStack* new_node=(LinkStack*)malloc(sizeof(LinkStack));
new_node->data=value;
new_node->next=NULL;
//修改*pstack的指向
new_node->next=*pstack;
*pstack=new_node;
}
3.5 出栈
//思路:头删实现出栈,第一个节点位置就是要出栈的节点,修改头指针的指向
int LinkStackPop(LinkStack** pstack){
//非法输入
if(pstack==NULL)
return 0; //返回0表示出栈失败
//空栈时,无法出栈
if(*pstack==NULL)
return 0;
//找到第一个节点
LinkStack* to_delete=*pstack;
//修改头指针的指向
*pstack=to_delete->next;
//销毁要出栈的节点
free(to_delete);
//出栈成功返回1
return 1;
}
3.6 取栈顶元素
//思路:由于是头插实现入栈,所以只需找到第一个节点即可,返回两个有效信息:1.取栈顶元素是否成功2.栈顶元素的值
int LinkStackTop(LinkStack* stack,LinkStackType* value){
//空栈时,取栈顶元素失败
if(stack==NULL)
return 0; //返回0表示取栈顶元素失败
//取栈顶元素
*value=stack->data;
//取栈顶元素成功返回1
return 1;
}
3.7 测试代码是否正确
//为了测试代码的方便需要写打印栈中元素的函数
//思路:遍历栈去打印每一个节点
void LinkStackPrintChar(LinkStack* stack){
//由于是头插实现入栈,所以需要逆序打印元素
if(stack==NULL)
return;
LinkStackPrintChar(stack->next);
printf("[%p][%c] ",stack,stack->data);
}
//1.测试LinkStackPush
void Test_LinkStackPush(){
HEADER;
LinkStack* stack;
LinkStackInit(&stack);
LinkStackPush(&stack,'a');
LinkStackPush(&stack,'b');
LinkStackPush(&stack,'c');
printf("入栈'a''b''c'\n");
LinkStackPrintChar(stack);
printf("\n");
}
//2.测试LinkStackPop
void Test_LinkStackPop(){
HEADER;
LinkStack* stack;
LinkStackInit(&stack);
LinkStackPush(&stack,'a');
LinkStackPush(&stack,'b');
LinkStackPush(&stack,'c');
printf("入栈'a''b''c'\n");
LinkStackPrintChar(stack);
printf("\n");
int ret=LinkStackPop(&stack);
printf("出栈一次\n");
printf("excepted 1,actual %d\n",ret);
LinkStackPrintChar(stack);
printf("\n");
LinkStackPop(&stack);
ret=LinkStackPop(&stack);
printf("出栈三次\n");
printf("excepted 1,actual %d\n",ret);
LinkStackPrintChar(stack);
printf("\n");
ret=LinkStackPop(&stack);
printf("尝试对空栈出栈\n");
printf("excepted 0,actual %d\n",ret);
LinkStackPrintChar(stack);
printf("\n");
}
//3.测试LinkStackTop
void Test_LinkStackTop()
{
HEADER;
LinkStack* stack;
LinkStackType value;
LinkStackInit(&stack);
LinkStackPush(&stack,'a');
LinkStackPush(&stack,'b');
LinkStackPush(&stack,'c');
int ret=LinkStackTop(stack,&value);
printf("ecpected 1,actual %d\n",ret);
printf("ecpected 'c',actual %c\n",value);
LinkStackPop(&stack);
ret=LinkStackTop(stack,&value);
printf("ecpected 1,actual %d\n",ret);
printf("ecpected 'b',actual %c\n",value);
LinkStackPop(&stack);
LinkStackPop(&stack);
ret=LinkStackTop(stack,&value);
printf("ecpected 0,actual %d\n",ret);
}
//主函数
int main(){
Test_LinkStackPush();
Test_LinkStackPop();
Test_LinkStackTop();
return 0;
}