【数据结构】栈:数组/链表实现,C语言代码,从0开始 含思路详解

这次是P14-P16的笔记。

老样子,附视频课程地址👇

http://www.bilibili.com/video/BV1Fv4y1f7T1?p=11&vd_source=02dfd57080e8f31bc9c4a323c13dd49c

栈是什么?

随言碎语

很抱歉我并不能像解释链表为什么会出现那样来解释栈这种数据结构为什么会出现(ㄒoㄒ)我的感受就是这是个知识点,所以就学了。。。或许可能会说有一些类似栈这种原理的生活中的例子,但这并不能作为理由说服我qaq。虽然理解不了源头和他的前世今生,但还是要学习的hhh。

计算机存储的区域:栈区

在学习栈这种数据结构之前,我所知道的了解过一点关于栈的东西,便是计算机存储的四个区域中,有一个栈区嘛,这里大概回顾一下咯。

程序运行前出现:

代码区:存放函数体的二进制代码,所有可执行的代码。程序运行这段时间该区域数据不可被修改只可以被执行

全局区:存放全局变量、静态变量、常量。该区域数据 在程序结束后 由操作系统释放

程序运行后出现:

栈区:存放的是函数中的局部变量。函数调用返回后栈上的数据并没有被清空,而是程序失去了对栈所在内存的控制权。由编译器自动分配释放

堆区:利用new关键字开辟一块区域(地址)所以类型应该是指针 类似于c语言中的int*p = (int *)malloc(sizeof(int))。程序员 程序运行结束了 点❌ 堆区数据才被释放。若程序员不释放,则程序结束时由操作系统回收

数据结构:栈

先对它下个定义吧:栈是一个种特殊的线性表,遵循着先进后出(Last in First out)LIFO的原则。

也就是只能从表的一端进行各种数据的删除和插入操作这一端被称为栈顶,相对地,把另一端称为栈底。就像这样👇

关系 

取自bing:

"在计算机内存中,栈被用来存储函数调用时的临时数据,包括局部变量、函数参数、返回地址等。而在数据结构中,栈被用来解决各种问题,如表达式求解、深度优先搜索等。所以,虽然它们在具体应用上有所不同,但基本的数据结构和操作原理是一样的。"

对了,第一次忘记写了 。这里补充一下:关于栈的作用,有一个功能就是 撤回  功能,让我一下子倍感亲切哈哈

数组实现栈中插入、删除数据

思路+实现

准备工作

首先我们需要一个数组命名为A,名称随意,由于数组要提前确定数组的大小,但是为了我们 想修改数组大小的时候不用一个一个找整段代码中需要修改的地方,在C语言中我们可以定义一个名为MAX_SIZE的宏,这个宏定义的名字是随意的,只是这样命名更具有实际意义。

不过关于实现  “想修改数组大小的时候不用一个一个找整段代码中需要修改的地方”  这个功能的做法比如动态数组,c++中的vector容器、c语言中的malloc函数都可以实现。但这里采用静态数组的方法来讲解栈我觉得主要是因为方便初学理解,这使得在调试过程中更容易观察和理解程序的行为。

接着就是主要函数的书写啦

插入

其实用数组实现插入数据很简单,直接将下标往下指一位赋值就好了。只是在这里注意是否栈将要满了即可。即需要做个判断:我们将栈顶命名为top,由于我们赋值的时候是先将数组下标加一位,所以我们要判断的是当top==MAX_SIZE-1时就结束执行函数了,避免溢出。

这里注意不是top==MAX_SIZE。因为数组的索引是从0开始的。所以,如果一个数组的大小为MAX_SIZE,那么它的最后一个元素的索引就是MAX_SIZE - 1。

//入栈
void Push(int x)
{
    //数组最大限度
    if(top==MAX_SIZE-1)
    {
        printf("Error:stack overflow\n");
        return;
    }
    A[++top] = x;//先++再使用
}

删除

数组怎么删除一个元素?直接将执行的下标向前减一位,然后继续插入数据的时候就把之前“删除”的数据重新赋值覆盖过去了呀

插入的时候要考虑是否溢出,那么删除的时候自然是要考虑是否时空栈咯。

所以此时需要的判断应该是:top==-1,就不用执行啦直接return.

//出栈
void Pop()
{
    //栈为空
    if(top==-1)
    {
        printf("Error: No element to pop\n");
        return;
    }
    top--;
}

接着是打印函数咯

