扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
今天我们来完成栈和队列,首先我们要明白什么是栈,什么是队列。
目前成都创新互联公司已为上千家的企业提供了网站建设、域名、网页空间、网站托管、服务器托管、企业网站设计、东川网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。目录
栈的选择
栈的结构
栈的初始化
栈的销毁
入栈
出栈
返回栈顶元素
计算数据个数
判断是否为空
队列的选择
队列的结构
入队列
出队列
判断是否为空
取队头元素
取队尾元素
计算数据个数
队列的销毁
我们之前写过顺序表,链表,这都是数据结构,而栈和队列,也是数据结构,栈的特殊地方在于,栈是后进先出,也叫LIFO(last in first out),比如我们平时在桌子上堆放的书,我们堆放了十本书,如果不直接把书抽出来的话,我们要拿到最下面那本书,需要把上面九本先拿下去,也就是先要从最上面的那一本开始拿,而最上面的那一本,是我们最后放上去的,这就是一个栈。再说队列,队列的特殊地方在于先进先出,也叫FIFO(first in first out),比如我们日常生活里的排队就是一个队列,不考虑插队这些,正常情况下,第一个来的人一定第一个完成自己的业务,最后一个来的人最后完成,这就是一个队列。
我们还是采取工程化的写法。
首先我们来完成栈,栈的实现可以使用数组或者链表,那我们选取哪个比较好呢?
栈的选择我们来比较一下二者的优缺点。
首先我们看数组: 使用数组就相当于之前顺序表的尾插和尾删,用尾去做栈顶,非常合适,唯一的缺点是空间不足时需要扩容。完美避开了顺序表插入删除时需要挪动数据的缺点。
我们再来看链表,链表又有多种结构,我们介绍一种使用单链表来完成的,插入和删除都只有O(1)。
使用这种结构即可完成,最初栈顶和栈底都为空,插入第一个元素后,栈顶栈底都指向第一个元素,每次插入新元素后,只需将其赋给栈顶,删除时,先删除节点,再把栈顶指向栈顶的next即可。
如果用尾做栈顶,那么使用双向链表最好,如果使用头做栈顶,那么单链表最好,插入删除都是O(1)。
知道了两种结构完成栈的优缺点,大家会选择哪一个呢?我们这里选择数组来完成栈,因为使用数组的话各方面效率都会更优,这里知识和预加载有关,这里就不多介绍,我们举个例子,使用数组和链表的区别,学生时代家里每个月都会给你打生活费,数组就像是一个月给你打一次生活费,够你一个月花,链表就像每天给你打一次,很明显,使用数组要比链表效率高。
决定好了用什么来实现栈,接着我们来完成栈的结构和各类接口
栈的结构typedef int STDataType;
typedef struct Stack {
STDataType* a;
int top;
int capacity;
}ST;
我们将其写为动态栈,即a不直接写为数组,不然就写死了,并且我们使用top来记录栈顶位置
接着我们来完成栈的初始化
栈的初始化void StackInit(ST* ps)//初始化
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL) {
printf("malloc fail\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0;
}
这里的top选取0还是-1看自己喜欢,选取-1代表的是top就是当前栈顶元素,选取0表示top为栈顶元素的后一位,我这里选择初始赋0,同时,如果初始化失败,我们输出提示并结束程序
栈的销毁void StackDestory(ST* ps)//销毁
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
入栈void StackPush(ST* ps, STDataType x)//入栈
{
assert(ps);
if (ps->top == ps->capacity) {//满了扩容
STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));
if (tmp == NULL) {
printf("realloc fail\n");
exit(-1);
}
else {
ps->a = tmp;
ps->capacity *= 2;
}
}
ps->a[ps->top] = x;
ps->top++;
}
入栈时我们要判断栈是否满了,满了需要扩容,扩容时我们扩容到原来的二倍,并且要判断是否扩容失败,失败的话我们给出提示并且结束程序,入栈后要记得给top+1
出栈void StackPop(ST* ps)//出栈
{
assert(ps);
assert(ps->top>0);
ps->top--;
}
出栈我们直接使top-1,有人可能会先让栈顶元素置为0,但是这句是不需要的,因为入栈元素有时候本身就可能为0,并且我们不能随意出栈,比如top为0时,即空栈,所以我们加一句断言
返回栈顶元素STDataType StackTop(ST* ps)//返回栈顶元素
{
assert(ps);
assert(ps->top >0);
return ps->a[ps->top - 1];
}
和出栈同理,栈为空时不能返回,我们加一句断言
计算数据个数void StackSize(ST* ps)//计算数据个数
{
assert(ps);
return ps->top;
}
判断是否为空bool StackEmpty(ST* ps)//判断是否为空
{
assert(ps);
return ps->top == 0;
}
我们直接return ps->top==0,使用bool类型,如果top==0,为真,返回true,否则返回false
我们来测试一下我们的代码
可以看到,没有问题。
完成了栈,我们接着来看队列
队列的选择和栈一样,队列也需要在数组和链表里选择一个,队列只允许在它的一端插入数据,在另一端删除数据
这次其实我们要选择链表,因为数组会出现假溢出现象,比如这个数组可以放5个元素,现在已经放了四个,我们出队列一个元素,再入队列一个,就会发现数组下标0的位置是空的,但队列却显示已满,如果要挪动数据的话也非常麻烦,还有一种办法就是写成循环队列,但那是后续的事情,而且也有局限性。选取链表的话,用单链表就可以解决,采用上图结构,队列就只需要尾插和头删,效率都很高。
选择好了用什么来实现队列后,我们来设计队列的结构吧
队列的结构typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
我们设计好队列的节点,然后采取两个指针,队头和队尾来设计队列即可。而且采用这样的结构可以避免使用二级指针(后续接口如果参数传QNode的话是需要二级指针的)
入队列void QueuePush(Queue* pq, QDataType x)//入队列
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) {
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL) {
pq->head = pq->tail = newnode;
}
else {
pq->tail->next = newnode;
pq->tail = newnode;
}
}
入队列是在队尾入元素,入队列我们需要判断队列是否为空,入队列需要一个新节点,我们给节点申请一个空间,申请失败我们输出提示并且报错,然后将x赋给节点的data,节点的next置为空,我们还要判断队列是否有节点,如果一个没有,那么队头和队尾都指向这个新节点,否则队尾的next指向新节点,再把队尾指向新节点
出队列void QueuePop(Queue* pq)//出队列
{
assert(pq);
assert(pq->head);
if (pq->head->next == NULL) {
free(pq->head);
pq->head = pq->tail = NULL;
}
else {
Queue* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
出队列是出队头元素,我们要进行断言,我们出队列需要把头节点的空间释放,然后让head指向它的next,但在释放前我们需要把head的next保存起来,否则我们就找不到next了,这是正常情况,如果队列只有一个元素,出队列后会造成tail的野指针现象,所以我们需要额外判断,所以我们把出队列分为队列有一个元素和多个元素的情况
判断是否为空bool QueueEmpty(Queue* pq)//判断是否为空
{
assert(pq);
return pq->head == NULL;
}
我们直接返回该判断语句即可,为空返回true,否则返回false
取队头元素QDataType QueueFront(Queue* pq)//取队头元素
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
我们直接返回队头元素即可,最好再加一句断言,有时候会很有用,比如初始化后,队列里没有元素,这种情况没有第二句断言我们就不知道程序哪里出现了错误
取队尾元素QDataType QueueBack(Queue* pq)//取队尾元素
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
取队尾元素与取队头元素同理
计算数据个数int QueueSize(Queue* pq)//计算数据个数
{
assert(pq);
int size = 0;
QNode* cur = pq->head;
while (cur) {
cur = cur->next;
size++;
}
return size;
}
这个接口有人可能会把循环条件写成cur!=pq->tail,但这样是错误的,会少计算一个元素
队列的销毁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;
}
销毁需要注意先要保存cur的下一个,不然会找不到,然后就是最后要把head和tail置为空
我们最后再来测试一下代码
没有问题,到这里,我们的栈和队列以及他们的接口就全部实现了,希望大家可以有所收获,下面我会附上全部代码
#pragma once
//Stack.h
#include#include#include
#includetypedef int STDataType;
typedef struct Stack {
STDataType* a;
int top;
int capacity;
}ST;
void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//销毁
void StackPush(ST* ps,STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//返回栈顶元素
void StackSize(ST* ps);//计算数据个数
bool StackEmpty(ST* ps);//判断是否为空
//Stack.c
#include "Stack.h"
void StackInit(ST* ps)//初始化
{
assert(ps);
ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
if (ps->a == NULL) {
printf("malloc fail\n");
exit(-1);
}
ps->capacity = 4;
ps->top = 0;
}
void StackDestory(ST* ps)//销毁
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{
assert(ps);
if (ps->top == ps->capacity) {//满了扩容
STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));
if (tmp == NULL) {
printf("realloc fail\n");
exit(-1);
}
else {
ps->a = tmp;
ps->capacity *= 2;
}
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)//出栈
{
assert(ps);
assert(ps->top>0);
ps->top--;
}
STDataType StackTop(ST* ps)//返回栈顶元素
{
assert(ps);
assert(ps->top >0);
return ps->a[ps->top - 1];
}
void StackSize(ST* ps)//计算数据个数
{
assert(ps);
return ps->top;
}
bool StackEmpty(ST* ps)//判断是否为空
{
assert(ps);
return ps->top == 0;
}
#pragma once
//Queue.h
#include#include#include
#includetypedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
void QueueInit(Queue* pq);//初始化
void QueueDestory(Queue* pq);//销毁
void QueuePush(Queue* pq, QDataType x);//入队列
void QueuePop(Queue* pq);//出队列
QDataType QueueFront(Queue* pq);//取队头元素
QDataType QueueBack(Queue* pq);//取队尾元素
int QueueSize(Queue* pq);//取数据个数
bool QueueEmpty(Queue* pq);//判断是否为空
//Queue.c
#include"Queue.h"
void QueueInit(Queue* pq)//初始化
{
assert(pq);
pq->head = 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, QDataType x)//入队列
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) {
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL) {
pq->head = pq->tail = newnode;
}
else {
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)//出队列
{
assert(pq);
assert(pq->head);
if (pq->head->next == NULL) {
free(pq->head);
pq->head = pq->tail = NULL;
}
else {
Queue* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
QDataType QueueFront(Queue* pq)//取队头元素
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
QDataType QueueBack(Queue* pq)//取队尾元素
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
int QueueSize(Queue* pq)//计算数据个数
{
assert(pq);
int size = 0;
QNode* cur = pq->head;
while (cur) {
cur = cur->next;
size++;
}
return size;
}
bool QueueEmpty(Queue* pq)//判断是否为空
{
assert(pq);
return pq->head == NULL;
}
//test.c
#include "Stack.h"
#include"Queue.h"
void testStack() {
ST st;
StackInit(&st);
StackPush(&st, 1);
StackPush(&st, 2);
StackPush(&st, 3);
StackPush(&st, 4);
while (!StackEmpty(&st)) {
printf("%d ", StackTop(&st));
StackPop(&st);
}
StackDestory(&st);
}
void testQueue() {
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
while (!QueueEmpty(&q)) {
printf("%d ", QueueFront(&q));
QueuePop(&q);
}
QueueDestory(&q);
}
int main() {
//testStack();
testQueue();
return 0;
}
如有错误,还请指正。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流