当前位置:   article > 正文

C语言数据结构——栈和队列_c语言7-7 栈与队列-stack、queue与string小综合c语言输入样例: thisisat

c语言7-7 栈与队列-stack、queue与string小综合c语言输入样例: thisisatest s

目录

1、栈

1.1 栈的概念及结构

1.2 栈的基本操作

1.3 栈基本操作的实现

1.3.1 顺序表和链表的优缺点比较

1.3.2 动态顺序表实现栈的基本操作——源码

2、队列

2.1 队列的概念及结构

2.2 队列的基本操作

2.3 队列基本操作的实现

2.3.1单链表实现队列的基本操作——源码


1、栈

1.1 栈的概念及结构

概念: 

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫出栈。出数据也在栈顶。 

注意:

(1)通常称入栈(插入)、出栈(删除)的这一端为栈顶(top),另一端称为栈底(Bottom)。

(2)当线性表中没有元素时叫称为空栈。

(3)栈为后进先出(Last In First Out)的线性表,简称为LIFO表。

栈的结构: 如下图。

1.2 栈的基本操作

(1)StackInit(Stack* ps):构造一个空栈。

(2)StackEmpty(Stack* ps):判栈空。若为空栈,则返回TRUE,否则返回FALSE。

(3)StackPush(Stack* ps, StackDataType x):进栈。将元素 x 插入到栈顶。

(4)StackPop(Stack* ps):退栈。若栈非空,则将栈顶的元素删去。

(5)StackTop(Stack* ps):取栈顶的元素。若栈非空,则返回栈顶元素,但不改变栈的状态。

(6)StackSize(Stack* ps):栈的大小。也就是栈的元素个数。

(7)StackDestroy(Stack* ps):销毁栈。栈使用结束,就对栈进行销毁。

1.3 栈基本操作的实现

实现栈的基本运算选择的是顺序表呢?还是链表呢?两者都是可以实现的,但是为了选择更为优一些的方法。首先,来对比一下这两个的优缺点,再做抉择。 

1.3.1 顺序表和链表的优缺点比较

顺序表:

优点:

(1)支持随机访问(用小标访问)。需要随机访问的结构支持的算法可以很好的适用/

(2)CPU高速缓存命中率更高(不懂需要百度)。

缺点:

(1)头部中部插入删除时间效率低(原因:删除头部,还要把头部后面的全部数据往前移)。O(N)

(2)连续的物理空间,空间不够了以后需要增容。

        a.增容有一定程度消耗。

        b.为了避免频繁增容,一般我们都是按倍数去增容,用不完可能存在一定的空间浪费。

链表(带头双向循环链表):

优点:

(1)任意位置插入删除效率高。O(1)

(2)按需申请释放空间。

缺点:

(1)不支持随机访问。(不支持用下标访问)意味着:一些排序、二分查找等在这种结构不适合用。

(2)链表存储一个值,同时要存储链接指针,有一定的消耗。

(3) CPU高速缓存命中率更低(不懂需要百度)。

对比以上顺序表和链表的优缺点,基本上可以判断出顺序表实现栈更为优一些。栈的性质就是先进后出。假设顺序表和链表实现栈的结构如下图: 

 两者都是尾插和尾删。时间复杂度基本一致。链表(带头双向循环)唯一缺点就是CPU高速缓存命中率更低。基于这一缺点,这里我们是使用顺序表来实现栈的基本操作。

1.3.2 动态顺序表实现栈的基本操作——源码

 stack.h

  1. #pragma once
  2. #include<stdlib.h>
  3. #include<stdio.h>
  4. #include<assert.h>
  5. #include<stdbool.h>
  6. typedef int StackDataType;//栈的数据类型
  7. //顺序栈类型的定义----动态顺序表
  8. typedef struct Stack
  9. {
  10. StackDataType* data; //栈的数据
  11. int top; //栈顶
  12. int capacity; //栈的容量
  13. }Stack;
  14. //栈基本操作的接口函数
  15. void StackInit(Stack* ps);//初始化栈---构造一个空栈
  16. void StackDestroy(Stack* ps);//销毁栈
  17. void StackPush(Stack* ps, StackDataType x);//栈顶插入一个元素---进栈
  18. void StackPop(Stack* ps);//删除栈的元素---退栈/出栈
  19. StackDataType StackTop(Stack* ps);//取栈顶的数据
  20. int StackSize(Stack* ps);//栈的元素个数
  21. bool StackEmpty(Stack* ps);//判空

