当前位置:   article > 正文

数据结构--栈和队列_假设栈的空间足够,head和tail

假设栈的空间足够,head和tail

1.栈

1.1栈的概念

栈是一种特殊的线性表,只允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈内元素遵从先进后出的规则。压栈就是插入数据的操作,出栈就是删除数据的操作,都在栈顶实现。

1.2栈的实现

栈的实现可以由链表和数组分别实现,不过考虑到栈的特性,还是选择用数组来实现栈,因为数组在删除和添加尾部数据时消耗较少。

  1. //栈的实现类似顺序表的实现
  2. typedef int STDataType;
  3. typedef struct Stack
  4. {
  5. STDataType* a;
  6. int capacity;
  7. int top;
  8. }ST;
  9. void StackInit(ST* ps);//初始化
  10. void StackDestory(ST* ps);//销毁栈
  11. void StackPush(ST* ps, STDataType x);//插入数据
  12. void StackPop(ST* ps);//删除数据
  13. bool StackEmpty(ST* ps);//判断栈是否为空
  14. STDataType StackTop(ST* ps);//得到栈顶数据
  15. int StackSize(ST* ps);//得到栈中元素个数

初始化函数

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

销毁函数

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

插入函数

  1. void StackPush(ST* ps, STDataType x)
  2. {
  3. assert(ps);
  4. if(ps->capacity == ps->top)//判断是否满了,满了扩容
  5. {
  6. int newcapacity = ps->capacity==0 ? 4 : ps->capacity * 2;//防止栈原来为空
  7. ps->a = (STDataType*)realloc(ps->a,sizeof(STDataType)*newcapacity);
  8. ps->capacity = newcapacity;
  9. }
  10. ps->a[ps->top] = x;
  11. ps->top++;
  12. }

删除数据

  1. void StackPop(ST* ps)
  2. {
  3. assert(ps);
  4. assert(ps->top > 0);
  5. ps->top--;
  6. }
  7. //注意top得大于0,否则会越界访问

判断栈是否为空

  1. bool StackEmpty(ST* ps)
  2. {
  3. assert(ps);
  4. return ps->top == 0;
  5. }
  6. //判断是否为空,与删除数据密切相关,top不可能小于零,所以
  7. //等于0时就为空返回true,不等于0时就返回false

得到栈顶数据

  1. STDataType StackTop(ST* ps)
  2. {
  3. assert(ps);
  4. assert(ps->top != 0);
  5. return ps->a[ps->top-1];
  6. }
  7. //注意top不能为0,如果为0则说明为空,不能返回值。

得到栈中元素个数

  1. int StackSize(ST* PS)
  2. {
  3. assert(ps);
  4. return ps->top;
  5. }

1.3关于栈的习题

这道题用栈就很方便,创建出两个栈,将字符依次压栈,当遇到右符号时,停止压栈。记录栈顶符号,然后出栈。再与右符号进行对比,如果相同,字符串继续向下走直到字符为空;如果不同,直接返回false。

  1. //要将栈的实现部分代码拷贝到这里,在这里不再拷贝
  2. //copy code
  3. //.......
  4. bool isValid(char * s){
  5. ST st;
  6. StackInit(&st);
  7. while(*s)
  8. {
  9. if(*s == '(' || //压栈
  10. *s == '{' ||
  11. *s == '[')
  12. {
  13. StackPush(&st,*s);
  14. s++;
  15. }
  16. else //比较
  17. {
  18. char top = StackTop(&st);
  19. StackPop(&st);
  20. if((*s==')' && top!='(') ||
  21. (*s == '}' && top!='{') ||
  22. (*s == ']' && top!='['))
  23. {
  24. StackDestory(&st);
  25. return false;
  26. }
  27. else
  28. {
  29. s++;
  30. }
  31. }
  32. }
  33. StackDestory(&st);
  34. return true;
  35. }

 如果这样就提交是会报错的!!有特殊情况没有考虑到!当字符串只有右字符或者左字符时,或者左右字符个数并不相等时,这样的写法是不对的!

  1. //要将栈的实现部分代码拷贝到这里,在这里不再拷贝
  2. //copy code
  3. //.......
  4. bool isValid(char * s){
  5. ST st;
  6. StackInit(&st);
  7. while(*s)
  8. {
  9. if(*s == '(' ||
  10. *s == '{' ||
  11. *s == '[')
  12. {
  13. StackPush(&st,*s);
  14. s++;
  15. }
  16. else
  17. {
  18. //当全为右括号,或者右括号数大于左括号时,应返回false,如果不加这一步,则会返回true
  19. if(StackEmpty(&st))
  20. return false;
  21. char top = StackTop(&st);
  22. StackPop(&st);
  23. if((*s==')' && top!='(') ||
  24. (*s == '}' && top!='{') ||
  25. (*s == ']' && top!='['))
  26. {
  27. StackDestory(&st);
  28. return false;
  29. }
  30. else
  31. {
  32. s++;
  33. }
  34. }
  35. }
  36. //当全为左括号,或者左括号数大于右括号时,应返回false,而且正常进行到这一步时
  37. //栈就应该为空。应该是true
  38. bool ret = StackEmpty(&st);
  39. StackDestory(&st);
  40. return ret;
  41. }

