一.栈的表示和实现
1.栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵循后进先出LIFO(Last in First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据也在栈顶
2.栈的实现
从上面我们也可以看出来,栈的实现一般可以使用数组或者链表实现,相对而言,其实数组的结构实现更优一些,因为数组在尾上插入数据的代价比较小
二、栈的实现
1.栈的声明和定义
根据我们上面的分析,我们决定使用顺序表来实现栈。所以它的声明如下
typedef int STDateType; typedef struct Stack { STDateType* a; int top; int capacity; }Stack;
2.栈的初始化
它的声明如下
//栈的初始化 void StackInit(Stack* ps);
它的函数实现也很简单
//栈的初始化 void StackInit(Stack* ps) { assert(ps); ps->a = (STDateType*)malloc(4 * sizeof(STDateType)); if (ps->a == NULL) { printf("malloc is fail\n"); exit(-1); } ps->capacity = 4; ps->top = 0; }
注意这里的top可以为0也可以为-1
这两种其实也是有区别的,
如果top初始化为0的化,那就意味着top指向栈顶元素的下一个
如果top初始化欸-1的话,那么意味着top指向栈顶元素
3.栈的销毁
创建好那么必要要有销毁
销毁的声明为
//栈的销毁 void StackDestory(Stack* ps);
实现为
//栈的销毁 void StackDestory(Stack* ps) { assert(ps); free(ps->a); ps->a = NULL; ps->capacity = ps->top = 0; }
4.入栈
这个也非常简单,函数声明为
//入栈 void StackPush(Stack* ps, STDateType x);
实现为
//入栈 void StackPush(Stack* ps, STDateType x) { assert(ps); if (ps->top == ps->capacity) { //扩容 STDateType* tmp = (STDateType*)realloc(ps->a, sizeof(STDateType) * 2 * ps->capacity); if (tmp == NULL) { printf("realloc is fail\n"); exit(-1); } ps->a = tmp; ps->capacity *= 2; } ps->a[ps->top] = x; ps->top++; }
5.出栈
函数声明为
//出栈 void StackPop(Stack* ps);
函数实现为
//出栈 void StackPop(Stack* ps) { assert(ps); //栈为空了,还想要继续删除直接报错 assert(ps->top > 0); ps->top--; }
6.返回栈顶元素
这是函数声明
//取出栈顶元素 STDateType StackTop(Stack* ps);
这是函数实现
//取出栈顶元素 STDateType StackTop(Stack* ps) { assert(ps); //栈为空,还继续调用的话直接报错 assert(ps->top > 0); return ps->a[ps->top - 1]; }
7.返回栈的元素个数
这是函数声明
//栈的元素个数 int StackSize(Stack* ps);
这是函数实现
//栈的元素个数 int StackSize(Stack* ps) { assert(ps); return ps->top; }
8.栈是否为空
这是函数声明
//栈是否为空 bool StackEmpty(Stack* ps);
这是函数实现
//栈是否为空 bool StackEmpty(Stack* ps) { assert(ps); return (ps->top == 0); }
9.测试
#define _CRT_SECURE_NO_WARNINGS 1 #include"Stack.h" int main() { Stack s; StackInit(&s); StackPush(&s, 1); StackPush(&s, 2); StackPush(&s, 3); printf("%d ", StackTop(&s)); StackPop(&s); printf("%d ", StackTop(&s)); StackPop(&s); StackPush(&s, 4); StackPush(&s, 5); while (!StackEmpty(&s)) { printf("%d ", StackTop(&s)); StackPop(&s); } StackDestory(&s); }
运行结果为
三、栈的完整代码
Test.c文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"Stack.h" int main() { Stack s; StackInit(&s); StackPush(&s, 1); StackPush(&s, 2); StackPush(&s, 3); printf("%d ", StackTop(&s)); StackPop(&s); printf("%d ", StackTop(&s)); StackPop(&s); StackPush(&s, 4); StackPush(&s, 5); while (!StackEmpty(&s)) { printf("%d ", StackTop(&s)); StackPop(&s); } StackDestory(&s); }
Stack.h文件
#pragma once #include<stdio.h> #include<stdbool.h> #include<assert.h> #include<string.h> #include<stdlib.h> typedef int STDateType; typedef struct Stack { STDateType* a; int top; int capacity; }Stack; //栈的初始化 void StackInit(Stack* ps); //栈的销毁 void StackDestory(Stack* ps); //入栈 void StackPush(Stack* ps, STDateType x); //出栈 void StackPop(Stack* ps); //取出栈顶元素 STDateType StackTop(Stack* ps); //栈的元素个数 int StackSize(Stack* ps); //栈是否为空 bool StackEmpty(Stack* ps);
Stack.c文件
#define _CRT_SECURE_NO_WARNINGS 1 #include"Stack.h" //栈的初始化 void StackInit(Stack* ps) { assert(ps); ps->a = (STDateType*)malloc(4 * sizeof(STDateType)); if (ps->a == NULL) { printf("malloc is fail\n"); exit(-1); } ps->capacity = 4; ps->top = 0; } //栈的销毁 void StackDestory(Stack* ps) { assert(ps); free(ps->a); ps->a = NULL; ps->capacity = ps->top = 0; } //入栈 void StackPush(Stack* ps, STDateType x) { assert(ps); if (ps->top == ps->capacity) { //扩容 STDateType* tmp = (STDateType*)realloc(ps->a, sizeof(STDateType) * 2 * ps->capacity); if (tmp == NULL) { printf("realloc is fail\n"); exit(-1); } ps->a = tmp; ps->capacity *= 2; } ps->a[ps->top] = x; ps->top++; } //出栈 void StackPop(Stack* ps) { assert(ps); //栈为空了,还想要继续删除直接报错 assert(ps->top > 0); ps->top--; } //取出栈顶元素 STDateType StackTop(Stack* ps) { assert(ps); //栈为空,还继续调用的话直接报错 assert(ps->top > 0); return ps->a[ps->top - 1]; } //栈的元素个数 int StackSize(Stack* ps) { assert(ps); return ps->top; } //栈是否为空 bool StackEmpty(Stack* ps) { assert(ps); return (ps->top == 0); }
四、队列的表示和实现
1.队列的概念和结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First in First Out)入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
2.队列的实现
队列也可以数组和链表的结构实现,但使用链表的结构更优一些,因为如果使用数组的结构,出队列在数组的头上出数据,效率会比较低.
五、队列的实现
1.队列的声明和定义
我们想要使用链表的结构来实现队列,但是由于单链表的尾插过于麻烦,不妨我们直接声明头节点和尾结点来控制队列
typedef int QDateType; typedef struct QueueNode { struct QueueNode* next; QDateType date; }QNode; typedef struct Queue { QNode* head; QNode* tail; }Queue;
如上所示,我们先定义了一个队列结点的结构体,里面包含了指向下一个结点的指针和一个队列的数据。然后我们定义了一个队列结构体,我们主要是使用队列这个结构体,它里面有头结点和尾结点,有头有尾刚好确定一个队列。
2.队列的初始化
接下来让我们先来实现一下队列的初始化
函数声明为
//队列初始化 void QueueInit(Queue* pq);
实现为
//队列的初始化 void QueueInit(Queue* pq) { assert(pq); pq->head = NULL; pq->tail = NULL; }
其实对这个实现,建议与前面的一些LeetCode题目和单链表的实现进行对比,我们可以看出来这里的妙处。
3.队列的销毁
有始有终,我们来实现一下队列的销毁
//队列的销毁 void QueueDestory(Queue* pq);
函数实现为
//队列的销毁 void QueueDestory(Queue* pq) { assert(pq); QNode* cur = pq->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; }
4.队列的插入
函数声明为
//队列的插入 void QueuePush(Queue* pq, QDateType x);
函数实现为
//队列的插入 void QueuePush(Queue* pq, QDateType x) { assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { printf("malloc is fail\n"); exit(-1); } //对新节点进行初始化 newnode->date = x; newnode->next = NULL; //对新结点进行连接 if (pq->head == NULL) { //头结点是空的,也就是第一个数据的插入 pq->head = pq->tail = newnode; } else { //非第一个数据的插入 pq->tail->next = newnode; pq->tail = newnode; } }
5.队列的删除
函数声明为
//队列的删除 void QueuePop(Queue* pq);
函数实现为
//队列的删除 void QueuePop(Queue* pq) { assert(pq); //确保队列不是空的队列 assert(pq->head); //如果只有一个结点,防止tail形成野指针 if (pq->head->next == NULL) { free(pq->head); pq->head = pq->tail = NULL; } //不是只有一个结点 else { //保存第二个队列结点 QNode* next = pq->head->next; free(pq->head); pq->head = next; } }
6.队列的判空
函数声明
//判断队列是否为空 bool QueueEmpty(Queue* pq);
函数实现
//判断队列是否为空 bool QueueEmpty(Queue* pq) { assert(pq); return pq->head == NULL; }
7.取出队头的数据
函数声明
//取出队头的数据 QDateType QueueFront(Queue* pq);
函数实现
//取出队头的数据 QDateType QueueFront(Queue* pq) { assert(pq); assert(pq->head); return pq->head->date; }
8.取出队尾的数据
函数声明
//取出队尾的数据 QDateType QueueBack(Queue* pq);
函数实现
//取出队尾的数据 QDateType QueueBack(Queue* pq) { assert(pq); assert(pq->head); return pq->tail->date; }
9.队列数据的个数
函数声明
//取出数据的个数 int QueueSize(Queue* pq);
函数实现
//取出数据的个数 int QueueSize(Queue* pq) { assert(pq); int size = 0; QNode* cur = pq->head; while (cur) { cur = cur->next; size++; } return size; }
10.测试
void TestQueue() { Queue q; QueueInit(&q); QueuePush(&q, 1); QueuePush(&q, 2); QueuePush(&q, 3); QueuePush(&q, 4); QueuePush(&q, 5); while (!QueueEmpty(&q)) { printf("%d ", QueueFront(&q)); QueuePop(&q); } printf("\n"); QueueDestory(&q); } int main() { //TestStack(); TestQueue(); }
运行结果为
六、队列完整代码
Test.c
void TestQueue() { Queue q; QueueInit(&q); QueuePush(&q, 1); QueuePush(&q, 2); QueuePush(&q, 3); QueuePush(&q, 4); QueuePush(&q, 5); while (!QueueEmpty(&q)) { printf("%d ", QueueFront(&q)); QueuePop(&q); } printf("\n"); QueueDestory(&q); } int main() { //TestStack(); TestQueue(); }
Queue.h
#pragma once #include<stdio.h> #include<stdlib.h> #include<string.h> #include<stdbool.h> #include<assert.h> typedef int QDateType; typedef struct QueueNode { struct QueueNode* next; QDateType date; }QNode; typedef struct Queue { QNode* head; QNode* tail; }Queue; //队列初始化 void QueueInit(Queue* pq); //队列的销毁 void QueueDestory(Queue* pq); //队列的插入 void QueuePush(Queue* pq, QDateType x); //队列的删除 void QueuePop(Queue* pq); //取出队头的数据 QDateType QueueFront(Queue* pq); //取出队尾的数据 QDateType QueueBack(Queue* pq); //取出数据的个数 int QueueSize(Queue* pq); //判断队列是否为空 bool QueueEmpty(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"Queue.h" //队列的初始化 void QueueInit(Queue* pq) { assert(pq); pq->head = NULL; pq->tail = NULL; } //队列的销毁 void QueueDestory(Queue* pq) { assert(pq); QNode* cur = pq->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; } //队列的插入 void QueuePush(Queue* pq, QDateType x) { assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { printf("malloc is fail\n"); exit(-1); } //对新节点进行初始化 newnode->date = x; newnode->next = NULL; //对新结点进行连接 if (pq->head == NULL) { //头结点是空的,也就是第一个数据的插入 pq->head = pq->tail = newnode; } else { //非第一个数据的插入 pq->tail->next = newnode; pq->tail = newnode; } } //队列的删除 void QueuePop(Queue* pq) { assert(pq); //确保队列不是空的队列 assert(pq->head); //如果只有一个结点,防止tail形成野指针 if (pq->head->next == NULL) { free(pq->head); pq->head = pq->tail = NULL; } //不是只有一个结点 else { //保存第二个队列结点 QNode* next = pq->head->next; free(pq->head); pq->head = next; } } //判断队列是否为空 bool QueueEmpty(Queue* pq) { assert(pq); return pq->head == NULL; } //取出队头的数据 QDateType QueueFront(Queue* pq) { assert(pq); assert(pq->head); return pq->head->date; } //取出队尾的数据 QDateType QueueBack(Queue* pq) { assert(pq); assert(pq->head); return pq->tail->date; } //取出数据的个数 int QueueSize(Queue* pq) { assert(pq); int size = 0; QNode* cur = pq->head; while (cur) { cur = cur->next; size++; } return size; }
总结
本节实现了栈和队列的实现,难度不大,希望大家都能学会
如果对你有帮助,不要忘记点赞加收藏哦!!!
想获得更多优质的内容,一定要记得关注我哦!!!