【学完线性表不学栈和队列是真的可惜】栈和队列实质性操作

简介: 【学完线性表不学栈和队列是真的可惜】栈和队列实质性操作

在这里插入图片描述

文章目录

🔒栈区

  1. 🪐栈的接口函数

🔒队列区

  1. 🪐队列的接口函数

👉小结

🎬功能动画演示区

🔒源文件区




🔑栈区

👉什么是栈

栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

image.png

🌟重点:后进先出

栈就好比叠书本,第一本书叠到最低,后续把书叠在第一本书的上面,最后叠的那一本是最容易拿的,而第一本书只能把上面叠的书拿完才能拿

🪐栈的接口函数

👉栈的建立与初始化

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小

链表形式(比较麻烦,不建议使用)

image.png

我们栈的实现比较优的做法都是顺序表实现

image.png

栈的建立有两种方式:一种是静态栈,容量固定,不建议使用。另一种是动态栈,容量可以动态变化,比较灵活(本文使用的方式)

不管是动态栈还是静态栈,都需要一个变量top用来指向栈顶

两种栈的实现方式如下

//静态栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;


//动态栈!!!!!
typedef int STDataType;  //方便后续可能要存储其他类型的数据

typedef struct Stack
{
    STDataType* a;  //用来指向顺序表
    int  top; //栈顶
    int  capacity; //容量
}Stack;

初始化栈:把指向顺序表的指针置空,把栈顶和容量值零

void StackInit(Stack* ps)
{
    ps->a = NULL;
    ps->capacity = ps->top = 0;
}

👉入栈与出栈

入栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

image.png

入栈前的工作就是判断是否有容量可以存放,没有就增容(realloc这个函数可以增容,也可以像malloc函数一种开辟空间,之前的顺序表有详解)

而且要注意的就是入栈只能是从尾入

void StackPush(Stack* ps, STDataType x)
{
    assert(ps);  //判空,ps不能为空,不然下面的操作会导致程序崩溃
    if (ps->capacity == ps->top)
    {
        //如果栈的容量为零,就赋值4个空间给它,如果不为零就2倍扩容
        int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        Stack* tem = (Stack*)realloc(ps->a, sizeof(STDataType) * newCapacity);
        if (tem == NULL)
        {
            printf("realloc fail\n");
            exit(-1);
        }
        ps->a = tem;
        ps->capacity = newCapacity;
    }
    ps->a[ps->top] = x;
    ps->top++;
}

出栈就跟顺序表尾删一样,直接把top下标减一即可

要注意的是要出栈要判断栈中是否有数据可出

void StackPop(Stack* ps)
{
    assert(ps);
    //栈不为空才能出栈
    assert(ps->top > 0);
    
    ps->top--;
}

👉获取栈顶元素

获取之前也是需要判断栈是否有数据

有数据就直接返回栈顶数据即可

STDataType StackTop(Stack* ps)
{
    assert(ps->top > 0);
    return ps->a[ps->top - 1];
}

image.png

👉获取栈的有效个数

栈的有效个数就是top的下标,直接返回top即可。

int StackSize(Stack* ps)
{
    return ps->top;
}

👉检测栈是否为空

判断栈是否为空,其实就是判断有效个数top是否为零

要注意返回要求:检测栈是否为空,如果为空返回非零结果,如果不为空返回0

版本1.0的判断方式可以直接由版本2.0的一句表达式搞定,如果不理解就版本1.0
//版本1.0
int StackEmpty(Stack* ps)
{
    if(ps->top>0)
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

//版本2.0
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
    return ps->top == 0;
}

👉销毁栈

销毁要注意的是记得把销毁后的变量置空即可。

void StackDestroy(Stack* ps)
{
    free(ps->a);
    ps->a = NULL;
    ps->capacity = ps->top = 0;
}

🔑队列区

在这里插入图片描述

👉什么是队列

队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头

image.png

🌟重点:先进先出

队列就好比去排队做核酸,肯定是先来先做嘛!!!跟排队一个道理。

🪐队列的接口函数

