数据结构与算法——栈和队列
文章目录
- 一、前言
- 二、栈
- 栈的概念
- 栈的结构
- 三、用数组实现栈
- 3.1栈的初始化
- 3.2入栈
- 3.3栈的判空
- 3.4出栈
- 3.5取栈顶元素
- 3.6获取栈中有效元素个数
- 3.7栈的销毁
- 四、栈的整体代码分析
- 4.1头文件——Stack.h及代码展示
- 4.2主体代码文件Stack.c及代码展示
- 4.3测试文件test.c及代码展示
- 4.3.1初始化测试
- 4.3.2入栈测试
- 4.3.3出栈测试
- 4.3.4取栈顶元素测试
- 4.3.5获取有效个数测试
- 4.3.6栈的销毁测试
- 4.3.7test.c整体代码
- 五、队列
- 5.1队列的概念
- 5.2队列的结构
- 六、用链表实现队列
- 6.1队列的初始化
- 6.2入队
- 6.3队列的判空
- 6.4出队
- 6.5取队头元素
- 6.6取队尾元素
- 6.7获取队列中有效元素个数
- 6.8队列的销毁
- 七、队列的整体代码分析
- 7.1头文件——Queue.h及代码展示
- 7.2主体代码文件——Queue.c及代码展示
- 7.3测试文件——test.c及代码展示
- 7.3.1初始化测试
- 7.3.2入队测试
- 7.3.3出队列测试
- 7.3.4取队头元素测试
- 7.3.5取队尾元素测试
- 7.3.6获取有效个数测试
- 7.3.7队列的销毁测试
- 7.3.8test.c整体代码
- 八、总结
一、前言
Hello啊!还是那句老掉牙的开场白——今天的你还在努力学习吗?可不能半途而废哦! 如果你正在迷茫,请看看下面这句话——未知的领域是学习的源泉,保持好奇,永远在学习的路上。现在你是否又满血复活了呢?ok那现在我们接着继续学习数据结构与算法,今天up要给大家介绍的内容则是——栈和队列。
二、栈
栈的概念
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。。
压栈:向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素,入的数据在栈顶。
出栈:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素,出的数据也在栈顶。
栈满足后进先出或者先进后出的原则。
栈的结构
三、用数组实现栈
3.1栈的初始化
在我们用数组来实现栈的时候,那我们该如何初始化呢?
代码展示:
void StackInit(ST* ps)//栈的初始化
{ps->arr = NULL;//初始化数组ps->top = 0;//栈顶ps->capacity = 0;//栈的容量大小
}
栈的初始化实质:把数组arr初始化为NULL,栈顶指针初始化为0,栈的空间大小也初始化为0。
3.2入栈
画图展示:
代码展示:
void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);if (ps->top == ps->capacity)//判断栈空间是否已满{int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newcapacity*sizeof(STDataType));//增容if (tmp == NULL){printf("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newcapacity;}ps->arr[ps->top++] = x;
}
入栈的实质:在栈空间未满的情况下,直接把元素插入栈顶即可;在栈空间已满的情况下,先进行二倍增容,在把元素插入栈顶,top++。
3.3栈的判空
画图展示:
代码展示:
bool Stackempty(ST* ps)//判空
{assert(ps);return ps->top == 0;
}
栈的判空实质:在栈这块空间指向不为空的情况下:看栈顶指针top是否为0即可,为0则为空,反之不为空。
3.4出栈
画图展示:
代码展示:
void StackPop(ST* ps)//出栈
{assert(!Stackempty(ps));--ps->top;
}
出栈的实质:在判断栈不为空的情况下:直接把元素从栈顶删除即可,top–。
3.5取栈顶元素
代码展示:
STDataType StackTop(ST* ps)//取栈顶元素
{assert(!Stackempty(ps));return ps->arr[ps->top - 1];
}
取栈顶元素实质:在判断栈不为空的情况下,直接返回栈顶元素即可。
3.6获取栈中有效元素个数
代码展示:
int Stacksize(ST* ps)//获取栈中元素个数
{assert(ps);return ps->top;
}
获取栈中有效个数实质:top的值的大小就是栈中元素个数的大小,直接返回top值即可。
3.7栈的销毁
画图展示:
代码展示:
void Stackdestroy(ST* ps)//栈的销毁
{if (ps->arr){free(ps->arr);}ps->arr = NULL;ps->top = 0;ps->capacity = 0;
}
栈的销毁实质:在数组不为空的情况下,先把数组置为空(NULL),再把top和capacity也置为0即可。
四、栈的整体代码分析
4.1头文件——Stack.h及代码展示
代码展示:
//Stack.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;int capacity;
}ST;
void StackInit(ST* ps);//栈的初始化
void Stackdestroy(ST* ps);//栈的销毁
void StackPush(ST* ps, STDataType x);//入栈
bool Stackempty(ST* ps);//判空
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//取栈顶元素
int Stacksize(ST* ps);//获取栈中元素个数
4.2主体代码文件Stack.c及代码展示
代码展示:
//stack.c
#include "Stack.h"
void StackInit(ST* ps)//栈的初始化
{ps->arr = NULL;//初始化数组ps->top = 0;//栈顶ps->capacity = 0;//栈的容量大小
}
void Stackdestroy(ST* ps)//栈的销毁
{if (ps->arr){free(ps->arr);}ps->arr = NULL;ps->top = 0;ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{assert(ps);if (ps->top == ps->capacity)//判断栈空间是否已满{int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newcapacity*sizeof(STDataType));//增容if (tmp == NULL){printf("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newcapacity;}ps->arr[ps->top++] = x;
}
bool Stackempty(ST* ps)//判空
{assert(ps);return ps->top == 0;
}
void StackPop(ST* ps)//出栈
{assert(!Stackempty(ps));--ps->top;
}
STDataType StackTop(ST* ps)//取栈顶元素
{assert(!Stackempty(ps));return ps->arr[ps->top - 1];
}
int Stacksize(ST* ps)//获取栈中元素个数
{assert(ps);return ps->top;
}
4.3测试文件test.c及代码展示
4.3.1初始化测试
StackInit(&st);//初始化
4.3.2入栈测试
StackPush(&st, 1);//入栈
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
4.3.3出栈测试
StackPop(&st);//出栈
StackPop(&st);
StackPop(&st);
4.3.4取栈顶元素测试
if(!Stackempty(&st))//判空+取栈顶元素
{int top = StackTop(&st);printf("%d\n", top);
}
4.3.5获取有效个数测试
int size = Stacksize(&st);//获取有效个数printf("%d\n", size);
4.3.6栈的销毁测试
Stackdestroy(&st);//栈的销毁
4.3.7test.c整体代码
请根据自己的需求合理注释再进行测试。
test.c
#include "Stack.h"
void test01()
{ST st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);//StackPop(&st);//StackPop(&st);//StackPop(&st);//if(!Stackempty(&st))//{// int top = StackTop(&st);// printf("%d\n", top);//}//int size = Stacksize(&st);//printf("%d\n", size);Stackdestroy(&st);
}
int main()
{test01();return 0;
}
五、队列
5.1队列的概念
队列是一种特殊的线性表,他只允许在一端插入数据,在另一端删除数据。
入队列:在队尾进行插入操作。
出队列:在对头进行删除操作。
因此,队列满足先进先出的原则。
5.2队列的结构
六、用链表实现队列
6.1队列的初始化
void QueueInit(Queue* pq)//队列初始化
{assert(pq);pq->phead = NULL;pq->ptail = NULL;
}
队列的初始化实质:当队列这块空间指向不为空的情况下:把对头结点指针和队尾结点指针都置为NULL即可。
6.2入队
画图展示:
代码展示:
void QueuePush(Queue* pq, QDataType x)//入队
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//创建新结点if (newnode == NULL){printf("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}
}
入队的实质:在队列这块空间指向不为空的情况下:将新节点newnode的地址放入队尾ptail结点的ptail->next中,再让新节点newnode成为新的队尾ptail即可。
6.3队列的判空
代码展示:
bool Queueempty(Queue* pq)//判空
{assert(pq);return pq->phead == NULL;
}
判空的实质:当队列指向的这块空间不为空的情况下:判断队头是否为空,若队头为空则队列为空,反之队列不为空。
6.4出队
画图展示:
代码展示:
void QueuePop(Queue* pq)//出队
{assert(!Queueempty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
出队的实质:在判断栈不为空的情况下:如果队列只有一个结点,直接将这个结点释放掉,队头(pq->phead)和队尾(pq->ptail)都置为空即可;如果队列不止一个结点,先把队头的下一个结点的地址用next指针保存下来,再将队头给释放掉,最后再让next指针成为新的队头指针即可。
6.5取队头元素
代码展示:
QDataType QueueFront(Queue* pq)//取对头数据
{assert(!Queueempty(pq));return pq->phead->data;
}
取队头元素实质:在判断队列不为空的情况下:直接返回队头所指向的数据值pq->phead->data即可。
6.6取队尾元素
代码展示:
QDataType QueueBack(Queue* pq)//取队尾数据
{assert(!Queueempty(pq));return pq->ptail->data;
}
取队尾元素实质:在判断队列不为空的情况下:直接返回队尾所指向的数据值pq->ptail->data即可。
6.7获取队列中有效元素个数
代码展示:
int Queuesize(Queue* pq)//计算队列有效个数
{assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;
}
获取队列中有效元素个数实质:在队列所指向的这块空间不为空的情况下:定义一个开始指针pcur从队头开始,再定义一个size值来累计元素个数,依次遍历整个队列,只要pcur不为空,size就累加,反之停止。
6.8队列的销毁
画图展示:
代码展示:
void QueueDestroy(Queue* pq)//队列的销毁
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = NULL;pq->ptail = NULL;
}
队列的销毁实质:当队列所指向的空间这块不为空的情况下:定义一个开始指针pcur从队头开始,再定义一个next指针保存队头的下一个结点,当pcur不为空时,将队头直接释放掉,然后让next成为新的队头,依次循环遍历整个队列,直到pcur为空时停止。
七、队列的整体代码分析
7.1头文件——Queue.h及代码展示
//Queue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode//定义结点
{QDataType data;struct QueueNode* next;
}QueueNode;
typedef struct Queue
{QueueNode* phead;//定义队头队尾指针QueueNode* ptail;
}Queue;
void QueueInit(Queue* pq);//队列初始化
void QueueDestroy(Queue* pq);//队列的销毁
void QueuePush(Queue* pq, QDataType x);//入队
bool Queueempty(Queue* pq);//判空
void QueuePop(Queue* pq);//出队
QDataType QueueFront(Queue* pq);//取对头数据
QDataType QueueBack(Queue* pq);//取队尾数据
int Queuesize(Queue* pq);//计算队列有效个数
7.2主体代码文件——Queue.c及代码展示
//Queue.c
#include "Queue.h"
void QueueInit(Queue* pq)//队列初始化
{assert(pq);pq->phead = NULL;pq->ptail = NULL;
}
void QueueDestroy(Queue* pq)//队列的销毁
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = NULL;pq->ptail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//入队
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//创建新结点if (newnode == NULL){printf("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}
}
bool Queueempty(Queue* pq)//判空
{assert(pq);return pq->phead == NULL;
}
void QueuePop(Queue* pq)//出队
{assert(!Queueempty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
QDataType QueueFront(Queue* pq)//取对头数据
{assert(!Queueempty(pq));return pq->phead->data;
}
QDataType QueueBack(Queue* pq)//取队尾数据
{assert(!Queueempty(pq));return pq->ptail->data;
}
int Queuesize(Queue* pq)//计算队列有效个数
{assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;
}
7.3测试文件——test.c及代码展示
7.3.1初始化测试
Queue q;
QueueInit(&q);//初始化
7.3.2入队测试
QueuePush(&q, 1);//入队列
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
7.3.3出队列测试
QueuePop(&q);//出队列
QueuePop(&q);
7.3.4取队头元素测试
int front = QueueFront(&q);//取队头元素printf("%d\n", front);
7.3.5取队尾元素测试
int rear = QueueBack(&q);//取队尾元素printf("%d\n", rear);
7.3.6获取有效个数测试
int sizenum = Queuesize(&q);//获取有效个数printf("%d\n", sizenum);
7.3.7队列的销毁测试
QueueDestroy(&q);//队列销毁
7.3.8test.c整体代码
请根据自己的需求合理注释再进行测试。
//test.c
#include "Queue.h"
void test01()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePop(&q);QueuePop(&q);//int front = QueueFront(&q);//printf("%d\n", front);//int rear = QueueBack(&q);//printf("%d\n", rear);//int sizenum = Queuesize(&q);//printf("%d\n", sizenum);QueueDestroy(&q);
}
int main()
{test01();return 0;
}
八、总结
Ok啊兄弟们,随着你们看到总结的到来,数据结构与算法——栈和队列的讲解也就结束了。虽然这一节涉及到了两个数据结构,但是整体理解起来还是比较easy。希望大家好好消化,增强自身。I believe you!
错误经不起失败,但是真理却不怕失败。