热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构之_栈的顺序存储和链式存储的代码实现

数据结构之_栈的顺序存储和链式存储的代码实现,Go语言社区,Golang程序员人脉社


数据结构之_栈的顺序存储和链式存储的代码实现

1.栈的基本概念


  • 概念:

    首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。


  • 特性

    它的特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。


  • 操作
    •   栈的插入操作,叫做进栈,也成压栈。类似子弹入弹夹(如下图所示)

    •   栈的删除操作,叫做出栈,也有的叫做弾栈,退栈。如同弹夹中的子弹出夹(如下图所示)


 



    •   创建栈

    •   销毁栈

    •   清空栈

    •   进栈

    •   出栈

    •   获取栈顶元素

    •   获取栈的大小 


栈的抽象数据类型


ADT 栈(stack)
Data
通线性表。元素具有相同的类型,相邻的元素具有前驱和后继关系。
Operation
// 初始化,建立一个空栈S
InitStack(*S);
// 若栈存在,则销毁它
DestroyStack(*S);
// 将栈清空
ClearStack(*S);
// 若栈为空则返回true,否则返回false
StackEmpty(S);
// 若栈存在且非空,用e返回S的栈顶元素
GetTop(S,*e);
// 若栈S存在,插入新元素e到栈S中并成为其栈顶元素
Push(*S,e);
// 删除栈S中的栈顶元素,并用e返回其值
Pop(*S, *e);
// 返回栈S的元素个数
StackLength(S);
endADT


2.栈的顺序存储代码实现


  • 基本概念

    栈的顺序存储结构简称顺序栈,它是运算受限制的顺序表。顺序栈的存储结构是:利用一组地址连续的的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top只是栈顶元素在顺序表中的位置。


  • 设计与实现

    因为栈是一种特殊的线性表,所以栈的顺序存储可以通过顺序线性表来实现。

SeqStack.h  


#ifndef SEQSTACK_H
#define SEQSTACK_H
#include
#include
//数组去模拟栈的顺序存储
#define MAX_SIZE 1024
#define SEQSTACK_TRUE 1
#define SEQSTACK_FALSE 0
typedef struct SEQSTACK {
void* data[MAX_SIZE];
int size;
}SeqStack;
//初始化栈
SeqStack* Init_SeqStack();
//入栈
void Push_SeqStack(SeqStack* stack, void* data);
//返回栈顶元素
void* Top_SeqStack(SeqStack* stack);
//出栈
void Pop_SeqStack(SeqStack* stack);
//判断是否为空
int IsEmpty(SeqStack* stack);
//返回栈中元素的个数
int Size_SeqStack(SeqStack* stack);
//清空栈
void Clear_SeqStack(SeqStack* stack);
//销毁
void FreeSpace_SeqStack(SeqStack* stack);
#endif

SeqStack.c  


#include"SeqStack.h"
//初始化栈
SeqStack* Init_SeqStack() {
SeqStack* stack = (SeqStack*)malloc(sizeof(SeqStack));
for (int i = 0; i stack->data[i] = NULL;
}
stack->size = 0;
return stack;
}
//入栈
void Push_SeqStack(SeqStack* stack, void* data) {
if (stack == NULL) {
return;
}
if (stack->size == MAX_SIZE) {
return;
}
if (data == NULL) {
return;
}
stack->data[stack->size] = data;
stack->size++;
}
//返回栈顶元素
void* Top_SeqStack(SeqStack* stack) {
if (stack == NULL) {
return NULL;
}
if (stack->size == 0) {
return NULL;
}
return stack->data[stack->size - 1];
}
//出栈
void Pop_SeqStack(SeqStack* stack) {
if (stack == NULL) {
return;
}
if (stack->size == 0) {
return;
}
stack->data[stack->size - 1] = NULL;
stack->size--;
}
//判断是否为空
int IsEmpty(SeqStack* stack) {
if (stack == NULL) {
return -1;
}
if (stack->size == 0) {
return SEQSTACK_TRUE;
}
return SEQSTACK_FALSE;
}
//返回栈中元素的个数
int Size_SeqStack(SeqStack* stack) {
return stack->size;
}
//清空栈
void Clear_SeqStack(SeqStack* stack) {
if (stack == NULL) {
return;
}
for (int i = 0; i size; i++) {
stack->data[i] = NULL;
}
stack->size = 0;
}
//销毁
void FreeSpace_SeqStack(SeqStack* stack) {
if (stack == NULL) {
return;
}
free(stack);
}

栈的顺序存储.c  


