【数据结构】栈和队列

【数据结构】栈和队列

1.栈

栈的概念

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

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

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

栈的实现

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

初始化栈

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

cpp 复制代码
typedef int DataType;
// 动态栈的结构
typedef struct Stack {
    DataType *a;
    int top;     // 栈顶
    int capacity;// 容量
} Stack;

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

cpp 复制代码
// 初始化
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
}

销毁栈

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

cpp 复制代码
// 销毁栈
void StackDestroy(Stack *ps) {
    assert(ps);  
    free(ps->a);
    ps->a = NULL;
    ps->top = -1;
    ps->capacity = 0;
}

入栈

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

cpp 复制代码
// 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;
}

出栈

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

cpp 复制代码
// Pop
void StackPop(Stack *ps) {
    assert(ps);             // 地址不为空
    assert(!StackEmpty(ps));// 栈不为空
    ps->top--;
}

获取栈顶元素

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

cpp 复制代码
// 取栈顶
DataType StackTop(Stack *ps) {
    assert(ps);
    return ps->a[ps->top];
}

检测栈是否为空

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

cpp 复制代码
// 判断是否为空
bool StackEmpty(Stack *ps) {
    assert(ps);
    // 如果top是-1 则为空 返回真
    return ps->top == -1;
}

获取栈中有效元素个数

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

cpp 复制代码
// 求栈的长度
size_t StackSize(Stack *ps) {
    assert(ps);
    return ps->top + 1;
}

完整代码

cpp 复制代码
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
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;
}

2.队列

队列的概念

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

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

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

队列的实现

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

初始化队列

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

cpp 复制代码
typedef int QDataType;
// 队列里队头和队尾的结构
typedef struct QueueNode {
    QDataType data;        //数据域
    struct QueueNode *next;//指针域
} QNode;

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

cpp 复制代码
// 队列的结构
typedef struct Queue {
    QNode *front;// 队头
    QNode *rear; // 队尾
    size_t size; // 队列长度
} Queue;

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

cpp 复制代码
// 队列的初始化
void QueueInit(Queue *pq) {
    assert(pq);
    pq->front = NULL;
    pq->rear = NULL;
    pq->size = 0;
}

销毁队列

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

cpp 复制代码
// 队列的销毁
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;
}

入队列

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

cpp 复制代码
// 进队列
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++;
}

出队列

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

cpp 复制代码
// 出队列
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--;
}

获取队列头部元素

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

cpp 复制代码
// 取队头数据
QDataType QueueFront(Queue *pq) {
    assert(pq);
    assert(!QueueEmpty(pq));

    return pq->front->data;
}

获取队列尾部元素

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

cpp 复制代码
// 取队尾数据
QDataType QueueBack(Queue *pq) {
    assert(pq);
    assert(!QueueEmpty(pq));

    return pq->rear->data;
}

检测队列是否为空

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

cpp 复制代码
// 判断队列是否为空
bool QueueEmpty(Queue *pq) {
    assert(pq);

    return pq->front == NULL && pq->rear == NULL;
}

获取队列中有效元素个数

cpp 复制代码
// 队列长度
size_t QueueSize(Queue *pq) {
    assert(pq);

    return pq->size;
}

完整代码

cpp 复制代码
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

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");
}
相关推荐
lb36363636363 小时前
介绍一下数组(c基础)(详细版)
c语言
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
丶Darling.5 小时前
Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
c++·算法·动态规划·记忆化搜索·回溯
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法
我是哈哈hh6 小时前
专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结
c++·算法·动态规划
执笔者5486 小时前
C语言:函数栈帧的创建与销毁
c语言
_小柏_7 小时前
C/C++基础知识复习(15)
c语言·c++