数据结构-栈

一、了解栈

栈是一种线性数据结构,遵循“后进先出”(Last In, First Out,LIFO)的原则。也就是说,最后被压入栈中的元素最先被弹出。栈可以看作是一个只允许在一端进行插入和删除操作的集合。

栈的基本特性:

  1. 操作限制

    • 压栈(Push):将元素添加到栈顶。
    • 弹栈(Pop):从栈顶移除元素并返回该元素。
    • 查看栈顶元素(Peek/Top):返回栈顶元素但不移除它。
  2. 栈的状态

    • 空栈:没有任何元素。
    • 满栈:达到栈的最大容量(在使用数组实现时)。
  3. 应用场景

    • 函数调用(调用栈)
    • 表达式求值(如后缀表达式)
    • 括号匹配
    • 深度优先搜索(DFS)

总结

栈是一种简单而重要的数据结构,广泛应用于计算机科学和编程中,尤其是在管理函数调用、表达式求值和实现算法时。由于其特有的LIFO特性,栈能够有效地处理需要临时存储和后续访问的元素。

二、栈的顺序存储结构(C语言)

1. 栈的顺序存储结构

// 栈的顺序存储结构
#define MaxSize 50  // 定义一个常量 MaxSize,表示栈的最大容量为 50

// 定义一个顺序栈的结构体 SqStack
typedef struct {
	int data[MaxSize]; // 用一个数组 data 来存储栈中的元素,大小为 MaxSize
	int top;           // top 用于记录栈顶元素的索引,初始值通常为 -1 表示栈空
} SqStack; // SqStack 结构体类型的定义结束

2. 栈的初始化


// 初始化栈
void InitStack(SqStack* S) {
	S->top = -1; // 将栈顶指针初始化为 -1,表示栈为空
}

3. 判断栈为空


// 判断栈是否为空
bool StackEmpty(SqStack* S) {
	// 检查栈顶指针是否为 -1
	if (S->top == -1)
		return true; // 如果是,返回 true,表示栈为空
	else
		return false; // 否则返回 false,表示栈不为空
}

4. 进栈


// 进栈操作
bool Push(SqStack* S, int e) {
	// 检查栈是否已满
	if (S->top == MaxSize - 1)
		return false; // 如果满了,返回 false

	S->top++; // 栈顶指针增加
	S->data[S->top] = e; // 将元素 e 压入栈顶
	return true; // 返回 true,表示成功压入元素
}

5. 出栈


// 出栈操作
bool Pop(SqStack* S, int* e) {
	// 检查栈是否为空
	if (S->top == -1)
		return false; // 如果栈为空,返回 false

	*e = S->data[S->top]; // 将栈顶元素赋值给 e
	S->top--; // 栈顶指针减少,移除栈顶元素
	return true; // 返回 true,表示成功出栈
}

6. 读栈顶元素


// 读顶栈元素
bool GetTop(SqStack* S, int* e) {
	// 检查栈是否为空
	if (S->top == -1)
		return false; // 如果栈为空,返回 false

	*e = S->data[S->top]; // 将栈顶元素的值赋给 e
	return true; // 返回 true,表示成功获取栈顶元素
}

7. 销毁栈


// 销毁栈
void DestroyStack(SqStack* S) {
	S->top = -1; // 将栈顶指针设置为 -1,表示栈已被销毁
}

三、栈的链式存储结构(C语言)

1. 栈的链式存储结构


// 链表节点结构体定义
typedef struct LNode {
    int data;                // 节点存储的数据
    struct LNode* next;     // 指向下一个节点的指针
} LNode, * Linknode;          // Linknode 是指向 LNode 的指针类型

2. 栈的初始化


// 初始化链表(栈)-- 带头结点
bool InitStack(Linknode* LS) {
    // 为链表头节点分配内存
    (*LS) = (LNode*)malloc(sizeof(LNode));

    // 检查内存分配是否成功
    if ((*LS) == NULL) return false; // 如果分配失败,返回 false

    (*LS)->next = NULL; // 初始化头节点的 next 指针为 NULL
    return true;       // 返回 true,表示初始化成功
}

3. 判断栈为空

// 判断栈是否为空 -- 带头结点
bool StackEmpty(Linknode LS) {
    // 检查头结点是否为空
    // if (LS == NULL) return false; // 此行代码被注释掉,表示不考虑头结点为空的情况
    // 如果头结点的下一个节点为空,则栈为空
    if (LS->next == NULL) return true;
    else
        return false; // 否则栈不为空
}

4. 进栈