👉队列的建立和初始化

那队列也面临一个问题,那就是用顺序表呢?还是链表实现呢?

其实从队列的先进先出性质就可以知道顺序表是不太好的,因为如果你要顺序表的话,把头元素出列后,为了保持顺序表的结构,其他元素是要向移动一位的,而我们知道顺序表移动数据的时间复杂度为O(N),效率比较低。

所以队列使用的是链表结构

image.png

队列的建立需要注意的是:与单链表相比要多定义一个尾指针tail,方便数据入队列,不用每次都找尾。

这里可能有人会问,那为什么实现单链表的时候不加一个尾指针嘞?

其实单链表加尾指针作用不大,因为你尾删的时候,不也还需要遍历链表找尾的前一个节点吗!

那为了避免函数传参的时候要多传一个尾指针,可以把头和尾指针定义在结构体中,减少传参个数

typedef int QDataType;
//队列节点
typedef struct Qnode
{
    QDataType date;
    struct Qnode* next;
}Qnode;
//头指针和尾指针
typedef struct Queue
{
    Qnode * head;
    Qnode* tail;
}Queue;

初始化也是常规置空了

void QueueInit(Queue* q)
{
    assert(q);
    q->head = q->tail = NULL;
}

👉入队列与出队列

🌟重点:先进先出

入队列前嘛,要注意的是要判断第一次入列的时候是直接把新节点的值赋给头和尾指针的

后续入列就直接尾插即可

void QueuePush(Queue* q, QDataType data)
{
    assert(q);
    Qnode* newNode = (Qnode*)malloc(sizeof(Qnode));
    newNode->date = data;
    newNode->next = NULL;

    if (q->head == NULL)
    {
        q->head = q->tail = newNode;
    }
    else
    {
        q->tail->next = newNode;
        q->tail = q->tail->next;
    }
}

出列是跟单链表头删一样的操作,先记录头结点的下一个节点地址,然后释放头结点,重新连接新的头

image.png

void QueuePop(Queue* q)
{
    assert(q);
    assert(q->head);
    if(q->head->next==NULL)//删最后一个元素
    {
        free(q->head);
        q->tail=q->head=NULL;
    }
    else
    {
    Qnode* next = q->head->next;
    free(q->head);
    q->head = next;
    }

}

👉获取队列头部元素与队列尾部元素

获取头部元素就比较简单了,返回头指针指向的值前只需判断一下头有没有空即可,没有空就直接返回头指针指向的数据

QDataType QueueFront(Queue* q)
{
    assert(q);
    assert(q->head);
    return q->head->date;
}

获取尾部元素也是一样的操作

QDataType QueueBack(Queue* q)
{
    assert(q);
    assert(q->tail);
    return q->tail->date;
}

👉获取队列中有效元素个数

获取队列有效元素,就普普通通的遍历链表即可

定义一个变量count用来记录元素个数

int QueueSize(Queue* q)
{
    assert(q);
    Qnode* cur = q->head;
    //记录元素个数
    int count = 0;
    while (cur)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

👉检测队列是否为空
还是注意返回要求:检测队列是否为空,如果为空返回非零结果,如果非空返回0

跟上面栈的判定一样,也有两个版本,我就给一个较优的

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
    assert(q);
    return q->head == NULL;
}

👉销毁队列

销毁也跟单链表一样的操作,记录后一个节点,释放前一个节点,迭代往后遍历链表,最后把头和尾指针置空

要注意什么时候都要养成free掉节点后把对应指针置空,不然容易出bug!!!
void QueueDestroy(Queue* q)
{
    assert(q);
    Qnode* cur = q->head;
    while (cur)
    {
        Qnode* next = cur->next;
        free(cur);
        cur=next;
    }
    q->tail=q->head = NULL;

}

👉小结
看到这里你已经基本理解栈和队列的操作了,如果自己可以动手实操了,那么恭喜你已经掌握了栈和队列的基本操作。 其实学了顺序表和单链表的操作之后,栈和队列对于那么在座的各位来说是真的小菜一碟!

