【细谈数据结构】小白都可以三分钟立马上手的“栈”讲解


今天要说的这个“栈”,可是数据结构界里有名的枪手!

诶~ 为什么会被称为枪手呢?

原来啊,栈的使用步骤,和手枪弹夹的使用步骤是一样的后进先出。所以大家就时常拿手枪弹夹的原理来解释它的原理。

下面这张动图为我们解释了,什么叫做后进先出,后压入的子弹最先打出,而最先压入的子弹最后打出。

在这里插入图片描述


常见操作名称

我们压入第一个子弹的地方被称为栈底,压入最后一颗子弹的地方被称为栈顶

压子弹入弹夹时的操作被称为进栈或者压栈(push)
在这里插入图片描述
而退出子弹的操作被称为出栈或者弹栈(pop)在这里插入图片描述

而不包含子弹的弹夹被称为空栈
在这里插入图片描述


栈的抽象数据类型

全文代码都使用C 语言实现

栈有两种表达形式,一种是使用数组来存放元素,另一种是使用链表来存放,也被称为链栈。

一、使用数组存放数据的栈结构

// SeqStack.h
typedef struct SeqStack{		// 栈的数据结构
    ElementType elements[MAX_SIZE];     // 用来存储栈中的数据,MAX_SIZE 为数组大小,按需求定义
    int top;                            // 栈顶下标,-1 时表示空栈
    int length;                         // 栈的元素个数
}SeqStack;
1.1 常用操作:
// SeqStack.h
#include <stdio.h>
#include <stdlib.h>
#include "Element.h"

typedef struct SeqStack{
    ElementType elements[MAX_SIZE];     // 用来存储栈中的数据
    int top;                            // 栈顶,-1 时表示空栈
    int length;                         // 栈的元素个数
}SeqStack;

/**  初始化栈 */
void InitSeqStack(SeqStack * S);

/**  压栈,返回压入结果(true/false) */
int PushSeqStack(SeqStack * S, ElementType element);

/** 出栈,用指针返回栈顶元素,返回出栈结果(ture/false) */
int PopSeqStack(SeqStack * S, ElementType * element);

/**  清空栈 */
void ClearSeqStack(SeqStack * S);

/**  判断栈是否为空,返回判断结果(ture/false) */
int stackEmpty(SeqStack * S);

/** 返回栈顶元素 */
void GetTopElement(SeqStack * S, ElementType * element);
1.2 具体实现:
// SeqStack.c
/**  初始化栈 */
void InitSeqStack(SeqStack * S){
    S->top = -1;		// top == -1 表示栈S 此时为空栈
    S->length = 0;		// 栈内元素为0 个
}

/**  压栈,返回压入结果(true/false) */
int PushSeqStack(SeqStack * S, ElementType element){
	// 判断栈内是否有空间给新元素使用
    if(S->top >= MAX_SIZE - 1){
        printf("栈满,压栈失败!\n");
        return FALSE;
    }
    // 先更新栈顶的下标
    S->top++;
    // 将元素放入栈顶
    S->elements[S->top] = element;
    // 栈的元素个数加一
    S->length++;
    return TRUE;
}

/** 出栈,用指针返回栈顶元素,返回出栈结果(ture/false) */
int PopSeqStack(SeqStack * S, ElementType * element){
	// 判断栈内是否有元素可以删除
    if(S->top == -1){
        printf("空栈,出栈失败!\n");
        return FALSE;
    }
    // 保存栈顶元素,*element 用来保存被删除的栈顶
    *element = S->elements[S->top];
    // 将栈顶元素删除,栈顶下标减一,元素个数减一
    S->top--;
    S->length--;

    return TRUE;
}

/**  清空栈 */
void ClearSeqStack(SeqStack * S){
    S->top = -1;	// 重新进行一次初始化操作就行
    S->length = 0;
}

/**  判断栈是否为空,返回判断结果(ture/false) */
int stackEmpty(SeqStack * S){
	// 若top == -1 表示空栈,返回是/TRUE,否则返回否/FALSE
    if(S->top == -1)
        return TRUE;
    else
        return FALSE;
}

/** 返回栈顶元素 */
void GetTopElement(SeqStack * S, ElementType * element){
	// 判断是否有栈顶元素,无则返回空
    if(S->top == -1){
        printf("空栈,返回失败!\n");
        element = NULL;
    }
    // 将栈顶元素赋值给返回元素*element
    *element = S->elements[S->top];
}


二、使用链表存放数据的链栈结构

链栈的使用相比于数组栈的使用稍微复杂一些,但是它的优点是不需要提前设定整个栈的空间大小,适用于元素个数不确定的情况。

// LinkedStack.h
typedef struct StackNode{		// 结点结构
    ElementType data;           // 结点中保存的元素内容
    struct StackNode * next;    // 指向下一个元素
}StackNode;