2.队列

2.1队列的概念

队列是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,具有先进先出的规则。进行插入操作的一端称为队尾,进行删除操作的一端称为对头。

2.2队列的实现

  1. //队列的实现就是用单链表实现的,
  2. //在此创建了两个结构体,一个是用来放结点的内容
  3. //另一个是用来放队列的头结点和尾结点,创建尾结点便于后面的插入数据的操作
  4. #include<stdio.h>
  5. #include<assert.h>
  6. #include<stdbool.h>
  7. #include<stdlib.h>
  8. typedef int QDataType;
  9. typedef struct QueueNode
  10. {
  11. QDataType val;
  12. struct QueueNode* next;
  13. }QNode;
  14. typedef struct Queue
  15. {
  16. QNode* head;
  17. QNode* tail;
  18. }Queue;
  19. void QueueInit(Queue* pq);
  20. void QueueDestory(Queue* pq);
  21. void QueuePush(Queue* pq,QDataType x);
  22. void QueuePop(Queue* pq);
  23. bool QueueEmpty(Queue* pq);
  24. size_t QueueSize(Queue* pq);
  25. QDataType QueueFront(Queue* pq);
  26. QDataType QueueBack(Queue* pq);

初始化函数

  1. void QueueInit(Queue* pq)
  2. {
  3. assert(pq);
  4. pq->head = pq->tail = NULL;
  5. }

销毁函数

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

插入函数

  1. void QueuePush(Queue* pq, QDataType x)
  2. {
  3. assert(pq);
  4. //建立新结点
  5. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  6. assert(newnode);
  7. newnode->val = x;
  8. newnode->next = NULL;
  9. //防止head和tail为空
  10. if (pq->head == NULL)
  11. {
  12. assert(pq->tail == NULL);
  13. pq->tail = pq->head = newnode;
  14. }
  15. else
  16. {
  17. pq->tail->next = newnode;
  18. pq->tail = newnode;
  19. }
  20. }

删除函数

  1. void QueuePop(Queue* pq)
  2. {
  3. assert(pq);//放头尾指针的结构体不能为空
  4. QNode* next = pq->head->next;
  5. free(pq->head);
  6. pq->head = next;
  7. }

 这样写对不对?这样写只考虑到了队列里不为空的情况,如果head和tail均为空,那么久使用空指针了,就会发生错误;还有一种情况就是当队列中只有一个元素时,删完元素后,next为空,这时head是赋了NULL不错,但tai还指向被释放的空间,那他l是不是就变成野指针了?所以要避免野指针的情况发生!

 

  1. void QueuePop(Queue* pq)
  2. {
  3. assert(pq);//放头尾指针的结构体不能为空
  4. assert(pq->head && pq->tail);//队列不能为空
  5. if (pq->head->next == NULL)
  6. {
  7. free(pq->head);
  8. pq->head = pq->tail = NULL;
  9. }
  10. else
  11. {
  12. QNode* next = pq->head->next;
  13. free(pq->head);
  14. pq->head = next;
  15. }
  16. }

判断队列是否为空

  1. bool QueueEmpty(Queue* pq)
  2. {
  3. assert(pq);
  4. return pq->tail == NULL;
  5. }
  6. //在这里用head.tail判断是否为NULL都可以,
  7. //只要有一个为NULL,就为空。

获得队列元素个数

  1. size_t QueueSize(Queue* pq)
  2. {
  3. assert(pq);
  4. QNode* cur = pq->head;
  5. size_t size = 0;
  6. while (cur)
  7. {
  8. size++;
  9. cur = cur->next;
  10. }
  11. return size;
  12. }

得到队头元素

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

得到队尾元素

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

2.3关于队列的习题

