赞
踩
目录
这篇文章是关于栈和队列的C语言实现,干货满满,大家可以边看边手写代码,最后附上栈和队列的参考代码。
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
这个数据结构可以理解为炸串。在最开始,插入丸子时,要插到这根签的最底部,就是入栈。开始吃的时候,一般从最上面的丸子入手,就是出栈。
我们学过顺序表和链表两种数据结构,用那种会更好呢?
在用C语言实现栈各种接口函数,需要创建三个文件,分别是STtest.c,Stack.c和Stack.h。
- typedef int STDataType;
- // 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
- typedef int STDataType;
- #define N 10
- typedef struct Stack
- {
- STDataType _a[N];
- int _top; // 栈顶
- }Stack;
-
- // 支持动态增长的栈
- typedef struct Stack
- {
- STDataType* a;
- int top; //栈顶
- int capacity; //容量
- }ST;
-
- //初始化栈
- void StackInit(ST* ps);
- //销毁栈
- void StackDestroy(ST* ps);
- //压栈
- void StackPush(ST* ps, STDataType x);
- //出栈
- void StackPop(ST* ps);
- //获取栈顶元素
- STDataType StackTop(ST* ps);
- //获取栈中有效元素个数
- int StackSize(ST* ps);
- //检测栈是否为空,为空返回true
- bool StackEmpty(ST* ps);
初始化栈一开始要断言,断言来判断ps指针是否为空,如果为空会直接终止程序并给出提示,方便之后的调试,之后的断言不再赘述。指针指向空,内部整型数据赋值为0。但是top得注意,top其实也是个下标。看下面的注释,top可以有两种赋值方式,只是表示不同意思。
- void StackInit(ST* ps)
- {
- assert(ps);
- ps->a = NULL;
- ps->top = 0;//表示指向栈顶的下一个元素
- //ps->top = -1 ///表示指向栈顶元素
- ps->capacity = 0;
- }
因为我们是以数组为基础,是动态开辟的连续空间,直接释放掉ps中的数组指针,并要置为空。
- void StackDestroy(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)
- {
- int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
- STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
- if (tmp == NULL)
- {
- printf("realloc fail\n");
- exit(-1);
- }
-
- ps->a = tmp;
- ps->capacity = newCapacity;
- }
-
- ps->a[ps->top] = x;
- ps->top++;
- }
这次不仅要判断ps是否不为空指针,还要判断栈内是否有元素,这样才可以删除元素,让栈顶减一即可。
- void StackPop(ST* ps)
- {
- assert(ps);
- assert(!StackEmpty(ps));
-
- ps->top--;
- }
这里的判断跟上个接口函数一样,只有栈内有元素,才能取栈顶元素。
- STDataType StackTop(ST* ps)
- {
- assert(ps);
- assert(!StackEmpty(ps));
-
- return ps->a[ps->top - 1];
- }
此时的top表示的是栈顶的下一个元素的下标,刚好就是元素个数,直接返回栈顶top
- int StackSize(ST* ps)
- {
- assert(ps);
-
- return ps->top;
- }
很多人会写个if语句判断,其实可以直接像下面一样写,ps->top == 0变成一个判断语句,如果为真返回一个部位0的数,如果为假返回0。
- bool StackEmpty(ST* ps)
- {
- assert(ps);
-
- return ps->top == 0;
- }
我们写两个测试函数,第一个测试函数,是出栈和入栈的操作,可以调试查看。第二个测试函数是栈的数据打印,需要使用StackTop获取栈顶元素,然后将打印的数据出栈。
- void TestStack1()
- {
- ST st;
- StackInit(&st);
-
- StackPush(&st, 1);
- StackPush(&st, 2);
- StackPush(&st, 3);
- StackPush(&st, 4);
-
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
-
- StackDestroy(&st);
- }
-
- void TestStack2()
- {
- 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);
- }
- StackDestroy(&st);
- }
-
- int main()
- {
- TestStack1();
- TestStack2();
-
- return 0;
- }
输出结果:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
这个数据结构可以理解为在银行取号,先取号的人,就是入队列。然后先取到号的人,就先去窗口处理业务,这就是出队列。
跟栈一样,队列也可以用顺序表和链表来实现,那哪一个更好呢?
链表的数据结构存储的是一个数据变量,和指向下一个结点的指针变量。我们入队列和出队列分别在队尾和队头,所以我们在队列的数据结构定义两个链表的指针变量head和tail,表示队头和队尾
跟栈的实现一样,首先要创建三个文件Qtest.c,Queue.c和Queue.h。
- // 链式结构:表示队列
- typedef int QDataType;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QDataType data;
- }QueueNode;
-
- // 队列的结构
- typedef struct Queue
- {
- QueueNode* head;
- QueueNode* tail;
- }Queue;
-
- //初始化队列
- void QueueInit(Queue* pq);
- //销毁队列
- void QueueDestroy(Queue* pq);
- //队尾入队列
- void QueuePush(Queue* pq, QDataType x);
- //队头出队列
- void QueuePop(Queue* pq);
- //获取队列头部元素
- QDataType QueueFront(Queue* pq);
- //获取队列尾部元素
- QDataType QueueBack(Queue* pq);
- //获取队列中有效元素个数
- int QueueSize(Queue* pq);
- //检测队列是否为空,如果为空返回true
- bool QueueEmpty(Queue* pq);
队列初始化,只需要将队头链表指针和队尾链表指针置为空指针就好。
- void QueueInit(Queue* pq)
- {
- assert(pq);
- pq->head = NULL;
- pq->tail = NULL;
- }
因为是以链表为基础,需要用while循环释放掉每一个动态开辟的结点。
- void QueueDestroy(Queue* pq)
- {
- assert(pq);
- QueueNode* cur = pq->head;
- while (cur != NULL)
- {
- QueueNode* next = cur->next;
- free(cur);
- cur = next;
- }
-
- pq->head = pq->tail = NULL;
- }
队尾入队列就是链表的尾插,但是我们有tail指针,直接在tail后面插入就好。不过需要分是否为空队列两种情况,如果是空队列,head和tail指针都指向新结点,如果不是空队列,在tail之后插入。
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
- if (newnode == NULL)
- {
- perror("Queue malloc fail");
- return;
- }
- newnode->data = 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(!QueueEmpty(pq));
-
- QueueNode* next = pq->head->next;
- free(pq->head);
- pq->head = next;
- }
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->head->data;
- }
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->tail->data;
- }
这需要一个计数变量。
- int QueueSize(Queue* pq)
- {
- assert(pq);
-
- int n = 0;
- QueueNode* cur = pq->head;
- while (cur)
- {
- n++;
- cur = cur->next;
- }
-
- return n;
- }
跟栈判空相同。
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
- return pq->head == NULL;
- }
写两个测试函数,第一是入队列和出队列的操作,可以一步一步调试看情况。第二个测试函数,是打印队列里的数据,现获取队头的数据,然后再出队。
- void TestQueue1()
- {
- Queue q;
- QueueInit(&q);
- QueuePush(&q, 1);
- QueuePush(&q, 2);
- QueuePush(&q, 3);
- QueuePush(&q, 4);
-
- QueuePop(&q);
- QueuePop(&q);
- QueuePop(&q);
- QueuePop(&q);
-
- printf("%d\n", QueueBack(&q));
-
- QueueDestroy(&q);
- }
-
- void TestQueue2()
- {
- Queue q;
- QueueInit(&q);
- QueuePush(&q, 1);
- QueuePush(&q, 2);
- QueuePush(&q, 3);
- QueuePush(&q, 4);
-
- while (!QueueEmpty(&q))
- {
- QDataType front = QueueFront(&q);
- printf("%d ", front);
- QueuePop(&q);
- }
- printf("\n");
- }
-
- int main()
- {
- TestQueue1();
- TestQueue2();
-
- return 0;
- }
输出的结果:
- #pragma once
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <stdbool.h>
-
- typedef int STDataType;
- 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
- //typedef int STDataType;
- //#define N 10
- //typedef struct Stack
- //{
- // STDataType _a[N];
- // int _top; // 栈顶
- //}Stack;
-
- // 支持动态增长的栈
- typedef struct Stack
- {
- STDataType* a;
- int top; //栈顶
- int capacity; //容量
- }ST;
- //初始化栈
- void StackInit(ST* ps);
- //销毁栈
- void StackDestroy(ST* ps);
- //压栈
- void StackPush(ST* ps, STDataType x);
- //出栈
- void StackPop(ST* ps);
- //获取栈顶元素
- STDataType StackTop(ST* ps);
- //获取栈中有效元素个数
- int StackSize(ST* ps);
- //检测栈是否为空,为空返回true
- bool StackEmpty(ST* ps);
- #include "Stack.h"
-
- void StackInit(ST* ps)
- {
- assert(ps);
- ps->a = NULL;
- ps->top = 0;//ps->top = -1
- ps->capacity = 0;
- }
-
- void StackDestroy(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)
- {
- int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
- STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
- if (tmp == NULL)
- {
- printf("realloc fail\n");
- exit(-1);
- }
-
- ps->a = tmp;
- ps->capacity = newCapacity;
- }
-
- ps->a[ps->top] = x;
- ps->top++;
- }
-
- void StackPop(ST* ps)
- {
- assert(ps);
- assert(!StackEmpty(ps));
-
- ps->top--;
- }
-
- STDataType StackTop(ST* ps)
- {
- assert(ps);
- assert(!StackEmpty(ps));
-
- return ps->a[ps->top - 1];
- }
-
- int StackSize(ST* ps)
- {
- assert(ps);
-
- return ps->top;
- }
-
- bool StackEmpty(ST* ps)
- {
- assert(ps);
-
- return ps->top == 0;
- }
- #include "Stack.h"
-
- void TestStack1()
- {
- ST st;
- StackInit(&st);
-
- StackPush(&st, 1);
- StackPush(&st, 2);
- StackPush(&st, 3);
- StackPush(&st, 4);
-
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
- StackPop(&st);
-
- StackDestroy(&st);
- }
-
- void TestStack2()
- {
- ST st;
- StackInit(&st);
-
- StackPush(&st, 1);
- StackPush(&st, 2);
- StackPush(&st, 3);
- StackPush(&st, 4);
-
- //printf("%d ", StackTop(&st));
- //StackPop(&st);
- //printf("%d ", StackTop(&st));
- //StackPop(&st);
-
- //StackPush(&st, 5);
- //StackPush(&st, 6);
-
- while (!StackEmpty(&st))
- {
- printf("%d ", StackTop(&st));
- StackPop(&st);
- }
-
- StackDestroy(&st);
- }
-
- int main()
- {
- TestStack1();
- //TestStack2();
-
- return 0;
- }
- #pragma once
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <stdbool.h>
-
- // 链式结构:表示队列
- typedef int QDataType;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QDataType data;
- }QueueNode;
-
- // 队列的结构
- typedef struct Queue
- {
- QueueNode* head;
- QueueNode* tail;
- }Queue;
-
- //初始化队列
- void QueueInit(Queue* pq);
- //销毁队列
- void QueueDestroy(Queue* pq);
- //队尾入队列
- void QueuePush(Queue* pq, QDataType x);
- //队头出队列
- void QueuePop(Queue* pq);
- //获取队列头部元素
- QDataType QueueFront(Queue* pq);
- //获取队列尾部元素
- QDataType QueueBack(Queue* pq);
- //获取队列中有效元素个数
- int QueueSize(Queue* pq);
- //检测队列是否为空,如果为空返回true
- bool QueueEmpty(Queue* pq);
- #include "Queue.h"
-
- void QueueInit(Queue* pq)
- {
- assert(pq);
- pq->head = NULL;
- pq->tail = NULL;
- }
-
- void QueueDestroy(Queue* pq)
- {
- assert(pq);
- QueueNode* cur = pq->head;
- while (cur != NULL)
- {
- QueueNode* next = cur->next;
- free(cur);
- cur = next;
- }
-
- pq->head = pq->tail = NULL;
- }
-
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
- if (newnode == NULL)
- {
- perror("Queue malloc fail");
- return;
- }
- newnode->data = 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(!QueueEmpty(pq));
-
- QueueNode* next = pq->head->next;
- free(pq->head);
- pq->head = next;
- }
-
-
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->head->data;
- }
-
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->tail->data;
- }
-
- int QueueSize(Queue* pq)
- {
- assert(pq);
-
- int n = 0;
- QueueNode* cur = pq->head;
- while (cur)
- {
- n++;
- cur = cur->next;
- }
-
- return n;
- }
-
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
- return pq->head == NULL;
- }
- void TestQueue1()
- {
- Queue q;
- QueueInit(&q);
- QueuePush(&q, 1);
- QueuePush(&q, 2);
- QueuePush(&q, 3);
- QueuePush(&q, 4);
-
- QueuePop(&q);
- QueuePop(&q);
- QueuePop(&q);
- QueuePop(&q);
-
- printf("%d\n", QueueBack(&q));
-
- QueueDestroy(&q);
- }
-
- void TestQueue2()
- {
- Queue q;
- QueueInit(&q);
- QueuePush(&q, 1);
- QueuePush(&q, 2);
- QueuePush(&q, 3);
- QueuePush(&q, 4);
-
- while (!QueueEmpty(&q))
- {
- QDataType front = QueueFront(&q);
- printf("%d ", front);
- QueuePop(&q);
- }
- printf("\n");
- }
-
- int main()
- {
- //TestQueue1();
- TestQueue2();
- return 0;
- }
栈和队列的实现其实相较于链表的实现简单一些,是因为其结构的特殊要求。之后会出一篇关于栈和队列的OJ题目。多多重复,百炼成钢!
创作不易,希望这篇文章能给你带来启发和帮助,如果喜欢这篇文章,请留下你的三连,你的支持的我最大的动力!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。