// 进栈
bool Push(Linknode* LS, int e) {
    // 检查头结点是否为空,若为空则无法进栈
    if (*LS == NULL) return false;

    // 创建一个新的节点
    LNode* p;
    p = (LNode*)malloc(sizeof(LNode)); // 为新节点分配内存
    // 检查内存分配是否成功
    if (p == NULL) return false;

    // 将新节点的下一个指针指向当前栈顶元素
    p->next = (*LS)->next;
    // 更新头结点的下一个指针,使其指向新节点
    (*LS)->next = p;
    // 将新节点的数据域赋值为进栈的元素
    p->data = e;

    return true; // 返回成功进栈的标志
}

5. 出栈


// 出栈
bool Pop(Linknode* LS, int* e) {
    // 检查栈是否为空,如果栈指针为空,返回false
    if ((*LS) == NULL) return false;

    // 检查栈是否只有一个元素(即栈顶元素),如果是,返回false
    if ((*LS)->next == NULL) return false;

    // 获取栈顶元素的指针
    LNode* p = (*LS)->next;

    // 将栈顶指针指向下一个元素
    (*LS)->next = p->next;

    // 将栈顶元素的值赋给e
    *e = p->data;

    // 释放栈顶元素的内存
    free(p);

    // 返回true,表示成功出栈
    return true;
}

6. 读栈顶元素


// 读栈顶元素
bool GetTop(Linknode* S, int* e) {
    // 检查栈是否为空,如果栈指针为空,返回false
    if ((*S) == NULL) return false;

    // 检查栈是否只有一个元素(即栈顶元素),如果是,返回false
    if ((*S)->next == NULL) return false;

    // 将栈顶元素的值赋给e
    *e = (*S)->next->data;

    // 返回true,表示成功获取栈顶元素
    return true;
}

7. 销毁栈


// 销毁栈
void DestroyStack(Linknode* S) {
    // 检查栈是否为空,如果栈指针为空,直接返回
    if ((*S) == NULL) return;

    LNode* p;

    // 循环释放栈中的所有元素
    while ((*S)->next != NULL) {
        p = (*S)->next;       // 获取当前栈顶元素的指针
        (*S)->next = p->next; // 将栈顶指针指向下一个元素
        free(p);             // 释放当前栈顶元素的内存
    }

    // 释放栈的头节点
    free(*S);

    // 将指针重置为 NULL,避免悬空指针
    *S = NULL;
}

四、总代码(C语言)

1. 顺序栈

#include <stdio.h>
#include <stdbool.h>

// 栈的顺序存储结构
#define MaxSize 50  // 定义一个常量 MaxSize,表示栈的最大容量为 50

// 定义一个顺序栈的结构体 SqStack
typedef struct {
	int data[MaxSize]; // 用一个数组 data 来存储栈中的元素,大小为 MaxSize
	int top;           // top 用于记录栈顶元素的索引,初始值通常为 -1 表示栈空
} SqStack; // SqStack 结构体类型的定义结束


// 初始化栈
void InitStack(SqStack* S) {
	S->top = -1; // 将栈顶指针初始化为 -1,表示栈为空
}

// 判断栈是否为空
bool StackEmpty(SqStack* S) {
	// 检查栈顶指针是否为 -1
	if (S->top == -1)
		return true; // 如果是,返回 true,表示栈为空
	else
		return false; // 否则返回 false,表示栈不为空
}

// 进栈操作
bool Push(SqStack* S, int e) {
	// 检查栈是否已满
	if (S->top == MaxSize - 1)
		return false; // 如果满了,返回 false

	S->top++; // 栈顶指针增加
	S->data[S->top] = e; // 将元素 e 压入栈顶
	return true; // 返回 true,表示成功压入元素
}


// 出栈操作
bool Pop(SqStack* S, int* e) {
	// 检查栈是否为空
	if (S->top == -1)
		return false; // 如果栈为空,返回 false

	*e = S->data[S->top]; // 将栈顶元素赋值给 e
	S->top--; // 栈顶指针减少,移除栈顶元素
	return true; // 返回 true,表示成功出栈
}


// 读顶栈元素
bool GetTop(SqStack* S, int* e) {
	// 检查栈是否为空
	if (S->top == -1)
		return false; // 如果栈为空,返回 false

	*e = S->data[S->top]; // 将栈顶元素的值赋给 e
	return true; // 返回 true,表示成功获取栈顶元素
}

// 销毁栈
void DestroyStack(SqStack* S) {
	S->top = -1; // 将栈顶指针设置为 -1,表示栈已被销毁
}



// 


int main() {
	return 0;
}

2. 链栈

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