打印即要遍历栈,然鹅我们此时使用数组模拟栈,那也就是遍历数组即可。

void Print()
{
    int i;
    printf("Stack:");
    for(i=0;i<=top;i++)
    {
        printf("%d ",A[i]);
    }
    printf("\n");
}

买三赠一(bushi)hh

取自bing:

Top函数的作用是返回栈顶元素。它返回的是数组A中索引为top的元素,也就是最后入栈的元素。这个函数在你需要获取栈顶元素但不想将其出栈时非常有用。例如,你可能想查看栈顶元素以决定是否要添加或删除其他元素,但并不希望改变栈的状态。在这种情况下,你就可以使用Top函数

int Top()
{
    return A[top];
}

C语言代码 

#include<stdio.h>
#define MAX_SIZE 101
int A[MAX_SIZE];
int top=-1;

//入栈
void Push(int x)
{
    //数组最大限度
    if(top==MAX_SIZE-1)
    {
        printf("Error:stack overflow\n");
        return;
    }
    A[++top] = x;//先++再使用
}
//出栈
void Pop()
{
    //栈为空
    if(top==-1)
    {
        printf("Error: No element to pop\n");
        return;
    }
    top--;
}
int Top()
{
    return A[top];
}
void Print()
{
    int i;
    printf("Stack:");
    for(i=0;i<=top;i++)
    {
        printf("%d ",A[i]);
    }
    printf("\n");
}

int main()
{
    Push(2);
    Print();
    Push(5);
    Print();
    Push(10);
    Print();
    Pop();
    Print();
    Push(12);
    Print();
    return 0;
}

在数组实现的栈中,插入(也称为推入)和删除(也称为弹出)操作的时间复杂度都是O(1)。这是因为这些操作都在数组的末尾执行,不需要移动其他元素。

链表实现栈中插入、删除数据

思路

其实在前面数组的听课过程中我其实就在想我们在学习链表时就已经分析了数组和链表的差别,链表的好处。然后这里就有链表实现啦hh

由于栈只从一端插入或删除的特点,我们就要考虑是从链表头部还是链表尾部来插入呢?

由于链表尾部插入数据的时间复杂度为O(n),因为我们要从第一个节点遍历到最后一个节点进行操作,而链表头部插入数据时间复杂度为O(1)。所以链表的头部应当作为我们的栈顶top

所以主要的插入、删除函数都应该是从头部的插入和删除,具体的思路在之前链表的讲解中都解释过,是一样的,这里就不再细说啦。

需要注意的就是,在打印函数中,按照我们之前链表的打印函数来打印,按链表来说是正序的,但是由于我们把链表头部规定为栈顶,这样打印出来会是栈的反序的,那么如果我们想正序打印(即从栈底向栈顶的方向),就需要按照逆序打印链表的函数,需要使用递归。在前面也都讲过,忘记的可以看看哦。👇

http://blog.csdn.net/Swillow_/article/details/132123399?spm=1001.2014.3001.5502

代码如下

#include<stdio.h>
#include<stdlib.h>
struct Node{
    int data;
    struct Node* link;
};
struct Node* top=NULL;
//从头部插入:即入栈
void Push(int x)
{
    struct Node* temp=(struct Node*)malloc(sizeof(struct Node*));
    temp->data=x;
    temp->link=top;
    top=temp;
}
//从头部删除数据
void Pop()
{
    struct Node* temp;
    if(top==NULL)return;
    temp=top;
    top=top->link;
    free(temp);
}
//对于链表来说是正向打印,但是对于栈来说是逆序
void Print()
{
    printf("Stack:");
    struct Node* temp=top;
    while(temp!=NULL)
    {
        printf("%d ",temp->data);
        temp=temp->link;
    }
    printf("\n");
}
//通过递归正序打印
void Print(struct Node* temp)
{
    if(temp == NULL) //退出条件
    {
        return;
    }
    else
    {
        Print(temp->link); //递归调用
        printf("%d ", temp->data); //打印元素
    }
}

int main()
{
    Push(2);
    Print();
    //Print(top);
    Push(5);
    Print();
    //Print(top);
    Push(10);
    Print();
    //Print(top);
    Pop();
    Print();
    //Print(top);
    Push(12);
    Print();
    //Print(top);
    return 0;
}

ok啦。数组和链表实现栈都已经写完啦。(我要去上厕所了ㄒoㄒ

如果有问题欢迎指出,非常感谢!!

也欢迎交流建议哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值