• 栈和队列知识点+例题


    1.栈

    1.1栈的概念及结构

    栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端成为栈顶,另一端成为栈底。遵守后进先出的原则(类似于弹夹)

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

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

    那如何实现栈呢?

     经过比较,数组栈是最优解,(链式的扩容会很久才会扩容一下)

    由于top的位置意义不同,我们分为两种解决方案

    1.2基本操作 

    1.定义一个栈

    1. typedef int SLDataType;
    2. typedef struct Stack
    3. {
    4. int *a;
    5. int top;
    6. int capacity;
    7. }

    2,初始化一个栈

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

    3压栈

    1. void STPush(ST* pst, SLDataType x)
    2. {
    3. assert(pst);
    4. if (pst->top == pst->capacity)
    5. {
    6. ST* newcapacity = pst->capacity == 0 ? 4 : capacity * 2;
    7. SLDataType* tmp = (SLDataType*)realloc((SLDataType)*newcapacity);
    8. if (newcapacity == NULL)
    9. {
    10. return -1;
    11. }
    12. else
    13. {
    14. pst->a = tmp;
    15. pst->capacity = newcapacity;
    16. pst->a[pst->top] = x;
    17. pst->top++;
    18. }
    19. }
    20. }

     4,弹栈

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

    5 返回栈顶元素

    1. void STTop(ST* pst)
    2. {
    3. assert(pst);
    4. assert(pst->top>0);
    5. return pst->a[pst->top-1];
    6. }

    6 判断是否为空

    1. bool STEmpty(ST* pst)
    2. {
    3. assert(pst);
    4. if (pst->top == 0)
    5. {
    6. return true;
    7. }
    8. else
    9. {
    10. return false;
    11. }
    12. }

    7 栈的大小

    1. int STSize(ST* pst)
    2. {
    3. assert(pst);
    4. return pst->top;
    5. }

    8销毁栈

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

    让我们看几道例题吧

    例题1:

     思路:栈的顺序是后进先出,有题可知,最后一个是E,所以E先出,故选B

    例题2:

     我们首先看选项,A选项:1先进,1先出,把2 3 4放进去,把4拿出来,再把3拿出来,最后把2拿出来。同理,我们看C选项,把1 2 3放进去,然后把3拿出来,然后我们会发现,如果想要拿1的话,拿2是必经之路,所以此选项错误

    例题3:

     思路:

    1,先建立一个栈,初始化一个栈,

    2,然后我们把所有的左括号放入栈里面,如果不是左括号,即是有括号;

    3,其次我们要知道,本题的关键在于数量匹配和顺序匹配。所以我们要考虑一下栈是否为空(右括号的数量大于左括号的数量),然后考虑顺序匹配的问题

    4,最后我们看栈是否为空,如果为空,就返回true,然后把栈毁掉

    1. bool isVaild(char* s)
    2. {
    3. ST st;// 定义一个栈
    4. STInit(&st);
    5. while (*s)
    6. {
    7. if (*s == '[' || *s == '{' || *s == '(')
    8. {
    9. STPush(&st, *s);
    10. s++;
    11. }
    12. else
    13. {
    14. if (STEmpty(&st))
    15. {
    16. return false;
    17. }
    18. //栈里面取左括号
    19. char top = STTop(&st);
    20. STPop(&st);
    21. //顺序不匹配
    22. if (*s == ']' && top != '[') || (8s == '}' && top != '{') || (*s == ')' && top == '(')
    23. {
    24. return false;
    25. }
    26. s++;
    27. }
    28. }
    29. //栈为空,返回真,说明数量都匹配
    30. bool ret = STEmpty(&st);
    31. STDestory(&pst);
    32. return ret;
    33. }

    好啦~栈我们就先讲到这里啦,让我们看一下队列的知识点吧

    2,队列

    2.1队列的概念和结构

     我们可以考虑一个问题

    入队之后,出队的时候顺序是唯一一定的吗?

    答案是:当然是;

    从以上我们可以了解到,栈用数组的方法比较好;而队列用单链表,头删尾插的方式比较好

    2.2基本操作

    1定义一个队列

    1. typedef int QueueType;
    2. typedef struct QueueNode
    3. {
    4. QueueType val;
    5. struct QueueNode* next;
    6. }QNode;

    为了解决二级指针以及两个指针的问题,我们可以把两个指针放入一个结构体里面,然后进行一级指针的操作即可

    1. typedef struct Queue
    2. {
    3. QNode* phead;
    4. QNode* ptail;
    5. int size;
    6. }Queue;

    2.初始化一个队列

    1. void QueueInit(Queue* pq)
    2. {
    3. assert(pq);
    4. pq->size = 0;
    5. pq->phead = pq->ptail = NULL;
    6. }

    3.插入到队列

    1. void QueuePush(Queue* pq, QDataType x)
    2. {
    3. QNode* newnode = (QNode*)malloc(sizeof(QNode));
    4. if (newnode == NULL)
    5. return -1;
    6. else
    7. {
    8. newnode->val = x;
    9. newnode->next = NULL;
    10. }
    11. if (pq->tail == NULL)
    12. {
    13. return -1;
    14. pq->tail = pq->phead = newnode;
    15. }
    16. else
    17. {
    18. pq->tail->next = newnode;
    19. pq->tail = newnode;
    20. }
    21. pq->size++;
    22. }

    4. 头删

    1. void QueuePop(Queue* pq)
    2. {
    3. assert(pq);
    4. assert(pq->phead);
    5. QNode* del = pq->phead;
    6. pq->phead = pq->phead->next;
    7. if (pq->phead = NULL)
    8. pq->tail = NULL;
    9. }

    5找头结点的值

    1. QDataType QueueFront(Queue* pq)
    2. {
    3. assert(pq);
    4. assert(pq->phead);
    5. return pq->phead->val;
    6. }

    6队列是否为空

    1. bool QueueEmpty(Queue* pq)
    2. {
    3. assert(pq);
    4. return pq->phead=NULL;
    5. }

    7队列大小

    1. int QueueSize(Queue* pq)
    2. {
    3. assert(pq);
    4. return pq->size;
    5. }

    8销毁队列

    1. void QueueDestory(Queue* pq)
    2. {
    3. assert(pq);
    4. QNode* cur = pq->phead;
    5. while (cur)
    6. {
    7. QNode* next = cur->next;
    8. cur = next;
    9. }
    10. pq->phead=pq->ptail=NULL;
    11. }

    让我们看几道关于队列和栈的例题吧

    例题1:

    思路: 

     代码实现:

    1. typedef struct
    2. {
    3. Queue q1;
    4. Queue q2;
    5. }Stack;
    6. MyStack* CreateStack()
    7. {
    8. MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    9. QueueInit(&pst->q1);
    10. QueueInit(&pst->q2);
    11. return pst;
    12. }
    13. void mystackpush(Mystack* obj, int x)
    14. {
    15. Queue Empty = &obj->q1;
    16. Queue nonEmpty =&obj->q2;
    17. if (!Empty(&obj->q1))
    18. {
    19. Queue Empty = &obj->q2;
    20. Queue nonEmpty = &obj->q1;
    21. }
    22. //开始到数据
    23. while (QueueSize(nonempty) > 1)
    24. {
    25. QueuePush(Empty, QueueFront(nonempty));
    26. QueuePop(nonempty);
    27. }
    28. int top = QueueFront(nonempty);
    29. QueuePop(nonempty);
    30. return top;
    31. }
    32. int mystackTop(Mystack* obj)
    33. {
    34. if (!Empty(&obj->q1))
    35. {
    36. return QueueBack(&obj->q1);
    37. }
    38. else
    39. {
    40. return QueueBack(&obj->q2);
    41. }
    42. }
    43. bool mystackEmpty(MyStack* obj)
    44. {
    45. return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
    46. }
    47. void mystackFree(Mystack* obj)
    48. {
    49. QueueDestory(&obj->q1);
    50. QueueDestory(&obj->q2);
    51. free(obj);
    52. }

    例题2:

     

     思路:

     代码实现:

     

    1. typedef struct
    2. {
    3. int* a;
    4. int top;
    5. int capacity;
    6. }ST;
    7. typedef struct
    8. {
    9. ST pushst;
    10. ST popst;
    11. }MyQueue;
    12. //初始化
    13. void STInit(ST* pst)
    14. {
    15. assert(pst);
    16. pst->a = NULL;
    17. pst->top = 0;
    18. pst->capacity = 0;
    19. }
    20. //压栈
    21. void STPush(ST* pst, SLDataType x)
    22. {
    23. assert(pst);
    24. if (pst->top == pst->capacity)
    25. {
    26. ST* newcapacity = (SLDataType*)malloc(sizeof(SLDataType);
    27. SLDataType* tmp = pst->capacity == 0 ? 4 : newcapacity * 2;
    28. if (newcapacity == 0)
    29. {
    30. return -1;
    31. }
    32. else
    33. {
    34. pst->a = tmp;
    35. pst->capacity = newcapacity;
    36. pst->a[pst->top] = x;
    37. pst->top++;
    38. }
    39. }
    40. }
    41. //返回栈顶元素
    42. void STTop(ST* pst)
    43. {
    44. assert(pst);
    45. assert(pst->top > 0);
    46. return pst->a[pst->top - 1];
    47. }
    48. //弹栈
    49. void STPop(ST* pst)
    50. {
    51. assert(pst);
    52. assert(pst->top > 0);
    53. pst->top--;
    54. }
    55. //判断是否为空
    56. bool STEmpty(ST* pst)
    57. {
    58. assert(pst);
    59. if (pst->top == 0)
    60. {
    61. return true;
    62. }
    63. else
    64. {
    65. return -1;
    66. }
    67. }
    68. MyQueue* myQueueCreate()
    69. {
    70. MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    71. STInit(&obj->pushst);
    72. STInit(&obj->popst);
    73. return obj;
    74. }
    75. void myQueuePush(MyQueue* obj, int x)
    76. {
    77. STPush(&obj->pushst, x);
    78. }
    79. 返回队列开头的元素(不删除)
    80. void myQueuepeek(MyQueue* obj)
    81. {
    82. if (!STEmpty(&obj->popst))
    83. {
    84. return STTop(&obj->popst);
    85. }
    86. else
    87. {
    88. while (!STEmpty(&obj->pushst))
    89. {
    90. STPush(&obj->popst, STTop(&obj->pushst);
    91. STPop(&obj->pushst);
    92. }
    93. return STTop(&obj->popst);
    94. }
    95. }
    96. //从队列开头移除并返回元素
    97. void myQueuePop(MyQueue* obj)
    98. {
    99. int front = myQueuePeek(obj);
    100. STPop(&obj->popst);
    101. return front;
    102. }
    103. bool myQueueEmpty(MyQueue* obj)
    104. {
    105. return STEmpty(&obj->pushst) && (&obj->popst);
    106. }
    107. void myQueueFree(MyQueue* obj)
    108. {
    109. STDestory(&obj->popst);
    110. STDestory(&obj->pushst);
    111. free(obj);
    112. }

    接下来我们看一下循环队列吧

    1.判断循环队列是否为空:front==back(front指向对头,back指向队尾的下一个)

     

     如何判断队列是否为满

    1.前提:front==back(当size=0时,为空,size!=0则为满)

    2,再增加一个地方)

    数组实现(back+1)%(k+1)==front则为满,其中,k+1指的是开辟空间的个数,k指的是有效数据数 数组实现&(k+1)是为了防止溢出

    链表实现,即把上面式子去掉  %(k+1)

    链表实现:

     

     数组实现:

     单链表缺陷以及找尾的办法:

     如何计算循环中元素的个数

    1. typedef struct {
    2. int* a;
    3. int front;
    4. int back;
    5. int k;
    6. }MyCircularQueue;
    7. //初始化
    8. MyCircularQueue* myCircularQueueCreate(int k) {
    9. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    10. obj->a = (int*)malloc(sizeof(int) * (k + 1));
    11. obj->front = 0;
    12. obj->back = 0;
    13. obj->k = 0;
    14. return obj;
    15. }
    16. //是否为空
    17. bool myCircularQueueEmpty(MyCircularQueue* obj)
    18. {
    19. return obj->front = obj - back;
    20. }
    21. //是否为满
    22. bool myCircularQueueIsFull(MyCircularQueue* obj)
    23. {
    24. return (obj->front) % (obj->k + 1) == obj->front;
    25. }
    26. //插入
    27. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
    28. {
    29. if (myCircularQueueIsFull(obj))
    30. {
    31. return false;
    32. }
    33. obj->a[obj->back] = value;
    34. obj->back++;
    35. obj->back % (obj->k + 1) = obj->back;
    36. return true;
    37. }
    38. //删除
    39. bool myCircularQueueDeQueue(MyCircularQueue* obj, int value)
    40. {
    41. if (myCircularQueueIsFull(obj))
    42. {
    43. return false;
    44. }
    45. ++obj->front;
    46. obj->front % (obj->k + 1) = obj->front;
    47. return true;
    48. }
    49. //返回队头
    50. int myCircularQueueFront(MyCircularQueue* obj)
    51. {
    52. if (myCircularQueueIsFull(obj))
    53. {
    54. return false;
    55. }
    56. return obj->a[obj->front];
    57. }
    58. //返回队尾
    59. int myCircularQueueRear(MyCircularQueue* obj)
    60. {
    61. if (myCircularQueueIsFull(obj))
    62. {
    63. return false;
    64. }
    65. return obj->a[obj->back - 1];
    66. }
    67. //清空
    68. void myCircularQueueFree(MyCircularQueue* obj)
    69. {
    70. free(obj->a);
    71. free(obj);
    72. }

    好啦~关于栈和队列的知识点就这些啦~谢谢大家观看~

  • 相关阅读:
    sftp文件上传uploadFile
    el-upload实现上传文件夹
    React常见的一些坑
    10 个用于网络管理员进行高级扫描的端口扫描工具
    GBASE 8A v953报错集锦27--企业管理器执行投影列中含有重复列名的 sql 语句 报错
    动态IP和静态IP哪个安全,该怎么选择
    Api 接口优化的那些技巧
    第3章 列表简介
    贝锐蒲公英客户端6.0发布,异地组网更快、更简单
    【Linux入门】Linux环境配置
  • 原文地址:https://blog.csdn.net/aig_m_l/article/details/134463902