👉栈功能的动画演示

在这里插入图片描述

下面就分享一下我的源代码给大家玩玩

👉栈实现的源文件
text.c
#define _CRT_SECURE_NO_WARNINGS   //vs编译器需要这个

#include"Stack.h"

void menu()
{

        printf("*******************************\n");
        printf("       1.入栈   2.出栈         \n");
        printf("       3.获取栈顶元素          \n");
        printf("       4.获取栈中有效元素个数  \n");
        printf("       5.检测栈  6.打印栈数据   \n");
        printf("       -1.退出                 \n");
        printf("*******************************\n");
        printf(" 请选择>:   \n");

}
void textMenu()
{
    Stack stack;
    StackInit(&stack);
    int input = 0;
    STDataType x1 = 0;
    int  ret = 0;
    while (input != -1)
    {
        menu();
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("输入你要入栈的值,以-1为结束\n");
            scanf("%d", &x1);
            while (x1 != -1)
            {
                StackPush(&stack, x1);
                scanf("%d", &x1);
            }
            break;
        case 2:
            StackPop(&stack);
            printf("出栈成功\n");
            break;
        case 3:
            printf("栈顶元素为:%d \n", StackTop(&stack));
            break;
        case 4:
            printf("有效个数有:%d \n", StackSize(&stack));
            break;
        case 5:
            ret = StackEmpty(&stack);
            if (ret == 0)
            {
                printf("栈非空\n");
            }
            else
            {
                printf("栈为空\n");
            }
            break;
        case 6:        
            //栈不为空一直打印
            while (!StackEmpty(&stack))
            {
                printf("%d ", StackTop(&stack));
                //打印一个Pop一个
                StackPop(&stack);
            }printf("\n");
            break;
        case -1:
            printf("退出成功\n");
            break;
        default:
            printf("无此选项,请重新输入\n");
            break;
        }
    }
    StackDestroy(&stack);
}
int main()
{
    textMenu();

    return 0;
}
Stack.c
#define _CRT_SECURE_NO_WARNINGS

#include"Stack.h"

void StackInit(Stack* ps)
{
    ps->a = NULL;
    ps->capacity = ps->top = 0;
}

void StackPush(Stack* ps, STDataType x)
{
    assert(ps);
    if (ps->capacity == ps->top)
    {
        int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
        STDataType* tem = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);
        if (tem == NULL)
        {
            printf("realloc fail\n");
            exit(-1);
        }
        ps->a = tem;
        ps->capacity = newCapacity;
    }
    ps->a[ps->top] = x;
    ps->top++;
}

void StackPop(Stack* ps)
{
    assert(ps);
    assert(ps->top > 0);
    
    ps->top--;
}

STDataType StackTop(Stack* ps)
{
    assert(ps->top > 0);
    return ps->a[ps->top - 1];
}

int StackSize(Stack* ps)
{
    return ps->top;
}

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
    return ps->top == 0;
}

void StackDestroy(Stack* ps)
{
    free(ps->a);
    ps->a = NULL;
    free(ps);
    ps->capacity = ps->top = 0;
}

Stack.h
#pragma once

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

typedef int STDataType;

typedef struct Stack
{
    STDataType* a;
    int  top; //栈顶
    int  capacity; //容量
}Stack;

// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);

👉队列功能的动画演示

在这里插入图片描述

👉队列实现的源文件
text.c
#define _CRT_SECURE_NO_WARNINGS  //vs编译器需要这个

#include"Queue.h"

