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

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

作者:互联网

文章目录


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

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

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

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

在这里插入图片描述


常见操作名称

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

压子弹入弹夹时的操作被称为进栈或者压栈(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;
}


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

我们下期再见!

标签:三分钟,int,top,元素,栈顶,element,SeqStack,细谈,数据结构
来源: https://blog.csdn.net/weixin_43776724/article/details/114017923