结构-栈的实现

栈是一种先进后出的数据结构

栈(stack)是限定仅在表的一端进行操作的数据结构,请联系我们前文所学的,设想一个单链表我们只能够对其链表的表尾结点进行操作,而操作也只能够进行插入一个新的结点与删除最末尾的这个结点两个操作,而这样强限制性的‘链表’,就是我们所说的栈。

就像是小时候玩的玩具枪的弹夹,一颗颗小子弹就像是数据,先压进弹夹的子弹后出来,想要添加减少子弹,也只能在弹夹最上面的小开口对顶部的子弹操作。

一个思考

为什么要有栈,我明明可以用实现一个链表的方法来造一个和栈功能一样的东西?

答:用实现一个链表的方法来造一个和栈功能一样的东西,到最后这个造出来的东西就是栈。不信可以思考尝试如何用实现链表的方法实现一个栈(这个叫‘栈’的链表和正常链表相比是倒着的,功能是只在最后插入删除数据)。到最后就会发现——这结构,这操作,这代码,不和标准栈的实现一模一样吗。

为什么要有第二个结构体

我的猜想:如果只是存粹的一个data-next结点,当然也可以实现,但是在执行插入等等方法的时候,不可避免地会访问到原表各个部分的内容,这与栈“仅允许在表的一端进行操作”的目的不同。队列同理。看代码,增加了一个新的结构体之后,我们操作的只有新的栈顶和旧的栈顶,很符合定义~这个定义第二个结构体的操作在栈中似乎看不出多大作用,不弄我觉得也能实现栈,但在队列里作用比较明显,因为队列不可避免地要寻找最下面的结点,需要遍历,当然会访问到不该访问的数据。

作用

略,后面再补。

分类

栈分为数组栈和链表栈。链表栈又分为静态链表栈和动态链表栈。静态链表栈限定栈的大小,动态链表栈使用自动创建空间的方法创建。

实现

链表栈

结点

为了方便创建以及更好的实现后续功能,我们用两个结构体来参与栈的设计。

//栈的结点设计
//单个结点设计,数据和下一个指针
typedef struct node     //第一个结构体
{
    int data; 
    struct node *next;
} Node;
//第一个结构体的作用:和前面一样,储存数据,指向其他结点

//利用上面的结点创建栈,分为指向头结点的top指针和计数用的count
typedef struct stack    //第二个结构体
{
    Node *top;
    int count;
} Link_Stack;
//第二个结构体的作用:提供一个能够操作元素的指针(例如用a->top->data),提供一个能确定栈为空的方法(top指向空,或是count为0)。

操作---入栈

//入栈 push
Link_Stack *Push_stack(Link_Stack *p, int elem)
{
    if (p == NULL)
        return NULL;
    Node *temp;
    temp=(Node*)malloc(sizeof(Node));
    //temp = new Node;
    temp->data = elem;
    temp->next = p->top;
    p->top = temp;
    p->count++;
    return p;
}
//简述:
/*
创造一个新结点,存进去数据;
新结点的next指向原来的”表头“,也就是它下面的那个结点;
新结点成为新”表头“;
计数器增加。
*/

操作---出栈

//出栈 pop
Link_Stack *Pop_stack(Link_Stack *p)
{
    Node *temp;
    temp = p->top;
    if (p->top == NULL)
    {
        printf("错误:栈为空");
        return p;
    }
    else
    {
        p->top = p->top->next;
        free(temp);
        //delete temp;
        p->count--;
        return p;
    }
}
//该操作的结果是移除一个栈顶元素。
//简述步骤:
/*
判断是不是空栈;
将”表头“指向第二个结点;
释放第一个结点;
计数器减少;
*/

操作---遍历

//遍历栈:输出栈中所有元素
int show_stack(Link_Stack *p)
{
    Node *temp;
    temp = p->top;
    if (p->top == NULL)
    {
        printf("");
        printf("错误:栈为空");
        return 0;
    }
    while (temp != NULL)
    {
        printf("%d\t", temp->data);
        temp = temp->next;
    }
    printf("\n");
    return 0;
}
//先判断是否为空栈。

数组栈

数组栈是一种更为快速的模拟实现栈的方法,所谓模拟,就是不采用真实的链表设计,转而采用数组的方式进行“模拟操作”,这是一种仿真类型的操作,其可以快速的帮助我们构建代码,分析过程,相应的实现起来也更加的便捷

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxn 10000
 
//结点设计
typedef struct stack{
    int data[maxn];
    int top;
}stack;
 
//创建
stack *init(){
    stack *s=(stack *)malloc(sizeof(stack));
    if(s==NULL){
        printf("分配内存空间失败");
        exit(0);
    }
    memset(s->data,0,sizeof(s->data));
    //memset操作来自于库文件string.h,其表示将整个空间进行初始化
    //不理解可以查阅百度百科https://baike.baidu.com/item/memset/4747579?fr=aladdin
    s->top=0;     //栈的top和bottom均为0(表示为空)
    return s;
}
 
//入栈push
void push(stack *s,int data){
    s->data[s->top]=data;
    s->top++;
}
 
//出栈pop
void pop(stack *s){
    if(s->top!=0){
        s->data[s->top]=0;  //让其回归0模拟表示未初始化即可
        s->top--;
    }
}
 
//模拟打印栈中元素
void print_stack(stack *s){
    for(int n=s->top-1;n>=0;n--){
        printf("%d\t",s->data[n]);
    }
    printf("\n");   //习惯性换行
}
 
int main(){
    stack *s=init();
    int input[5]={11,22,33,44,55};  //模拟五个输入数据
    for(int i=0;i<5;i++){
        push(s,input[i]);
    }
    print_stack(s);
    /////////////
    pop(s);
    print_stack(s);
    return 0;
}
//从头到尾只分配过一次结构体的空间,这也是和链表栈不一样的一个地方。
//链表栈的数据都存在于每个结点的data中,而数组栈的数据都存在第一个分配好空间的那个结构体的数组中。
//top看来是用来记录栈元素个数的。
posted @ 2021-08-28 08:22  hyy233  阅读(83)  评论(0)    收藏  举报