void menu()
{

        printf("*******************************\n");
        printf("       1.入列   2.出列         \n");
        printf("       3.获取队列头元素        \n");
        printf("       4.获取队列队尾元素      \n");
        printf("       5.获取队列中有效元素个数\n");
        printf("       6.检测队列  7.打印队列  \n");
        printf("       -1.退出                 \n");
        printf("*******************************\n");
        printf(" 请选择>:   \n");


}
void textMenu()
{
    Queue queue;
    QueueInit(&queue);
    int input = 0;
    QDataType x1 = 0;
    int  ret = 0;
    while (input != -1)
    {
            menu();
            scanf("%d", &input);
            switch (input)
            {
                case 1:
                    printf("输入你要入列的值,以-1为结束\n");
                    scanf("%d", &x1);
                    while (x1 != -1)
                    {
                        QueuePush(&queue, x1);
                        scanf("%d", &x1);
                    }
                    break;
                case 2:
                    QueuePop(&queue);
                    printf("出列成功\n");
                    break;
                case 3:
                    printf("头部元素为:%d \n", QueueFront(&queue));
                    break;
                case 4:
                    printf("尾部元素为:%d \n", QueueBack(&queue));
                    break;
                case 5:
                    printf("有效个数有:%d \n", QueueSize(&queue));
                    break;
                case 6:
                      ret = QueueEmpty(&queue);
                    if (ret == 0)
                    {
                        printf("队列非空\n");
                    }
                    else
                    {
                        printf("队列为空\n");
                    }
                    break;
                case 7:
                    //队列不为空一直打印
                    while (!QueueEmpty(&queue))
                    {
                        printf("%d ", QueueFront(&queue));
                        //打印一个Pop一个
                        QueuePop(&queue);
                    }printf("\n");
                    break;

                case -1:
                    printf("退出成功\n");
                    break;
                default:
                    printf("无此选项,请重新输入\n");
                    break;
            }
    }
    QueueDestroy(&queue);
}

int main()
{
    textMenu();
    return 0;
}
Queue.c
#define _CRT_SECURE_NO_WARNINGS

#include"Queue.h"

void QueueInit(Queue* q)
{
    assert(q);
    q->head = q->tail = NULL;
}

void QueuePush(Queue* q, QDataType data)
{
    assert(q);
    Qnode* newNode = (Qnode*)malloc(sizeof(Qnode));
    newNode->date = data;
    newNode->next = NULL;

    if (q->head == NULL)
    {
        q->head = q->tail = newNode;
    }
    else
    {
        q->tail->next = newNode;
        q->tail = q->tail->next;
    }
}

void QueuePop(Queue* q)
{
    assert(q);
    assert(q->head);
    if(q->head->next==NULL)//删最后一个元素
    {
        free(q->head);
        q->tail=q->head=NULL;
    }
    else
    {
    Qnode* next = q->head->next;
    free(q->head);
    q->head = next;
    }

}

QDataType QueueFront(Queue* q)
{
    assert(q);
    assert(q->head);
    return q->head->date;
}

QDataType QueueBack(Queue* q)
{
    assert(q);
    assert(q->tail);
    return q->tail->date;
}

int QueueSize(Queue* q)
{
    assert(q);
    Qnode* cur = q->head;
    int count = 0;
    while (cur)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
    assert(q);
    return q->head == NULL;
}

void QueueDestroy(Queue* q)
{
    assert(q);
    while (q->head)
    {
        Qnode* cur = q->head->next;
        free(q->head);
        q->head = cur;
    }
    q->tail = NULL;
    free(q);
    q = NULL;
}
Queue.h
#pragma once

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

typedef int QDataType;

typedef struct Qnode
{
    QDataType date;
    struct Qnode* next;
}Qnode;

typedef struct Queue
{
    Qnode * head;
    Qnode* tail;
}Queue;


// 初始化队列
void QueueInit(Queue* q);

// 队尾入队列
void QueuePush(Queue* q, QDataType data);

// 队头出队列
void QueuePop(Queue* q);

// 获取队列头部元素
QDataType QueueFront(Queue* q);

// 获取队列队尾元素
QDataType QueueBack(Queue* q);

// 获取队列中有效元素个数
int QueueSize(Queue* q);

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);

// 销毁队列
void QueueDestroy(Queue* q);

相关文章
|
25天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
24 1
|
12天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
30 5
|
28天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
1月前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
1月前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
50 4
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
137 9
|
2月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
39 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
初步认识栈和队列
初步认识栈和队列
61 10
|
2月前
数据结构(栈与列队)
数据结构(栈与列队)
22 1