typedef struct LinkedStack{		// 栈的数据结构
    StackNode * top;            // 头结点,用来指向栈顶元素
    int length;                 // 栈中元素个数
}LinkedStack;
2.1 常用操作:
// LinkedStack.h
#include <stdio.h>
#include <stdlib.h>
#include "Element.h"

/** 初始化链栈 */
void InitLinkedStack(LinkedStack * S);

/** 压栈,返回压入结果(true/false) */
int PushLinkedStack(LinkedStack * S, ElementType element);

/** 出栈,用指针返回栈顶元素,返回出栈结果(ture/false) */
int PopLinkedStack(LinkedStack * S, ElementType * element);

/** 清空栈 */
void ClearLinkedStack(LinkedStack * S);

/** 销毁栈 */
void DestroyLinkedStack(LinkedStack * S);
2.2 具体实现:
#include "LinkStack.h"

/** 初始化链栈 */
void InitLinkedStack(LinkedStack * S){
    S->top = NULL;	// 栈顶元素指向空,相当于头结点指向空,表示空栈。
    S->length = 0;	// 栈内元素为0 个
}

/** 压栈,返回压入结果(true/false) */
int PushLinkedStack(LinkedStack * S, ElementType element){
	// 创建一个新结点,用来保存插入元素
    StackNode * newnode = (StackNode *)malloc(sizeof(StackNode));
    newnode->data = element;
    // 插入元素的下一个元素指向当前栈顶元素
    newnode->next = S->top;
    // 栈顶元素更新为插入元素
    S->top = newnode;
    // 栈内元素加一
    S->length++;
    // 返回操作成功
    return TRUE;
}

/** 出栈,用指针返回栈顶元素,返回出栈结果(ture/false) */
int PopLinkedStack(LinkedStack * S, ElementType * element){
	// 判断栈内是否有元素删除
    if(S->top == NULL){
        printf("空栈,无法进行删除操作!\n");
        return FALSE;
    }
	// 将栈顶元素保存到返回元素*element
    *element = S->top->data;
	// 新建一个临时结点保存待删除的栈顶元素,再通过释放临时结点达到删除操作
    StackNode * node = S->top;
    // 栈顶元素指向上一个插入的元素
    S->top = S->top->next;
    // 栈内元素个数减一
    S->length--;
    // 释放临时保存待删除的栈顶元素
    free(node);
	// 返回操作成功
    return TRUE;
}

/** 清空栈 */
void ClearLinkedStack(LinkedStack * S){
	// 创建一个临时结点,用来保存各个结点,再通过释放临时结点达到删除操作
    StackNode * tempNode;
    // 遍历整个栈,当S->top 指向NULL 时,整个栈删除完毕
    while(S->top){
    	// 保存当前栈顶元素
        tempNode = S->top;
        // 将栈顶元素指向上一个插入元素
        S->top = S->top->next;
        // 释放保存的栈顶元素
        free(tempNode);
        // 栈内元素个数减一
        S->length--;
    }
}

/** 销毁栈 */
void DestroyLinkedStack(LinkedStack * S){
	// 删除整个栈的元素
    ClearLinkedStack(S);
    // 再将栈给释放
    free(S);
    // 将栈指向NULL
    S = NULL;
}

三、其他文件以及测试

3.1 包含常数定义的头文件
//Element.h
#define MAX_SIZE 255
#define TRUE 1
#define FALSE 0

typedef struct{
    int id;
    char * name;
}ElementType;
3.2 测试文件

测试了第一种数组栈的使用方法

// main.c
#include <stdio.h>
#include <stdlib.h>
#include "SeqStack.h"

ElementType datas[] = {
    {1, "乔布斯"},
    {2, "比尔盖茨"},
    {3, "查理"},
    {4, "巴菲特"},
};

// 测试函数
void TestSeqStack();

int main()
{
    TestSeqStack();
    return 0;
}

void TestSeqStack(){
	// 创建栈并初始化
    SeqStack * S = (SeqStack *)malloc(sizeof(SeqStack));
    InitSeqStack(S);
    // 进栈
    for(int i = 0; i < 4; i++){
        printf("当前压栈的元素为:%d\t%s\n", datas[i].id, datas[i].name);
        PushSeqStack(S, datas[i]);
    }
	// 出栈
    ElementType * element = (ElementType *)malloc(sizeof(ElementType));
    PopSeqStack(S, element);
    printf("\n出栈操作:\n");
    printf("出栈元素为:%d\t%s\n", element->id, element->name);
    printf("当前栈内元素为:\n");
    for(int i = 0; i < S->length; i++){
        printf("%d\t%s\n", S->elements[i].id, S->elements[i].name);
    }
    // 释放栈
    free(S);
}
3.3 测试结果

在这里插入图片描述


以上就是本篇文章的所有内容了,如果觉得有帮助到你的话,
麻烦动动小手,点赞收藏转发!!!
你的每一次点赞都是我更新的最大动力~

我们下期再见!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值