stack.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "stack.h"
  3. void StackInit(Stack* ps)//初始化栈---构造一个空栈
  4. {
  5. assert(ps);
  6. ps->data = NULL;
  7. ps->top = 0; // top=-1,top指向栈顶的数据;top=0,top指向栈顶的下一个
  8. ps->capacity = 0;//栈的初始容量
  9. }
  10. void StackDestroy(Stack* ps)//销毁栈
  11. {
  12. assert(ps);
  13. free(ps->data); //释放内存
  14. ps->capacity = 0;
  15. ps->top = 0;
  16. }
  17. void StackPush(Stack* ps, StackDataType x)//栈顶插入一个元素---进栈
  18. {
  19. assert(ps);
  20. if (ps->capacity == ps->top)//开辟内存及其内存空间不足扩容
  21. {
  22. ps->capacity = ps->capacity == 0 ? 6 : ps->capacity * 2;
  23. StackDataType* temp = (StackDataType*)realloc(ps->data, sizeof(StackDataType) * ps->capacity);
  24. if (temp == NULL)
  25. {
  26. printf("realloc fail\n");
  27. exit(-1);//开辟失败异常退出
  28. }
  29. ps->data = temp;
  30. }
  31. ps->data[ps->top] = x;//插入元素数据
  32. ps->top++;//指向栈顶元素的下一个
  33. }
  34. void StackPop(Stack* ps)//删除栈的元素---退栈/出栈
  35. {
  36. assert(ps);
  37. assert(!StackEmpty(ps));//栈为空就不能删除了
  38. ps->top--;//开始删除
  39. }
  40. StackDataType StackTop(Stack* ps)//取栈顶的数据
  41. {
  42. assert(ps);
  43. assert(!StackEmpty(ps));//当栈为空时不取出数据
  44. return ps->data[ps->top - 1];//因为top是指向栈顶数据的下一个,所以 top-1 才能取到栈顶的元素
  45. }
  46. int StackSize(Stack* ps)//栈的元素个数
  47. {
  48. return ps->top;
  49. }
  50. bool StackEmpty(Stack* ps)//判空---判断栈是否为空
  51. {
  52. assert(ps);
  53. //if (ps->top == 0)
  54. //{
  55. // return true;
  56. //}
  57. //else
  58. //{
  59. // return false;
  60. //}
  61. return ps->top == 0;//当 top初始化为-1是需要写为 return ps->top==-1;
  62. }

test.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"stack.h"
  3. void TestStack()
  4. {
  5. Stack st;
  6. StackInit(&st);
  7. StackPush(&st, 1);//插入数据
  8. StackPush(&st, 2);
  9. StackPush(&st, 3);
  10. StackPush(&st, 4);
  11. StackPush(&st,5);
  12. //后进先出原则:5 4 3 2 1
  13. //栈是不能像顺序表、链表那样直接随便取数据
  14. //取栈的数据只有一个方式,就是需要把栈顶的数据取出来了才能取下一个,才符合栈的性质
  15. while (!StackEmpty(&st))
  16. {
  17. printf("%d ", StackTop(&st));//先取栈顶的数据
  18. //取了栈顶的数据,想要取栈顶的下一个数据,把当前的栈顶的数据 pop 掉,就是删除掉
  19. //才能取下一个栈的数据
  20. StackPop(&st);
  21. }
  22. StackDestroy(&st);//销毁栈
  23. }
  24. int main()
  25. {
  26. TestStack();
  27. return 0;
  28. }

2、队列

2.1 队列的概念及结构

概念: 

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

注意:

(1)允许删除的一端称为队头(Front)。

(2)允许插入的一端称为队尾(Back)。

(3)当队列中没有元素时称为空队列。

(4)队列亦称做先进先出(First In First Out)的线性表,简称为 FIFO 表。

 队列的结构:如下图。

2.2 队列的基本操作

(1)QueueInit(Queue* pq):置空队列。构造一个空队列。

(2)QueueEmpty(Queue* pq):判队空。若队列为空,则返回真值,否则返回假值。

(3)QueuePush(Queue* pq, QueueDataType x):将元素 x 插入队列的队尾,此操作简称为入队。

(4)QueuePop(Queue* pq):若队列非空,则删去队列的队头元素,此操作简称为出队。

(5)QueueFront(Queue* pq):取队头元素数据。若队列非空,则返回队头元素,但不改变队列的状态。

(6)QueueBack(Queue* pq):取队尾元素。若队列非空,则返回队尾元素,但不改变队列的状态。

(7)QueueSize(Queue* pq):队列元素个数。

(8)QueueDestroy(Queue* pq):销毁队列。

2.3 队列基本操作的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。出一个队头还需要把后面的元素往前移,时间复杂度O(N)。基于顺序表的这一缺点,这里采用单链表实现队列。
入队:

出队:

2.3.1单链表实现队列的基本操作——源码

