目录
一:栈
1.栈的概念和结构
2.栈的实现
<1>.初始化栈
<2>.入栈
<3>.出栈
<4>:获取栈顶元素
<5>.获取栈中有效元素个数
<6>.销毁栈
<7>.示例
二:栈的完整代码
一:栈
1.栈的概念和结构
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
2.栈的实现
在实现栈的时候,可以通过建立数组和链表中选择:
在此处我们选择实现数组栈
//定义结构体,以及栈内的数据类型
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a; //数组形式
int _top; // 栈顶
int _capacity; // 容量
}Stack;
<1>.初始化栈
void StackInit(Stack* ps);
在初始化栈时,可以令数组为空,也可以为数组开辟一定的空间。
ps->_top = 0; 表示指向栈顶的下一个元素
ps->_pos = -1; 表示指向栈顶元素。
代码为:
// 初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_top = 0;//指向栈顶的下一个元素
//ps->_top = -1;//指向栈顶的元素
ps->_capacity = 0;
}
<2>.入栈
void StackPush(Stack* ps, STDataType data);
//画图分析得:
在数据入栈时会存在空间不够需要扩容的情况,在初始化时,我们的数组为NULL,并没有为其分配空间,需要先给数组一个新的容量(newcapacity),
//条件操作符
//exp1 ? exp2 : exp3
int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
//上述代码即:若ps->_capacity == 0 , 则给其一个4字节的空间,若不为0,则给其原容量的二倍。
在给数组一个适当的容量后,需要判断所给容是否够用,即需要使用realloc来调整数组的大小
插入的数据类型为 STDataType
//realloc
//void *realloc( void *memblock, size_t size );
// 要调整的内存地址 调整之后新的大小STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("malloc fail");
return;
}
//开辟成功
ps->_a = tmp;// 将 tmp 的空间赋给ps->_a
ps->_capacity = newcapacity;//数组的容量为新的容量
然后将数据插入到数组中
ps->_a[ps->_top] = data;//将数据插入
//入栈,栈顶会发生变化
ps->_top++;
代码为:
// 入栈
void StackPush(Stack* ps, STDataType data)
{
//入栈时,会存在空间不够需要扩容
if (ps->_top == ps->_capacity)
{
//条件操作符
//exp1 ? exp2 : exp3
//newcapacity 等于 如果ps->_capacity 为0,则给其一个4字节空间,若不为0,给其原容量的二倍
int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
//realloc
//void *realloc( void *memblock, size_t size );
// 要调整的内存地址 调整之后新的大小
STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("malloc fail");
return;
}
//开辟成功
ps->_a = tmp;
ps->_capacity = newcapacity;
}
ps->_a[ps->_top] = data;//将数据插入
ps->_top++;
}
<3>.出栈
void StackPop(Stack* ps);
//画图 分析得:
在入栈时,需要考虑栈内是否为空,在出栈时同样需要考虑,若栈内为空,则无法进行出栈操作,可以使用一个函数,来检测栈是否为空。
检测栈是否为空:
int StackEmpty(Stack* ps);
检测栈为空可以通过判断栈顶元素,在初始化时,我们可以知道ps->_top指向的是栈顶的下一个元素,若 ps->_top == 0 ; 则栈为空,即栈内没有任何元素。
代码为:
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
assert(ps);
//ps->_top --- 栈顶元素,即若 ps->_top 为0 ,则栈为空
return ps->_top == 0;
}
检测完栈是否为空后,可以直接对 ps->_top 进行 -- 操作(因为此处采用的数组栈,当不想要最后一个元素时,可以直接 -- 。)
代码为:
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
assert(ps);
//ps->_top --- 栈顶元素,即若 ps->_top 为0 ,则栈为空
return ps->_top == 0;
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
//若栈内为空,则无法出栈,需要判断栈内是否为空。
assert(!StackEmpty(ps));
//断言为真则继续
ps->_top--;
}
<4>:获取栈顶元素
获取栈顶元素的前提是,栈内有元素,若次此栈为空栈,则无栈顶元素 --- 即在此处仍需要检测栈是否为空(函数:int StackEmpty(Stack* ps);)
在初始化时,ps->_top 指向的栈顶的下一个元素,在获取栈顶元素时,可以使用 ps->_top-1.
代码为:
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
//栈顶元素存在 --- 栈不为空
assert(!StackEmpty(ps));
//ps->_top 指向的是栈顶的下一个元素
return ps->_a[ps->_top - 1];
}
<5>.获取栈中有效元素个数
在入栈时,我们进行了 ps->_top++ 操作
在出栈时,我们进行了 ps->_top-- 操作
初始化时,ps->_top 指向的是栈顶下一个元素
即:ps->_top 的值 等价于栈内的有效元素 (capacity 时容量,并非有效个数)
代码为:
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
//栈内有效元素元素个数 --- 在入栈操作中,每进入一个 ps->_top++
// 在出栈操作中,每出去一个 ps->_top--;
// 所以 --- ps->_top 等价于站内有效元素
return ps->_top;
}
<6>.销毁栈
我们建立的是数组栈,其空间是由 malloc 开辟来的,需要使用 free 释放,又因为是数组,所以只需要释放数组首元素。
然后将数组置为NULL,容量和栈顶都置为 0 。
代码为:
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);//数组是由 malloc 开辟而来的,需要使用 free 释放
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
<7>.示例
代码为:
#include"stack.h"
void teat()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
printf("%d\n", StackTop(&st));
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
while (!StackEmpty(&st))
{
printf("%d ", StackTop(&st));
StackPop(&st);
}
StackDestroy(&st);
}
int main()
{
teat();
return 0;
}
运行结果为:
二:栈的完整代码
Stack.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<errno.h>
#include<stdbool.h>
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a; //数组形式
int _top; // 栈顶
int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
Stack.c
#include"stack.h"
// 初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->_a = NULL;
ps->_top = 0;//指向栈顶的下一个元素
//ps->_top = -1;//指向栈顶的元素
ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{
//入栈时,会存在空间不够需要扩容
if (ps->_top == ps->_capacity)
{
//条件操作符
//exp1 ? exp2 : exp3
//newcapacity 等于 如果ps->_capacity 为0,则给其一个4字节空间,若不为0,给其原容量的二倍
int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
//realloc
//void *realloc( void *memblock, size_t size );
// 要调整的内存地址 调整之后新的大小
STDataType* tmp = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("malloc fail");
return;
}
//开辟成功
ps->_a = tmp;
ps->_capacity = newcapacity;
}
ps->_a[ps->_top] = data;//将数据插入
ps->_top++;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
assert(ps);
//ps->_top --- 栈顶元素,即若 ps->_top 为0 ,则栈为空
return ps->_top == 0;
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
//若栈内为空,则无法出栈,需要判断栈内是否为空。
assert(!StackEmpty(ps));
//断言为真则继续
ps->_top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
//栈顶元素存在 --- 栈不为空
assert(!StackEmpty(ps));
//ps->_top 指向的是栈顶的下一个元素
return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
//栈内有效元素元素个数 --- 在入栈操作中,每进入一个 ps->_top++
// 在出栈操作中,每出去一个 ps->_top--;
// 所以 --- ps->_top 等价于站内有效元素
return ps->_top;
}
// 销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->_a);//数组是由 malloc 开辟而来的,需要使用 free 释放
ps->_a = NULL;
ps->_capacity = 0;
ps->_top = 0;
}
test.c
#include"stack.h"
void teat()
{
Stack st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
printf("%d\n", StackTop(&st));
StackPush(&st, 3);
StackPush(&st, 4);
StackPush(&st, 5);
while (!StackEmpty(&st))
{
printf("%d ", StackTop(&st));
StackPop(&st);
}
StackDestroy(&st);
}
int main()
{
teat();
return 0;
}