目录
知识框架

栈
栈的基本概念
一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO ( Last In First Out )的原则。
栈的操作
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶 。出栈:栈的删除操作叫做出栈。 出数据也在栈顶 。

手撕栈
对于栈的实现我们是要用链表好还是数组好呢?
使用数组:
优点:数据连续存储在栈区,比堆区不连续存储要缓存友好,显然速度也更快;另外数组一般属于高级语言语法中的基本类型,结构简单,有利于编译器做寄存器优化、常量折叠、无用代码精简等,优化完速度还要更快。
缺点:数组大小在编译期写死 (C99 有了可变数组,但实际上 VC 也并未支持,C++ 亦未能兼容这一特性),数组开多大你就只能存多少数据,超了就是越界;另栈区空间宝贵,利用数组结构实现的栈能存的数据显然没有利用链表结构能存的那么多。所以注定了数组封装的栈根本就不能应用于工程实践。
使用链表:优点缺点恰好就是以上反过来说。
实际工程中的方案:C++ STL (可以说是算法 & 数据结构模板库中的标杆) 默认采用的是用双端队列来封装栈。双端队列底层由多块不连续的缓冲区组成,但各个缓冲区内部元素连续。这样可以减少内存分配次数,降低内存碎片,提高缓存命中率,可以说综合了数组和链表的优点。
我们这边使用的场景比较简单所以我们先用数组来实现栈,在后面也会提到如何使用队列来封装栈,由浅入深。
栈初始化
typedef int STDataType;
struct Stack
{
STDataType* p;
int top;
int capacity;
};
//初始化栈
void StackInit(Stack* ps)
{
ps->p = (STDataType*)malloc(sizeof(Stack));
ps->top = 0;
ps->capacity = 4;
}
插入
一进来我们先判断空间是否足够,如果不够那么我们需要开辟空间
// 入栈
void StackPush(Stack* ps, STDataType data)
{
if (ps->capacity == ps->top)
{
//增容
Stack* tmp = realloc(ps->p, sizeof(Stack) * ps->capacity * 2);
if (tmp == NULL)
{
perror("ERROR:");
exit(-1);
}
ps->p = tmp;
tmp = NULL;
ps->capacity *= 2;
}
ps->p[ps->top] = data;
ps->top++;
}
出栈
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
获取栈顶元素
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->p[ps->top - 1];
}
判断栈是否为空
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps)
{
return ps->top == 0;
//空返回1;非空返会0;
}
获取栈的有效元素个数
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
return ps->top;
}
销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->p);
ps->p = NULL;
ps->top = 0;
ps->capacity = 0;
}
完整代码
Stack.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int STDataType;
struct Stack
{
STDataType* p;
int top;
int capacity;
};
typedef struct Stack Stack;
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
Stack.c
#include "stack.h"
//初始化栈
void StackInit(Stack* ps)
{
ps->p = (STDataType*)malloc(sizeof(Stack));
ps->top = 0;
ps->capacity = 4;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{
if (ps->capacity == ps->top)
{
//增容
Stack* tmp = realloc(ps->p, sizeof(Stack) * ps->capacity * 2);
if (tmp == NULL)
{
perror("ERROR:");
exit(-1);
}
ps->p = tmp;
tmp = NULL;
ps->capacity *= 2;
}
ps->p[ps->top] = data;
ps->top++;
}
// 出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
r
本文详尽阐述了数据结构中的栈和队列。从栈的基本概念、操作,包括栈的初始化、插入、出栈等,到队列的概念、常见操作,如循环队列的实现和双端队列的概念,全方位解析了这两个重要的数据结构。
最低0.47元/天 解锁文章

1613

被折叠的 条评论
为什么被折叠?