#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include "SeqStack.h"
typedef struct PERSON {
char name[64];
int age;
}Person;
int main(void) {
//创建栈
SeqStack* stack = Init_SeqStack();
//创建数据
Person p1 = { "aaa", 10 };
Person p2 = { "bbb", 20 };
Person p3 = { "ccc", 30 };
Person p4 = { "ddd", 40 };
Person p5 = { "eee", 50 };
//入栈
Push_SeqStack(stack, &p1);
Push_SeqStack(stack, &p2);
Push_SeqStack(stack, &p3);
Push_SeqStack(stack, &p4);
Push_SeqStack(stack, &p5);
//输出
while (Size_SeqStack(stack) > 0) {
//访问栈顶元素
Person* person = (Person*)Top_SeqStack(stack);
printf("Name:%s Age:%dn", person->name, person->age);
//弹出栈顶元素
Pop_SeqStack(stack);
}
//释放内存
FreeSpace_SeqStack(stack);
system("pause");
return 0;
}


3.栈的链式存储  


  • 基本概念

    栈的链式存储结构简称链栈。

    思考如下问题

         栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?

    答:由于单链表有头指针,而栈顶指针也是必须的,那干嘛不让他俩合二为一呢,所以比较好的办法就是把栈顶放在单链表的头部。另外都已经有了栈顶在头部了,单链表中比较常用的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。


  • 设计与实现

    链栈是一种特殊的线性表,链栈可以通过链式线性表来实现。

 LinkStack.h


#ifndef LINKSTACK_H
#define LINKSTACK_H
#include
#include
//链式栈的结点
typedef struct LINKNODE {
struct LINKNODE* next;
}LinkNode;
//链式栈
typedef struct LINKSTACK {
LinkNode head;
int size;
}LinkStack;
//初始化函数
LinkStack* Init_LinkStack();
//入栈
void Push_LinkStack(LinkStack* stack, LinkNode* data);
//出栈
void Pop_LinkStack(LinkStack* stack);
//返回栈顶元素
LinkNode* Top_LinkStack(LinkStack* stack);
//返回栈元素的个数
int Size_LinkStack(LinkStack* stack);
//清空栈
void Clear_LinkStack(LinkStack* stack);
//销毁栈
void FreeSpace_LinkStack(LinkStack* stack);
#endif

LinkStack.c  


#include"LinkStack.h"
//初始化函数
LinkStack* Init_LinkStack() {
LinkStack* stack = (LinkStack*)malloc(sizeof(LinkStack));
stack->head.next = NULL;
stack->size = 0;
return stack;
}
//入栈
void Push_LinkStack(LinkStack* stack, LinkNode* data) {
if (stack == NULL) {
return;
}
if (data == NULL) {
return;
}
data->next = stack->head.next;
stack->head.next = data;
stack->size++;
}
//出栈
void Pop_LinkStack(LinkStack* stack) {
if (stack == NULL) {
return;
}
if (stack->size == 0) {
return;
}
//第一个有效结点
LinkNode* pNext = stack->head.next;
stack->head.next = pNext->next;
stack->size--;
}
//返回栈顶元素
LinkNode* Top_LinkStack(LinkStack* stack) {
if (stack == NULL) {
return NULL;
}
if (stack->size == 0) {
return NULL;
}
return stack->head.next;
}
//返回栈元素的个数
int Size_LinkStack(LinkStack* stack) {
if (stack == NULL) {
return -1;
}
return stack->size;
}
//清空栈
void Clear_LinkStack(LinkStack* stack) {
if (stack == NULL) {
return;
}
stack->head.next = NULL;
stack->size = 0;
}
//销毁栈
void FreeSpace_LinkStack(LinkStack* stack) {
if (stack == NULL) {
return;
}
free(stack);
}

栈的链式存储.c  


#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include "LinkStack.h"
typedef struct PERSON {
LinkNode node;
char name[64];
int age;
}Person;
int main(void) {
//创建栈
LinkStack* stack = Init_LinkStack();
//创建数据
Person p1, p2, p3, p4, p5;
strcpy(p1.name, "aaa");
strcpy(p2.name, "bbb");
strcpy(p3.name, "ccc");
strcpy(p4.name, "ddd");
strcpy(p5.name, "eee");
p1.age = 10;
p2.age = 20;
p3.age = 30;
p4.age = 40;
p5.age = 50;
//入栈
Push_LinkStack(stack, (LinkNode*)& p1);
Push_LinkStack(stack, (LinkNode*)& p2);
Push_LinkStack(stack, (LinkNode*)& p3);
Push_LinkStack(stack, (LinkNode*)& p4);
Push_LinkStack(stack, (LinkNode*)& p5);
//输出
while (Size_LinkStack(stack) > 0) {
//取出栈顶元素
Person* p = (Person*)Top_LinkStack(stack);
printf("Name:%s Age:%dn", p->name, p->age);
//弹出栈顶元素
Pop_LinkStack(stack);
}
//销毁栈
FreeSpace_LinkStack(stack);
system("pause");
return 0;
}


4.栈的应用(案例)


4.1案例1: 就近匹配

几乎所有的编译器都具有检测括号是否匹配的能力,那么如何实现编译器中的符号成对检测?如下字符串:


