深度理解C语言基础数据结构----栈(Stack)

引言

栈(Stack)是一种基础数据结构,广泛应用于计算机科学中的各个领域。栈遵循后进先出(LIFO,Last In First Out)原则,即最后进入栈的数据会最先被取出。在C语言编程中,栈不仅是操作系统和编译器的重要组成部分,也是解决许多编程问题的有力工具。

在这篇博客中,我们将详细介绍栈的基本概念、栈的代码实现、栈的优缺点、栈的应用场景,以及栈的实现逻辑。

栈的基本概念

栈是一种线性数据结构,它的特点是“后进先出”(LIFO),即在栈中的数据只能从栈顶插入和删除。栈的基本操作有两个:

压栈(Push):将一个元素添加到栈顶。
弹栈(Pop):移除栈顶元素并返回它。

栈的其他常见操作包括:
查看栈顶元素(Top):查看栈顶元素,但不移除它。
栈空检查(isEmpty):检查栈是否为空。
栈满检查(isFull):检查栈是否已满(数组实现时使用)。
栈的实现可以基于数组或链表。栈的操作是受限的,只能在栈顶进行,因此它属于受限访问的数据结构。

接下来我用一张图来帮助大家理解
在这里插入图片描述

栈的实现

基于数组实现栈

定义一个栈的数据类型

typedef  int  DataType;
typedef struct Stack
{
	DataType* a;//数组指针
	int top;//栈顶
	int capacity;//栈容量
}ST;

栈的初始化和销毁

void STInit(ST* pst)//栈的初始化
{
	assert(pst);
	pst->a = NULL;//栈顶指向第一个元素   
	//pst->a = 0;//栈顶指向第一个元素下一个元素
	pst->top = 0;
	pst->capacity= 0;
}

void STDestory(ST* pst)//栈的销毁:先释放,后置空
{
	assert(pst);
	free(pst);
	pst->a = NULL;
	pst->capacity = pst->top =0;
	
}

入栈和出栈

void STPush(ST* pst, DataType x)//入栈
{
	assert(pst);
	//考虑扩容
	if (pst->top == pst->capacity)
	{
		int Newcapacity = pst->capacity==0?4:2* pst->capacity;//需要扩多大的空间:运用三目操作符
		DataType* temp = (DataType*)realloc(pst->a, Newcapacity * sizeof(DataType));
		if (temp == NULL)
		{
			perror("realloc fail!");
			return;
		}
		pst->a = temp;
		pst->capacity = Newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}

void STPop(ST* pst)//出栈
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

判空,栈的大小,取栈顶元素

bool STEmpty(ST* pst)//判断栈是否为空
{
	assert(pst);
	return pst->top == 0;
}

size_t STSize(ST* pst)//取栈的大小
{
	return pst->top;
}

DataType STTop(ST* pst)//取栈顶元素
{
	return pst->a[pst->top - 1];
}
int main()
{
	ST s;
	STInit(&s);//对栈进行初始化
	STPush(&s, 1);//插入第一个元素1
	STPush(&s, 2);//插入第二个元素2
	STPush(&s, 3);//插入第二个元素3
	STPush(&s, 4);//插入第二个元素4
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", s.a[i]);//先打印一下
	}
	printf("\n打印栈顶元素:%d ", STTop(&s));//打印栈顶元素
	printf("\n栈的大小:%d ", STSize(&s));//栈的大小
	STPop(&s);//将栈顶元素也就是4给pop掉
	printf("\n打印栈顶元素:%d ", STTop(&s));//打印栈顶元素
	printf("\n栈的大小:%d ", STSize(&s));//栈的大小

	return 0;
}

在这里插入图片描述

基于链表实现

创建节点和栈结构体

// 定义链表节点
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 栈结构体
typedef struct {
    Node* top;  // 栈顶指针
} Stack;

初始化和判空

// 初始化栈
void initStack(Stack* stack) {
    stack->top = NULL;  // 栈初始化为空
}

// 判断栈是否为空
int isEmpty(Stack* stack) {
    return stack->top == NULL;
}

压栈,弹栈,获取栈顶元素

// 压栈操作
void push(Stack* stack, int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));  // 创建新节点
    if (!newNode) {
        printf("Memory allocation failed!\n");
        return;
    }
    newNode->data = value;
    newNode->next = stack->top;  // 新节点指向当前栈顶
    stack->top = newNode;        // 更新栈顶为新节点
    printf("Pushed %d\n", value);
}

// 弹栈操作
int pop(Stack* stack) {
    if (isEmpty(stack)) {
        printf("Stack underflow!\n");
        return -1;
    }
    Node* temp = stack->top;       // 保存栈顶节点
    int poppedValue = temp->data;  // 获取栈顶元素
    stack->top = stack->top->next; // 更新栈顶为下一个节点
    free(temp);                    // 释放原栈顶节点
    return poppedValue;
}

// 获取栈顶元素
int peek(Stack* stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty!\n");
        return -1;
    }
    return stack->top->data;
}
int main() {
    Stack stack;
    initStack(&stack);  // 初始化栈

    push(&stack, 10);  // 压入元素
    push(&stack, 20);
    push(&stack, 30);

    printf("Top element is %d\n", peek(&stack));  // 获取栈顶元素

    printf("Popped element: %d\n", pop(&stack));  // 弹出元素
    printf("Popped element: %d\n", pop(&stack));

    return 0;
}

在这里插入图片描述

栈的优缺点

优点
操作简单:栈的基本操作只有压栈和弹栈,非常简单且易于实现。
高效性:栈的插入和删除操作时间复杂度为O(1),非常高效。
动态扩展(链表实现):链表实现的栈可以动态扩展,不会受到预设大小的限制,适用于元素数量不确定的场合。
缺点
空间限制(数组实现):数组实现的栈需要预设最大容量,一旦超出容量会导致栈溢出。
访问限制:栈只允许从栈顶插入和删除数据,无法直接访问栈中的其他元素,不适合用于需要随机访问的数据存储场景。

栈的应用场景

栈在许多算法和系统中都有着重要的应用,以下是栈的几个典型应用场景:

函数调用管理:
在程序执行时,每当调用一个函数,系统会将该函数的局部变量、返回地址等信息压入栈中,函数执行完毕后,这些信息会从栈中弹出,恢复到调用函数的状态。这种机制被称为调用栈。
括号匹配:
在编译器中,栈被用于检查表达式中的括号是否匹配。通过将每个左括号压栈,当遇到右括号时弹栈,检查是否匹配。
表达式求值:
栈广泛应用于数学表达式的求值,特别是后缀表达式(逆波兰表示法)。通过栈操作,可以将操作数和运算符按正确顺序求值。
深度优先搜索(DFS):
栈在图的遍历算法中常被用于实现深度优先搜索(DFS),它用于存储当前路径的节点。

总结

栈是一种非常重要的基础数据结构,它的“后进先出”特性使得它在许多场景下都非常有用。在C语言中,栈可以通过数组或链表来实现,具体选择哪种方式取决于实际需求。栈的主要优点是操作简单、高效,但也有一些缺点,如空间的预设限制和只能从栈顶访问数据。

掌握栈的实现与应用,能够帮助程序员在实际编程中解决很多复杂问题。希望这篇文章能够帮助你更好地理解栈的概念和应用,如果你有任何问题,欢迎在评论区提问!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值