深圳幻海软件技术有限公司 欢迎您!

【数据结构】栈和队列

2023-09-05

  🔥博客主页:小王又困了📚系列专栏:数据结构🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️目录一、栈 1.1栈的概念1.2栈的结构二、栈的实现📒2.1栈的初始化📒2.2进栈📒2.3出栈 📒2.4读取栈顶元素📒2.5判断

 

 🔥博客主页:小王又困了

📚系列专栏:数据结构

🌟人之为学,不日近则日退 

❤️感谢大家点赞👍收藏⭐评论✍️


目录

一、栈 

1.1栈的概念

1.2栈的结构

二、栈的实现

📒2.1栈的初始化

📒2.2进栈

📒2.3出栈 

📒2.4读取栈顶元素

📒2.5判断栈空

📒2.6栈的销毁

三、队列

3.1队列的概念

3.2队列的结构

 四、队列的实现

📒4.1队列的定义

📒4.2队列的初始化

📒4.3入队

📒4.4出队

📒4.5获取队头元素

📒4.6获取队尾元素

📒4.7判断空队列

📒4.8队列的销毁


🗒️前言:

在前几期的学习中,我们认识了顺序表和链表这两种线性表,而在本期学习中,我们将会认识别的线性表。跟随我们的脚本,看看栈和队列有怎样的特点。

一、栈 

1.1栈的概念

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

  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
  • 出栈:栈的删除操作叫做出栈。出数据也在栈顶。

1.2栈的结构

二、栈的实现

📒2.1栈的初始化

我们将结构体的所有元素都初始化为0。这里与我们在顺序表中的初始化不同,在顺序表中我们在初始化时就开辟了空间,下面我们会介绍另一种方式。

  1. void STInit(ST* ps)
  2. {
  3. assert(ps);
  4. ps->arr = NULL;
  5. ps->capacity = ps->top = 0;
  6. }

📒2.2进栈

在进栈时可能遇到容量为零,所以我们使用一个条件判断,来确定容量。因为top为0,所以它表示的是下一个元素的下标,要先赋值,再top++

  1. void STpush(ST* ps, STDateType* x)
  2. {
  3. assert(ps);
  4. if (ps->capacity == ps->top)
  5. {
  6. int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
  7. STDateType* tmp= (STDateType*)realloc(ps->arr, sizeof(STDateType) * newcapacity);
  8. if (tmp == NULL)
  9. {
  10. perror("realloc");
  11. exit(-1);
  12. }
  13. ps->arr = tmp;
  14. ps->capacity = newcapacity;
  15. }
  16. ps->arr[ps->top] = x;
  17. ps->top++;
  18. }

malloc 和 realloc 开辟空间的区别就是 realloc 要传递一个指针,而当我们给 realloc 传递一个空指针,那么它的功能就和 malloc 相同。 

📒2.3出栈 

出栈只需要将 top --就访问不到这个元素了。在出栈时我们要判断栈中是否还有元素。

  1. void STPop(ST* ps)
  2. {
  3. assert(ps);
  4. assert(ps->top > 0);
  5. ps->top--;
  6. }

📒2.4读取栈顶元素

栈顶元素就是我们插入的最后一个元素。由于top表示的是下一个元素的下标,所以读取栈顶元素是top要减1。

  1. STDateType STTop(ST* ps)
  2. {
  3. assert(ps);
  4. assert(ps->top > 0);
  5. return ps->arr[ps->top - 1];
  6. }

📒2.5判断栈空

  1. bool STEmpty(ST* ps)
  2. {
  3. assert(ps);
  4. return ps->top == 0;
  5. }

📒2.6栈的销毁

这里使用的内存是动态开辟的,因此在我们使用完后要及时释放掉内存,否则会造成内存泄漏。

  1. void STDestory(ST* ps)
  2. {
  3. assert(ps);
  4. free(ps->arr);
  5. ps->arr = NULL;
  6. ps->capacity = ps->top = 0;
  7. }

三、队列

3.1队列的概念

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

  • 入队列:进行插入操作的一端称为队尾
  • 出队列:进行删除操作的一端称为队头

3.2队列的结构

 四、队列的实现

📒4.1队列的定义

在入队时相当于尾插,我们可以定义一个尾指针来记录尾的位置。这就使我们传指针时,要传递两个指针,我们可以把指针放到结构体中,这样在插入第一个时也可以解决要传递二级指针的问题。

定义尾指针可以避免每次尾插时要遍历一遍链表。

  1. typedef int QDateType;
  2. typedef struct QueueNode
  3. {
  4. struct QueueNode* next;
  5. QDateType date;
  6. }QNode;
  7. typedef struct Queue
  8. {
  9. QNode* head;
  10. QNode* tail;
  11. int size;
  12. }Que;

📒4.2队列的初始化

这里的 size 用来记录队列中数据的个数。

  1. void QueueInit(Que* ps)
  2. {
  3. ps->head = ps->tail = NULL;
  4. ps->size = 0;
  5. }

📒4.3入队

入队相当于尾插,在入队时我们要考虑链表是否为空。

  1. void QueuePush(Que* ps, QDateType* x)
  2. {
  3. assert(ps);
  4. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  5. if (newnode == NULL)
  6. {
  7. perror("malloc");
  8. exit(-1);
  9. }
  10. newnode->date = x;
  11. newnode->next = NULL;
  12. if (ps->tail == NULL)
  13. {
  14. ps->head = ps->tail = newnode;
  15. }
  16. else
  17. {
  18. ps->tail->next = newnode;
  19. ps->tail = newnode;
  20. }
  21. ps->size++:
  22. }

📒4.4出队

出队相当于头删,与之前不同的是,当我们删除最后一个节点,还要记得处理尾指针。

  1. void QueuePop(Que* ps)
  2. {
  3. assert(ps);
  4. assert(!QueueEmpty(ps));
  5. if (ps->head->next == NULL)
  6. {
  7. free(ps->head);
  8. ps->head = ps->tail = NULL;
  9. }
  10. else
  11. {
  12. QNode* next = ps->head->next;
  13. free(ps->head);
  14. ps->head = next;
  15. }
  16. ps->size--;
  17. }

📒4.5获取队头元素

队头元素就是头指针指向的节点的数据域。

  1. QDateType QueueFront(Que* ps)
  2. {
  3. assert(ps);
  4. assert(!QueueEmpty(ps));
  5. return ps->head->date;
  6. }

📒4.6获取队尾元素

我们通过尾指针可以直接找到队尾,不用遍历链表。

  1. QDateType QueueBack(Que* ps)
  2. {
  3. assert(ps);
  4. assert(!QueueEmpty(ps));
  5. return ps->tail->date;
  6. }

📒4.7判断空队列

利用bool的函数判断队列是否为空,当尾指针为空时,返回true;当尾指针不为空时,返回false。

  1. bool QueueEmpty(Que* ps)
  2. {
  3. assert(ps);
  4. return ps->tail==NULL
  5. }

📒4.8队列的销毁

我们在销毁时,要考虑尾指针,要及时将尾指针置为空,否则会造成内存泄漏。

  1. void QueueDestroy(Que* ps)
  2. {
  3. assert(ps);
  4. QNode* cur=ps->head;
  5. while(cur)
  6. {
  7. QNode* next=cur->next;
  8. free(cur):
  9. cur=next;
  10. }
  11. ps->head=ps->tail=NULL:
  12. ps->size=0;
  13. }

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览51123 人正在系统学习中