• 【数据结构】栈和队列


    【数据结构】栈和队列

    1.栈

    栈的概念

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

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

    出栈:栈的删除操作叫做出栈。(出数据也在栈顶)

    在这里插入图片描述

    在这里插入图片描述

    栈的实现

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

    在这里插入图片描述

    初始化栈

    首先,我们需要用结构体创建一个栈,这个结构体需要包括栈的基本内容(栈,栈顶,栈的容量)。

    typedef int DataType;
    // 动态栈的结构
    typedef struct Stack {
        DataType *a;
        int top;     // 栈顶
        int capacity;// 容量
    } Stack;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后,我们需要一个初始化函数,对刚创建的栈进行初始化。

    // 初始化
    void StackInit(Stack *ps) {
        assert(ps);
        // 初始化的时候给数组一段空间
        ps->a = (DataType *) malloc(sizeof(DataType) * 4);
        if (ps->a == NULL) {
            perror("malloc fail");
            exit(-1);
        }
        ps->top = -1;    // 栈顶初始化为-1
        ps->capacity = 4;// 容量为4
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    销毁栈

    因为栈的内存空间是动态开辟出来的,当我们使用完后必须释放其内存空间,避免内存泄漏。

    // 销毁栈
    void StackDestroy(Stack *ps) {
        assert(ps);  
        free(ps->a);
        ps->a = NULL;
        ps->top = -1;
        ps->capacity = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    入栈

    进行入栈操作前,我们需要检测栈的当前状态,若已满,则需要先对其进行增容,然后才能进行入栈操作。

    // Push
    void StackPush(Stack *ps, DataType x) {
        assert(ps);
        // 扩容
        if (ps->top + 1 == ps->capacity) {
            DataType *tmp = (DataType *) realloc(ps->a, ps->capacity * 2 * sizeof(DataType));
            if (tmp == NULL) {
                perror("realloc fail");
                exit(-1);
            }
            ps->a = tmp;
            ps->capacity *= 2;
        }
        // 不需要扩容则Push,top初始化为-1 ,先++ 在插入
        ps->top++;
        ps->a[ps->top] = x;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    出栈

    出栈操作比较简单,即让栈顶的位置向下移动一位即可。但需检测栈是否为空,若为空,则不能进行出栈操作。

    // Pop
    void StackPop(Stack *ps) {
        assert(ps);             // 地址不为空
        assert(!StackEmpty(ps));// 栈不为空
        ps->top--;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    获取栈顶元素

    获取栈顶元素,即获取栈的最上方的元素。若栈为空,则不能获取。

    // 取栈顶
    DataType StackTop(Stack *ps) {
        assert(ps);
        return ps->a[ps->top];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    检测栈是否为空

    检测栈是否为空,即判断栈顶的位置是否是-1即可。若栈顶是-1,则栈为空。

    // 判断是否为空
    bool StackEmpty(Stack *ps) {
        assert(ps);
        // 如果top是-1 则为空 返回真
        return ps->top == -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    获取栈中有效元素个数

    因为top记录的是栈顶,使用top的值便代表栈中有效元素的个数。

    // 求栈的长度
    size_t StackSize(Stack *ps) {
        assert(ps);
        return ps->top + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    完整代码

    #pragma once
    #include 
    #include 
    #include 
    #include 
    typedef int DataType;
    // 动态栈的结构
    typedef struct Stack {
        DataType *a;
        int top;     // 栈顶
        int capacity;// 容量
    } Stack;
    
    // 初始化
    void StackInit(Stack *ps) {
        assert(ps);
        // 初始化的时候给数组一段空间
        ps->a = (DataType *) malloc(sizeof(DataType) * 4);
        if (ps->a == NULL) {
            perror("malloc fail");
            exit(-1);
        }
        ps->top = -1;    // 栈顶初始化为-1
        ps->capacity = 4;// 容量为4
    }
    
    // 销毁栈
    void StackDestroy(Stack *ps) {
        assert(ps);
        free(ps->a);
        ps->a = NULL;
        ps->top = -1;
        ps->capacity = 0;
    }
    
    // 判断是否为空
    bool StackEmpty(Stack *ps) {
        assert(ps);
        // 如果top是-1 则为空 返回真
        return ps->top == -1;
    }
    
    // Push
    void StackPush(Stack *ps, DataType x) {
        assert(ps);
        // 扩容
        if (ps->top + 1 == ps->capacity) {
            DataType *tmp = (DataType *) realloc(ps->a, ps->capacity * 2 * sizeof(DataType));
            if (tmp == NULL) {
                perror("realloc fail");
                exit(-1);
            }
            ps->a = tmp;
            ps->capacity *= 2;
        }
        // 不需要扩容则Push,top初始化为-1 ,先++ 在插入
        ps->top++;
        ps->a[ps->top] = x;
    }
    
    // Pop
    void StackPop(Stack *ps) {
        assert(ps);             // 地址不为空
        assert(!StackEmpty(ps));// 栈不为空
        ps->top--;
    }
    
    // 取栈顶
    DataType StackTop(Stack *ps) {
        assert(ps);
        return ps->a[ps->top];
    }
    
    // 求栈的长度
    size_t StackSize(Stack *ps) {
        assert(ps);
        return ps->top + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    2.队列

    队列的概念

    队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列遵守先进先出FIFO(First In First Out)的原则。

    入队列:队列的插入操作叫做入队列,进行插入操作的一端称为队尾。

    出队列:队列的删除操作叫做出队列,进行删除操作的一端称为队头。

    在这里插入图片描述

    队列的实现

    队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。

    在这里插入图片描述

    初始化队列

    首先我们需要创建一个结点类型,类型包含了该结点的数据和指向下一结点的指针。

    typedef int QDataType;
    // 队列里队头和队尾的结构
    typedef struct QueueNode {
        QDataType data;        //数据域
        struct QueueNode *next;//指针域
    } QNode;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    队列与普通链表又有所不同,普通链表只需要知道链表的头指针,而队列的信息包括了队头和队尾,所以我们需要再创建一个结构体用于存放队列的队头和队尾。并且记录队列的长度。

    // 队列的结构
    typedef struct Queue {
        QNode *front;// 队头
        QNode *rear; // 队尾
        size_t size; // 队列长度
    } Queue;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后,我们需要一个初始化函数,对刚创建的队列进行初始化。

    // 队列的初始化
    void QueueInit(Queue *pq) {
        assert(pq);
        pq->front = NULL;
        pq->rear = NULL;
        pq->size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    销毁队列

    队列中的每一个结点所占用的内存空间都是动态开辟的,当我们使用完队列后需要及时释放队列中的每一个结点。

    // 队列的销毁
    void QueueDestroy(Queue *pq) {
        assert(pq);
    	//接收队头
        QNode *cur = pq->front;
        //循环遍历队列销毁
    	while (cur) {
            QNode *nextnode = cur->next;
            free(cur);
            cur = nextnode;
        }
    	//初始化
        pq->front = pq->rear = NULL;
        pq->size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    入队列

    入队列,即申请一个新结点并将其链接到队尾,然后改变队尾的指针指向即可。需要注意的是:若队列中原本无数据,那么我们只需让队头和队尾均指向这个新申请的结点即可。

    // 进队列
    void QueuePush(Queue *pq, QDataType x) {
        assert(pq);
        QNode *newnode = (QNode *) malloc(sizeof(QNode));  //申请新节点
        if (newnode == NULL) {
            perror("malloc fail");
            exit(-1);
        }
        newnode->data = x;
        newnode->next = NULL;
        // 尾插,如果是第一个数据就设置队头和队尾,否则就尾插到队尾后面
        if (pq->front == NULL) {
            pq->front = pq->rear = newnode;
        } else {
            pq->rear->next = newnode;
            pq->rear = newnode;
        }
        pq->size++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    出队列

    出队列,即释放队头指针指向的结点并改变队头指针的指向即可。若队列中只有一个结点,那么直接将该结点释放,然后将队头和队尾置空即可。

    // 出队列
    void QueuePop(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
        // 当只有一个数据的时候,free掉后 front和rear都设置为NULL
        if (pq->front->next == NULL) {
            free(pq->front);
            pq->front = pq->rear = NULL;
        } else {
            QNode *nextnode = pq->front->next;
            free(pq->front);
            pq->front = nextnode;
        }
        pq->size--;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    获取队列头部元素

    获取队列头部元素,即返回队头指针指向的数据即可。

    // 取队头数据
    QDataType QueueFront(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
    
        return pq->front->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    获取队列尾部元素

    获取队列尾部元素,即返回队尾指针指向的数据即可。

    // 取队尾数据
    QDataType QueueBack(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
    
        return pq->rear->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    检测队列是否为空

    检测队列是否为空,即判断队头指针指向的内容是否为空。

    // 判断队列是否为空
    bool QueueEmpty(Queue *pq) {
        assert(pq);
    
        return pq->front == NULL && pq->rear == NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    获取队列中有效元素个数

    // 队列长度
    size_t QueueSize(Queue *pq) {
        assert(pq);
    
        return pq->size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    完整代码

    #pragma once
    #include 
    #include 
    #include 
    #include 
    
    typedef int QDataType;
    // 队列里队头和队尾的结构
    typedef struct QueueNode {
        QDataType data;        //数据域
        struct QueueNode *next;//指针域
    } QNode;
    
    // 队列的结构
    typedef struct Queue {
        QNode *front;// 队头
        QNode *rear; // 队尾
        size_t size; // 队列长度
    } Queue;
    
    // 队列的初始化
    void QueueInit(Queue *pq) {
        assert(pq);
        pq->front = NULL;
        pq->rear = NULL;
        pq->size = 0;
    }
    
    // 队列的销毁
    void QueueDestroy(Queue *pq) {
        assert(pq);
    	//接收队头
        QNode *cur = pq->front;
        //循环遍历队列销毁
    	while (cur) {
            QNode *nextnode = cur->next;
            free(cur);
            cur = nextnode;
        }
    	//初始化
        pq->front = pq->rear = NULL;
        pq->size = 0;
    }
    
    // 进队列
    void QueuePush(Queue *pq, QDataType x) {
        assert(pq);
        QNode *newnode = (QNode *) malloc(sizeof(QNode));  //申请新节点
        if (newnode == NULL) {
            perror("malloc fail");
            exit(-1);
        }
        newnode->data = x;
        newnode->next = NULL;
        // 尾插,如果是第一个数据就设置队头和队尾,否则就尾插到队尾后面
        if (pq->front == NULL) {
            pq->front = pq->rear = newnode;
        } else {
            pq->rear->next = newnode;
            pq->rear = newnode;
        }
        pq->size++;
    }
    
    // 出队列
    void QueuePop(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
        // 当只有一个数据的时候,free掉后 front和rear都设置为NULL
        if (pq->front->next == NULL) {
            free(pq->front);
            pq->front = pq->rear = NULL;
        } else {
            QNode *nextnode = pq->front->next;
            free(pq->front);
            pq->front = nextnode;
        }
        pq->size--;
    }
    
    // 取队头数据
    QDataType QueueFront(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
    
        return pq->front->data;
    }
    
    // 取队尾数据
    QDataType QueueBack(Queue *pq) {
        assert(pq);
        assert(!QueueEmpty(pq));
    
        return pq->rear->data;
    }
    
    // 判断队列是否为空
    bool QueueEmpty(Queue *pq) {
        assert(pq);
    
        return pq->front == NULL && pq->rear == NULL;
    }
    
    // 队列长度
    size_t QueueSize(Queue *pq) {
        assert(pq);
    
        return pq->size;
    }
    
    // 打印队列
    void QueuePrint(Queue *pq) {
        assert(pq);
        while (!QueueEmpty(pq)) {
            printf("%d->", QueueFront(pq));
            QueuePop(pq);
        }
        printf("NULL");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
  • 相关阅读:
    【OpenCV-Python】教程:3-9 轮廓(4)更多函数
    洛谷 P5536 【XR-3】核心城市(贪心 + 树形 dp 寻找树的中心)
    温敏AIE双亲多肽分子/AIE磁性荧光微球/多重刺激响应性聚合物纳米微球相关制备
    PL/SQL 数组
    Maven[项目构建工具]
    机器学习——奇异值分解二(特征分解+SVD纯理解)
    试试这些方法,误删文件怎么恢复?
    Apache ShardingSphere 5.1.2 发布|全新驱动 API + 云原生部署,打造高性能数据网关...
    Python:每日一题之四平方和
    什么是编辑器中的常量传播?
  • 原文地址:https://blog.csdn.net/ikun66666/article/details/132725510