文章目录
前言
数据结构的常见线性表,分别是顺序表,链表,栈
,队列
本篇给大家带来利用顺序表实现栈和讲解
1. 栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
- 出栈:栈的删除操作叫做出栈。出数据也在栈顶。
2. 栈的实现设计
由于栈的特性,后进先出所以入栈和出栈都是一端,我们可以用顺序表
或链表
实现,这个我推荐用顺序表实现
- 顺序表:插入和删除都是一端,顺序表尾插和尾删很符合,同时相比链表的缓存命中率和利用率更高。
- 链表:可以利用头插和头删实现或者利用尾插或尾插就必须要设计成双向循环,但是缓存命中和利用率低。
顺序表
链表
3. 栈的功能实现
首先我们看下结构类型的定义和实现功能
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a; // 数组地址
int top; // 数据个数
int capacity; // 容量
}Stack;
// 初始化
void StackInit(Stack* ps);
// 栈为空判断
bool StackEmpty(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType x);
// 出栈
void StackPop(Stack* ps);
// 取栈顶元素
STDataType StackTop(Stack* ps);
// 栈的大小
int StackSize(Stack* ps);
// 销毁
void StackDestroy(Stack* ps);
3.1. 初始化
一般结构体里面的成员形成一个整体结构,都是需要初始化,如:顺序表里面的容量和大小还有数据地址组成了顺序表,单链表就不用初始化,单链表是由多个节点组成。
// 初始化
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
3.2. 判断栈是否为空
为了方便和直观,我们后面对函数需要对栈是否为判断,如:插入,取栈顶元素等
这里先把这个功能实现出来。
判断top数据个数是否为0,为空就返回
true
,未假返回false
// 栈为空判断
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == 0;
}
3.3. 入栈
用顺序表实现的栈,所以肯定有两个版本,1.静态,2.动态,这里我在顺序表的文章说明过推荐使用动态,所以肯定就需要扩容。
这里有两种写法,关系到初始化的设计,取决于自己的习惯和喜好。
- 初始化
top = 0
,就是先插入数据,在top在++ - 初始化
top = -1
,top先++,在插入数据
我自己习惯1种,但是要注意你选这哪种,其他函数与top相关也要做出修改,如出栈、取栈顶数据等等。
// 入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
// 栈容量检查
if (ps->top == ps->capacity)
{
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
assert(tmp);
ps->a = tmp;
ps->capacity = newCapacity;
}
// 插入数据
ps->a[ps->top] = x;
ps->top++;
}
3.4. 出栈
空栈不能出,刚才定义的判断栈是否为空就起作用了,直接调用即可,出栈在栈顶的位置出
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
// 空栈不能出
assert(!StackEmpty(ps));
ps->top--;
}
3.5. 获取栈顶元素
空栈不能取,我初始化top为0,我是先插入在++,意味着我取栈顶元素的下标是top - 1
// 取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
// 空栈不能出
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
3.6. 栈的大小
// 栈的大小
int StackSize(Stack* ps)
{
assert(ps);
return ps->top;
}
3.7. 销毁
// 销毁
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
4. 功能测试
栈是不允许遍历,只有栈顶一端的数据可以被使用,所以要把栈顶的数据取了,才能取下一个,要实现打印数据,需要一个一个取数据,在出栈,栈为空结束
5. 总结
顺序表学完了,在实现栈比较简单,只不过是顺序表的变形而已,主要理解和熟悉栈的特性(后进先出),栈相关OJ题主要对其特性方面出题。