// 链表节点结构体定义
typedef struct LNode {
    int data;                // 节点存储的数据
    struct LNode* next;     // 指向下一个节点的指针
} LNode, * Linknode;          // Linknode 是指向 LNode 的指针类型

// 初始化链表(栈)-- 带头结点
bool InitStack(Linknode* LS) {
    // 为链表头节点分配内存
    (*LS) = (LNode*)malloc(sizeof(LNode));

    // 检查内存分配是否成功
    if ((*LS) == NULL) return false; // 如果分配失败,返回 false

    (*LS)->next = NULL; // 初始化头节点的 next 指针为 NULL
    return true;       // 返回 true,表示初始化成功
}



// 判断栈是否为空 -- 带头结点
bool StackEmpty(Linknode LS) {
    // 检查头结点是否为空
    // if (LS == NULL) return false; // 此行代码被注释掉,表示不考虑头结点为空的情况
    // 如果头结点的下一个节点为空,则栈为空
    if (LS->next == NULL) return true;
    else
        return false; // 否则栈不为空
}




// 进栈
bool Push(Linknode* LS, int e) {
    // 检查头结点是否为空,若为空则无法进栈
    if (*LS == NULL) return false;

    // 创建一个新的节点
    LNode* p;
    p = (LNode*)malloc(sizeof(LNode)); // 为新节点分配内存
    // 检查内存分配是否成功
    if (p == NULL) return false;

    // 将新节点的下一个指针指向当前栈顶元素
    p->next = (*LS)->next;
    // 更新头结点的下一个指针,使其指向新节点
    (*LS)->next = p;
    // 将新节点的数据域赋值为进栈的元素
    p->data = e;

    return true; // 返回成功进栈的标志
}



// 出栈
bool Pop(Linknode* LS, int* e) {
    // 检查栈是否为空,如果栈指针为空,返回false
    if ((*LS) == NULL) return false;

    // 检查栈是否只有一个元素(即栈顶元素),如果是,返回false
    if ((*LS)->next == NULL) return false;

    // 获取栈顶元素的指针
    LNode* p = (*LS)->next;

    // 将栈顶指针指向下一个元素
    (*LS)->next = p->next;

    // 将栈顶元素的值赋给e
    *e = p->data;

    // 释放栈顶元素的内存
    free(p);

    // 返回true,表示成功出栈
    return true;
}

// 读栈顶元素
bool GetTop(Linknode* S, int* e) {
    // 检查栈是否为空,如果栈指针为空,返回false
    if ((*S) == NULL) return false;

    // 检查栈是否只有一个元素(即栈顶元素),如果是,返回false
    if ((*S)->next == NULL) return false;

    // 将栈顶元素的值赋给e
    *e = (*S)->next->data;

    // 返回true,表示成功获取栈顶元素
    return true;
}

// 销毁栈
void DestroyStack(Linknode* S) {
    // 检查栈是否为空,如果栈指针为空,直接返回
    if ((*S) == NULL) return;

    LNode* p;

    // 循环释放栈中的所有元素
    while ((*S)->next != NULL) {
        p = (*S)->next;       // 获取当前栈顶元素的指针
        (*S)->next = p->next; // 将栈顶指针指向下一个元素
        free(p);             // 释放当前栈顶元素的内存
    }

    // 释放栈的头节点
    free(*S);

    // 将指针重置为 NULL,避免悬空指针
    *S = NULL;
}



// 示例用法
int main() {
    Linknode stack;
    if (InitStack(&stack)) {
        printf("Stack initialized successfully.\n");
    }
    else {
        printf("Failed to initialize stack.\n");
    }

    // 记得释放内存
    free(stack);
    return 0;
}

五、总结

        栈是一种重要的线性数据结构,遵循“后进先出”(LIFO)的原则,具有压栈、弹栈和查看栈顶元素等基本操作。栈的应用广泛,包括函数调用的管理、表达式求值、括号匹配和深度优先搜索等。栈可以通过顺序存储结构(如数组)或链式存储结构(如链表)实现。顺序栈通过一个固定大小的数组来存储元素,使用一个索引记录栈顶位置,具有简单高效的特性,但容易出现栈满的情况。链式栈则通过动态分配内存的链表节点来实现,能够灵活地使用内存,避免栈满的情况,但在访问和管理节点时相对复杂。栈的基本操作包括初始化、判断是否为空、进栈、出栈、获取栈顶元素和销毁栈等。通过这些操作,栈能够有效地管理临时数据,支持多种算法和应用场景。总体而言,栈是计算机科学中不可或缺的基础数据结构之一。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0X78

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值