#include int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;}


  • 算法思路
    •  从第一个字符开始扫描

    •  当遇见普通字符时忽略,

    •  当遇见左符号时压入栈中

    •  当遇见右符号时从栈中弹出栈顶符号,并进行匹配

    •  匹配成功:继续读入下一个字符

    •  匹配失败:立即停止,并报错

    •  结束:

    •  成功: 所有字符扫描完毕,且栈为空

    •  失败:匹配失败或所有字符扫描完毕但栈非空


  • 总结
    •  当需要检测成对出现但又互不相邻的事物时可以使用栈“后进先出”的特性

    •  栈非常适合于需要“就近匹配”的场合


案例代码:


#include"LinkStack.h"
//案例一 就近匹配
void test02(){
char* str = "#include int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;}";
//初始化栈
LinkStack* lstack = InitLinkStack();
//匹配括号
char* pCurrent = str;
while (*pCurrent != ''){
if (*pCurrent == '('){
PushLinkStack(lstack, pCurrent);
}
else if (*pCurrent == ')'){
char* p = (char*)TopLinkStack(lstack);
if (*p == '('){
PopLinkStack(lstack);
}
}
pCurrent++;
}
if (GetLengthLinkStack(lstack) > 0){
printf("匹配失败!n");
}
//销毁栈
DestroyLinkStack(lstack);
}
int main(){
test02();
system("pause");
return EXIT_SUCCESS;


4.2案例2:中缀表达式和后缀表达式


  • l  后缀表达式(由波兰科学家在20世纪50年代提出)
    •  将运算符放在数字后面 ===》 符合计算机运算

    •  我们习惯的数学表达式叫做中缀表达式===》符合人类思考习惯


  • l  实例
    •  5 + 4 => 5 4 + 

    •  1 + 2 * 3 => 1 2 3 * + 

    •  8 + ( 3 – 1 ) * 5 => 8 3 1 – 5 * +


  • l  中缀转后缀算法:

  遍历中缀表达式中的数字和符号:



    •  对于数字:直接输出

    •   对于符号:
      •    左括号:进栈 

      •    运算符号:与栈顶符号进行优先级比较


    •   若栈顶符号优先级低:此符号进栈 (默认栈顶若是左括号,左括号优先级最低)



    •   若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈


  • l  右括号:将栈顶符号弹出并输出,直到匹配左括号

  遍历结束:将栈中的所有符号弹出并输出


  • l  中缀转后缀伪代码 priority

transform(exp)
{
创建栈S;
i= 0;
while(exp[i] != ‘’)
{
if(exp[i] 为数字)
{
Output(exp[i]);
}
else if(exp[i] 为符号)
{
while(exp[i]优先级 <= 栈顶符号优先级)
{
output(栈顶符号);
Pop(S);
}
Push(S, exp[i]);
}
else if(exp[i] 为左括号)
{
Push(S, exp[i]);
}
else if(exp[i] 为右括号)
{
while(栈顶符号不为左括号)
{
output(栈顶符号);
Pop(S);
}
从S中弹出左括号;
}
else
{
报错,停止循环;
}
i++;
}
while(size(S) > 0 && exp[i] == ‘’)
{
output(栈顶符号);
Pop(S);
}
}


  • l  动手练习

  将我们喜欢的读的中缀表达式转换成计算机喜欢的后缀表达式

  中缀表达式: 8 + ( 3 – 1 ) * 5

       后缀表达式:  8 3 1 – 5 * +


4.3案例3:计算机如何基于后缀表达式计算


  • l  思考

    计算机是如何基于后缀表达式计算的?

    例如:8 3 1 – 5 * +


  • l  计算规则

    遍历后缀表达式中的数字和符号



    •   对于数字:进栈

    •   对于符号:
      •    从栈中弹出右操作数

      •    从栈中弹出左操作数

      •    根据符号进行运算

      •    将运算结果压入栈中



    遍历结束:栈中的唯一数字为计算结果


  • l  代码实现(伪代码) express

compute(exp)
{
创建栈;
int i = 0;
while( exp[i] != ‘’)
{
if(exp[i]为数字)
{
Push(S, exp[i]);
}
else if(exp[i]为符号)
{
1. 从栈顶弹出右操作数;
2. 从栈中弹出左操作数;
3. 根据符号进行运算;
4. Push(stack, 结果);
}
else
{
报错,停止循环;
}
i++;
}
if( Size(s) == 1 && exp[i] == ‘’)
{
栈中唯一的数字为运算结果;
}
返回结果;
}

  


转载于:https://www.cnblogs.com/wanghui1234/p/11315567.html



推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • NotSupportedException无法将类型“System.DateTime”强制转换为类型“System.Object”
    本文介绍了在使用LINQ to Entities时出现的NotSupportedException异常,该异常是由于无法将类型“System.DateTime”强制转换为类型“System.Object”所导致的。同时还介绍了相关的错误信息和解决方法。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
author-avatar
mobiledu2502873611
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有