queue.h 

  1. #pragma once
  2. #include<stdlib.h>
  3. #include<stdio.h>
  4. #include<assert.h>
  5. #include<stdbool.h>
  6. typedef int QueueDataType;//队列元素的数据类型
  7. //链表队列类型定义
  8. typedef struct QueueNode
  9. {
  10. struct QueueNode* next;
  11. QueueDataType data;
  12. }QueueNode;
  13. typedef struct Queue
  14. {
  15. QueueNode* head; //队列头指针
  16. QueueNode* tail; //队列尾指针
  17. }Queue;
  18. //队列基本操作的函数接口
  19. void QueueInit(Queue* pq);//初始化队列--置空队列。构造一个空队列。
  20. void QueueDestroy(Queue* pq);//销毁队列
  21. void QueuePush(Queue* pq, QueueDataType x);//向队尾插入一个元素---入队列
  22. void QueuePop(Queue* pq);//删除队头元素
  23. QueueDataType QueueFront(Queue* pq);//取队头元素
  24. QueueDataType QueueBack(Queue* pq);//取队尾元素
  25. int QueueSize(Queue* pq);//队列元素个数
  26. bool QueueEmpty(Queue* pq);//判空--判断队列是否为空

 queue.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"queue.h"
  3. void QueueInit(Queue* pq)//初始化队列--置空队列。构造一个空队列。
  4. {
  5. assert(pq);
  6. pq->head = NULL;
  7. pq->tail = NULL;
  8. }
  9. void QueueDestroy(Queue* pq)//销毁队列
  10. {
  11. assert(pq);
  12. QueueNode* current = pq->head;//记录头指针
  13. while (current)
  14. {
  15. QueueNode* next = current->next;//记录头指针的下一个结点的指针
  16. free(current);
  17. current = next;
  18. }
  19. pq->head = pq->tail = NULL;//释放结束将头尾指针置空
  20. }
  21. void QueuePush(Queue* pq, QueueDataType x)//向队尾插入一个元素---入队列
  22. {
  23. assert(pq);
  24. QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
  25. newnode->data = x;
  26. newnode->next = NULL;
  27. //尾插
  28. if (pq->head == NULL)//第一次入队
  29. {
  30. pq->head = pq->tail = newnode;
  31. }
  32. else
  33. {
  34. pq->tail->next = newnode;
  35. pq->tail = newnode;//更新尾指针
  36. }
  37. }
  38. void QueuePop(Queue* pq)//删除队头元素
  39. {
  40. assert(pq);
  41. assert(!QueueEmpty(pq));
  42. //开始头删
  43. QueueNode* headNext = pq->head->next;
  44. free(pq->head);
  45. pq->head = headNext;
  46. if (pq->head == NULL)//当把队列删空了,不仅要把 head 置空,还要把 tail 置空
  47. {
  48. pq->tail = NULL;
  49. }
  50. }
  51. QueueDataType QueueFront(Queue* pq)//取队头元素
  52. {
  53. assert(pq);
  54. assert(!QueueEmpty(pq));//队列非空就取
  55. return pq->head->data;
  56. }
  57. QueueDataType QueueBack(Queue* pq)//取队尾元素
  58. {
  59. assert(pq);
  60. assert(!QueueEmpty(pq));//队列非空就取
  61. return pq->tail->data;
  62. }
  63. int QueueSize(Queue* pq)//队列元素个数
  64. {
  65. //遍历队列,进行计数,返回出结果
  66. assert(pq);
  67. int count = 0;
  68. QueueNode* current = pq->head;
  69. while (current)
  70. {
  71. ++count;
  72. current = current->next;
  73. }
  74. return count;
  75. }
  76. bool QueueEmpty(Queue* pq)//判空--判断队列是否为空
  77. {
  78. assert(pq);
  79. return pq->head == NULL;//队列为空就为真,否则为假
  80. }

 test.c

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include"queue.h"
  3. void TestQueue()
  4. {
  5. Queue qu;
  6. QueueInit(&qu);
  7. QueuePush(&qu, 1);//入队
  8. QueuePush(&qu, 2);
  9. QueuePush(&qu, 3);
  10. QueuePush(&qu, 4);
  11. QueuePush(&qu, 5);
  12. //出队-----先进先出----1 2 3 4 5
  13. //取队头的元素,想要取出队头的下一个元素,将队头 Pop,才能取下一个
  14. while (!QueueEmpty(&qu))
  15. {
  16. QueueDataType front = QueueFront(&qu);
  17. printf("%d ", front);
  18. QueuePop(&qu);
  19. }
  20. printf("\n");
  21. QueueDestroy(&qu);//销毁队列
  22. }
  23. int main()
  24. {
  25. TestQueue();
  26. return 0;
  27. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/975676
推荐阅读
相关标签
  

闽ICP备14008679号