2.3.1 用栈实现队列

 

 思路:用两个栈来实现队列。首先要清楚栈和队列的特点,栈的特点是先进后出,队列的特点是先进先出。一个栈用来“入队”,另一个栈用来“出队”。入队的时候就将全部数据装到pushQ中;出栈的时候将pushQ中的数据全部放到popQ中,然后用stack的出栈即可。需要注意的是,出队的时候不是每次都要将pushQ中的数据放到popQ中,而是当popQ中没有数据时才将数据放入popQ中的。

  1. //要将栈的实现部分代码拷贝到这里,在这里不再拷贝
  2. //copy code
  3. //.......
  4. typedef struct {
  5. ST pushQ;
  6. ST popQ;
  7. } MyQueue;
  8. MyQueue* myQueueCreate() {
  9. MyQueue* qp = (MyQueue*)malloc(sizeof(MyQueue));
  10. assert(qp);
  11. StackInit(&qp->pushQ);
  12. StackInit(&qp->popQ);
  13. return qp;
  14. }
  15. void myQueuePush(MyQueue* obj, int x) {
  16. assert(obj);
  17. StackPush(&obj->pushQ,x);
  18. }
  19. int myQueuePop(MyQueue* obj) {
  20. assert(obj);
  21. int top = 0;
  22. if(StackEmpty(&obj->popQ))
  23. {
  24. while(!StackEmpty(&obj->pushQ))
  25. {
  26. top = StackTop(&obj->pushQ);
  27. StackPop(&obj->pushQ);
  28. StackPush(&obj->popQ,top);
  29. }
  30. top = StackTop(&obj->popQ);
  31. StackPop(&obj->popQ);
  32. }
  33. else
  34. {
  35. top = StackTop(&obj->popQ);
  36. StackPop(&obj->popQ);
  37. }
  38. return top;
  39. }
  40. int myQueuePeek(MyQueue* obj) {
  41. assert(obj);
  42. int top = 0;
  43. if(StackEmpty(&obj->popQ))
  44. {
  45. while(!StackEmpty(&obj->pushQ))
  46. {
  47. top = StackTop(&obj->pushQ);
  48. StackPop(&obj->pushQ);
  49. StackPush(&obj->popQ,top);
  50. }
  51. top = StackTop(&obj->popQ);
  52. }
  53. else
  54. {
  55. top = StackTop(&obj->popQ);
  56. }
  57. return top;
  58. }
  59. bool myQueueEmpty(MyQueue* obj) {
  60. assert(obj);
  61. return StackEmpty(&obj->pushQ) && StackEmpty(&obj->popQ);
  62. }
  63. void myQueueFree(MyQueue* obj) {
  64. assert(obj);
  65. StackDestory(&obj->pushQ);
  66. StackDestory(&obj->popQ);
  67. free(obj);
  68. }

 2.3.2 用队列实现栈

 

 思路:要用队列模拟栈,要充分利用栈和队列的特点。栈是先进后出,那在初始就将数据先放到一个空队列中。如果要出数据,就开始让数据出队,入队到另一个队列中去。当这个队列中只剩一个数据时,停止出队并将这个数据pop掉;如果要输入数据,就将数据输入到一个·不为空的队列中去。(初始随便进入)。

  1. //要将队列的实现部分代码拷贝到这里,在这里不再拷贝
  2. //copy code
  3. //.......
  4. typedef struct {
  5. Queue q1;
  6. Queue q2;
  7. } MyStack;
  8. MyStack* myStackCreate() {
  9. //得到两个队列
  10. MyStack* dst = (MyStack*)malloc(sizeof(MyStack));
  11. assert(dst);
  12. //初始化队列
  13. QueueInit(&dst->q1);
  14. QueueInit(&dst->q2);
  15. return dst;
  16. }
  17. void myStackPush(MyStack* obj, int x) {
  18. assert(obj);
  19. //哪个队列不为空,往哪个队列里push数据
  20. if(!QueueEmpty(&obj->q1))
  21. {
  22. QueuePush(&obj->q1,x);
  23. }
  24. else
  25. {
  26. QueuePush(&obj->q2,x);
  27. }
  28. }
  29. int myStackPop(MyStack* obj) {
  30. assert(obj);
  31. //假设空队列是q1,判断,如果不是,则交换。
  32. Queue* EmptyQ = &obj->q1;
  33. Queue* NonemptyQ = &obj->q2;
  34. if(!QueueEmpty(&obj->q1))
  35. {
  36. EmptyQ = &obj->q2;
  37. NonemptyQ = &obj->q1;
  38. }
  39. while(QueueSize(NonemptyQ)>1)
  40. {
  41. int top = QueueFront(NonemptyQ);
  42. QueuePush(EmptyQ,top);
  43. QueuePop(NonemptyQ);
  44. }
  45. int top = QueueFront(NonemptyQ);
  46. QueuePop(NonemptyQ);
  47. return top;
  48. }
  49. int myStackTop(MyStack* obj) {
  50. assert(obj);
  51. if(!QueueEmpty(&obj->q1))
  52. {
  53. return QueueBack(&obj->q1);
  54. }
  55. else
  56. {
  57. return QueueBack(&obj->q2);
  58. }
  59. }
  60. bool myStackEmpty(MyStack* obj) {
  61. assert(obj);
  62. return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
  63. }
  64. void myStackFree(MyStack* obj) {
  65. assert(obj);
  66. QueueDestory(&obj->q1);
  67. QueueDestory(&obj->q2);
  68. free(obj);
  69. }

 2.3.3 循环队列

 循环队列无非就是在逻辑上实现队列的循环,在这里选择使用数组来实现循环队列。但循环队列的容量是有限的,只有还有多余空间时才能插入数据,如果满了就不能插入数据了。

 因此必须想出一种方法,就是在开辟空间时,多开辟一个空间,不存放数据只为了判断队列是否已经满了。条件就是back的下一个是否就为front。(要注意back在最后面,front在最前面的情况,这时他们两个相差size)。而判断队列为空的条件就是back与front相等。在写程序时要时刻注意back或者front是否走到了数组最后!!

  1. typedef struct {
  2. int* a;
  3. int front;
  4. int back;
  5. int size;
  6. } MyCircularQueue;
  7. MyCircularQueue* myCircularQueueCreate(int k) {
  8. MyCircularQueue* pst = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  9. assert(pst);
  10. pst->a = (int*)malloc(4 * (k + 1));
  11. assert(pst->a);
  12. pst->size = k;
  13. pst->front = pst->back = 0;
  14. return pst;
  15. }
  16. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
  17. assert(obj);
  18. //判断队列是否满了
  19. if ((obj->front - obj->back) != 1 && (obj->back - obj->front) != obj->size)
  20. {
  21. obj->a[obj->back] = value;
  22. //判断是否到数组尾部
  23. if (obj->back != obj->size)
  24. {
  25. obj->back++;
  26. }
  27. else
  28. {
  29. obj->back = 0;
  30. }
  31. return true;
  32. }
  33. else
  34. {
  35. return false;
  36. }
  37. }
  38. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
  39. assert(obj);
  40. //判断队列是否为空
  41. if (obj->front == obj->back)
  42. {
  43. return false;
  44. }
  45. else
  46. {
  47. //判断是否到了数组尾部
  48. if (obj->front != obj->size)
  49. obj->front++;
  50. else
  51. obj->front = 0;
  52. return true;
  53. }
  54. }
  55. int myCircularQueueFront(MyCircularQueue* obj) {
  56. assert(obj);
  57. if (obj->front != obj->back)
  58. {
  59. return obj->a[obj->front];
  60. }
  61. else
  62. {
  63. return -1;
  64. }
  65. }
  66. int myCircularQueueRear(MyCircularQueue* obj) {
  67. assert(obj);
  68. int back = 0;
  69. if (obj->front != obj->back)
  70. {
  71. //判断back是否在数组头部
  72. if (obj->back != 0)
  73. {
  74. back = obj->back - 1;
  75. }
  76. else
  77. {
  78. back = obj->size;
  79. }
  80. return obj->a[back];
  81. }
  82. else
  83. {
  84. return -1;
  85. }
  86. }
  87. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
  88. assert(obj);
  89. if (obj->front == obj->back)
  90. {
  91. return true;
  92. }
  93. else
  94. {
  95. return false;
  96. }
  97. }
  98. bool myCircularQueueIsFull(MyCircularQueue* obj) {
  99. assert(obj);
  100. if ((obj->front - obj->back) == 1 || (obj->back - obj->front) == obj->size)
  101. {
  102. return true;
  103. }
  104. else
  105. {
  106. return false;
  107. }
  108. }
  109. void myCircularQueueFree(MyCircularQueue* obj) {
  110. assert(obj);
  111. free(obj->a);
  112. obj->front = obj->back = obj->size = 0;
  113. free(obj);
  114. }

 注意,

1.back每次是指向加入的数据之后的那个位置,因此在打印尾部数据时要注意不能直接使用back指向的数据;

2.在删除数据时,不需要给数据赋值,只需要front++,但是要注意front是否在数组最后,如果是的话,就让他等于0;

3.数组内有一个位置一定是空的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/531892
推荐阅读
相关标签
  

闽ICP备14008679号