文章目录
本文中所涉及的完整代码及测试代码等已提交至gitee,可以点击此链接查看参考。
因为本人是编程初学者,文中及代码中难免出现错误,请同志们批评指正!
🎄栈的基本概念
上图是一个羽毛球筒,它只有一端开口,羽毛球在其中挨个存放,如果我们想往里面放入一只羽毛球,只能从开口处放进去,而不能随意插入。如果我们想拿出一只羽毛球,也只能从开口拿到最靠近开口的那一个,而不能越过第一个去随便拿中间的。其实这就是对栈这种数据结构的一个形象描述。
我们先对栈下一个结论:栈(stack)是仅允许在表尾进行插入和删除操作的线性表。
我们把允许插入和删除的一端称为栈顶(top), 另一端称为栈底(bottom), 不含任何数据元素的栈称为空栈。栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。
对于栈的定义如何理解呢?
首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。
它的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行。这也就使得栈底是固定的,最先进栈的只能在栈底。
栈的插入操作,叫作进栈,也称压栈、入栈。
栈的删除操作,叫作出栈,也有的叫作弹栈。
对于栈来讲,理论上线性表的操作特性它都具备,但是因为其结构的特殊性,所以插入删除这两个操作会与线性表有不同。但它到底还是一个线性表,因此根据存储结构,栈也分为顺序栈和链式栈,它们的基本思路和我们之前说的线性表和链表是几乎一样的。下面我们来看看顺序栈和链式栈的实现。
🎄顺序栈及其操作的实现
顺序表是用类似于数组的结构来实现的,顺序栈的实现也应该如此。观察上图,就是一个顺序栈的模型。
因为顺序栈中我们的栈的大小是动态的,所存储的元素个数也是动态的,因此我们需要一个变量top来代表栈顶,元素入栈则top+1,一个元素出栈则top-1。由此可以对比顺序表的结构定义初步构想出栈的结构定义:
typedef int data_t;
typedef struct
{
data_t* data;//栈内数据
int top;//栈顶位置下标,栈顶指针
int cap;//栈中能容纳元素数量
}sqstack, * pstack;
与其他数据结构相同,我们需要实现以下操作:
- 顺序栈的初始化;
- 顺序栈入栈操作;
- 顺序栈出栈操作;
- 判断顺序栈是否空;
- 判断顺序栈是否满;
- 遍历打印顺序栈中元素;
- 清空顺序栈中元素;
- 计算顺序栈元素个数;
- 动态内存释放;
⭐1.顺序栈的初始化
对于顺序栈的创建与初始化,我们需要考虑使用malloc来申请动态内存空间来存放我们的栈及栈中数据。因此应该需要两次malloc:
- malloc申请动态内存空间来管理维护栈结构;
- malloc申请动态内存空间来管理维护栈中数据;
- 对申请的空间中的内容进行初始化:数据置空、top置-1、cap置初始值。
为了实现空间的有效利用,这里在为栈的数据申请空间的时候,应该由用户来指定这个栈中能存放多少个data_t类型的数据,即cap得值由用户指定,用多少就申请多少的空间。
top为什么要置为-1呢?因为top是栈顶指针,是栈顶元素得下标,如果栈中有一个元素,那应该是在下标为0的位置。没有元素,那top自然要置为-1。
//1. 顺序栈的初始化;
/*
* @return init success:栈指针 init failed:NULL
* @para maxlen:栈内元素最大数量由用户指定
*/
pstack sqstack_init(int maxlen)
{
//1.给栈申请动态内存
pstack s = (pstack)malloc(sizeof(sqstack));
if (s == NULL)
{
printf("\nstack init failed\n");
return NULL;
}
//2.给栈中的数据data申请动态内存:
s->data = (data_t*)malloc(maxlen * sizeof(data_t));
if (s->data == NULL)
{
printf("\nS->data malloc failed\n");
free(s);
s = NULL;
return NULL;
}
//3.数据初始化:
memset(s->data, 0, maxlen * sizeof(data_t));//栈中数据置0
s->top = -1;
s->cap = maxlen;
return s;
}
⭐2.顺序栈入栈操作
入栈就是从栈顶向栈内存入一个元素,那么在存入之前应该先判断栈是不是存在,如果存在判断栈是不是满了,满了的话就不能再存入了。步骤如下:
- 判断栈是否存在;
- 判断栈是不是满了;
- 没满往里面存入数据:s->data[top] = val;
- 栈顶指针top+1;
//2. 顺序栈入栈操作;
/*
* @return -1:failed 0:success
* @para s:ptr to stack val:data to push
*/
int sqstack_push(pstack s, data_t val)
{
//1.判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
//2.判断栈满没满:
if (s->top == s->cap - 1)
{
printf("\nstack is full\n");
return -1;
}
//3.开始存:
s->top++;//栈顶指针先+1,腾出一个空地方存数据
s->data[s->top] = val;
return 0;
}
⭐3.顺序栈出栈操作
和入栈的操作相反,先给当前栈顶元素置0,清除掉当前栈顶元素。然后让top–,即让栈顶下降一格,说明栈中元素少了一个。在执行上述操作之前,依旧需要判断栈存不存在,也要判断栈空不空,空了就不能再出栈了。
//3. 顺序栈出栈操作;
/*
* @return -1:failed
* @para s:ptr to stack
*/
data_t sqstack_pop(pstack s)
{
//1.判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
//2.判断栈空不空:
if (s->top == -1)
{
printf("\nstack is empty\n");
return -1;
}
//弹出栈顶元素:
data_t tmp = s->data[s->top];
memset(&(s->data[s->top]), 0, sizeof(data_t));//清空
s->top--;
return tmp;//返回那个被弹出栈的元素
}
⭐4.判断顺序栈是否空
只需判断top的值即可。
//4. 判断顺序栈是否空;
int sqstack_is_empty(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
if (-1 == s->top)
{
printf("\nstack is empty\n");
}
return 0;
}
⭐5.判断顺序栈是否满
同样判断cap和top的关系:
//5. 判断顺序栈是否满;
int sqstack_is_full(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
if (s->cap - 1 == s->top)
{
printf("\nstack is full\n");
}
return 0;
}
⭐6.遍历打印顺序栈中元素
//6. 遍历打印顺序栈中元素;
/*
* @return void
* @para s:ptr to stack
*/
void sqstack_show(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return ;
}
//判断栈空不空:
if (-1 == s->top)
{
printf("\nstack is empty\n");
return;
}
for (int i = 0; i <= s->top; i++)
{
printf("%d ", s->data[i]);
}
return;
}
⭐7.清空顺序栈中元素
和初始化一样,将data全部置0,将top置为-1
//7. 清空顺序栈中元素;
int sqstack_clear(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
//判断栈空不空:
if (-1 == s->top)
{
printf("\nstack is empty\n");
return -1;
}
memset(s->data, 0, (s->top + 1) * sizeof(data_t));
s->top = -1;
return 0;
}
⭐8.计算顺序栈元素个数
返回top+1即可。
//8. 计算顺序栈元素个数;
int sqstack_len(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
return s->top + 1;
}
⭐9.动态内存释放
这里注意的是,先申请的先释放,注意malloc和free的配对。几个malloc就应该几个free。
//9. 动态内存释放;
int sqstack_free(pstack s)
{
//判断栈存不存在:
if (s == NULL)
{
printf("\nstack is not exist\n");
return -1;
}
if (s->data != NULL)
{
free(s->data);
s->data = NULL;
}
free(s);
s = NULL;
return 0;
}