【数据结构初阶】栈和队列

打印 上一主题 下一主题

主题 697|帖子 697|积分 2091

1. 栈

1.1 栈的概念及结构

栈:一种特别的线性表,其只允许在固定的一端进行插入和删除元素操纵。进行数据插入和删除操纵的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)(即最后面进的数据会最先出来)的原则。
压栈:栈的插入操纵叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操纵叫做出栈。出数据也在栈顶。
1.2 栈的实现

栈的实现一般可以利用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
   

  • 访问效率:数组的随机访问特性意味着可以在常数时间内访问任何位置的元素,这对于栈的操纵尤为重要,因为栈的操纵总是在栈顶进行。
  • 空间利用:与链表相比,数组不需要额外的空间来存储指针,这意味着数组的空间利用率更高。
  • 缓存局部性:由于数组在内存中是连续的,因此具有更好的缓存局部性。当栈顶元素被访问时,相邻的元素也会被加载到缓存中,这大概会提高随后的栈操纵的速率。
  • 实现简单:数组实现栈的逻辑相对简单,不需要维护额外的指针或是节点信息,这使得代码更加简便,也更容易维护和调试。
  • 性能考量:在大多数情况下,数组实现的栈在性能上优于链表实现的栈,因为数组的入栈和出栈操纵只需要修改栈顶指针,而链表则需要动态分配节点并调整指针。
  • 空间分配:数组实现的栈在初始化时大概需要分配较大的空间,这大概会导致肯定的空间浪费,尤其是在栈的大小远小于数组容量时。然而,这一点可以通过动态扩容的策略来缓解。
  别的,在选择栈的实现方式时,除了考虑性能因素外,还需要考虑实际应用场景中的具体需求。例如,假如栈的大小厘革非常频繁,那么链表实现大概更为符合,因为链表在插入和删除节点时不需要进行大量的数据复制。然而,假如栈的操纵主要是入栈和出栈,并且对性能有较高的要求,那么数组实现将是更好的选择。
  支持动态增长的栈:
  1. typedef char STDataType;
  2. typedef struct Stack
  3. {
  4.         STDataType* _a;
  5.         int _top;                // 栈顶
  6.         int _capacity;  // 容量
  7. }Stack;
复制代码
 初始化栈 

  1. void StackInit(Stack* ps)
  2. {
  3.         ps->_a = NULL;
  4.         ps->_capacity = ps->_top = 0;
  5. }
复制代码
入栈

  1. void StackPush(Stack* ps, STDataType data)
  2. {
  3.         assert(ps);
  4.         if (ps->_capacity == ps->_top)
  5.         {
  6.                 int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
  7.                 STDataType* tmp = (STDataType*)realloc(ps->_a,sizeof(STDataType) * newcapacity);
  8.                 if (tmp==NULL)
  9.                 {
  10.                         perror("realloc");
  11.                         return;
  12.                 }
  13.                 ps->_a = tmp;
  14.                 ps->_capacity = newcapacity;
  15.         }
  16.         ps->_a[ps->_top] = data;
  17.         ps->_top++;
  18. }
复制代码
 出栈

  1. void StackPop(Stack* ps)
  2. {
  3.         assert(ps);
  4.         ps->_top--;
  5. }
复制代码
 获取栈顶元素 

  1. STDataType StackTop(Stack* ps)
  2. {
  3.         assert(ps);
  4.         assert(ps->_top > 0);
  5.         return ps->_a[ps->_top - 1];
  6. }
复制代码
 获取栈中有效元素个数

  1. int StackSize(Stack* ps)
  2. {
  3.         assert(ps);
  4.         return ps->_top;
  5. }
复制代码
判空

检测栈是否为空,假如为空返回非零结果,假如不为空返回0  
  1. int StackEmpty(Stack* ps)
  2. {
  3.         assert(ps);
  4.         if (ps->_top == 0)
  5.                 return 1;
  6.         else
  7.                 return 0;
  8. }
复制代码
 烧毁栈 

  1. void StackDestroy(Stack* ps)
  2. {
  3.         assert(ps);
  4.         free(ps->_a);
  5.         ps->_a == NULL;
  6.         ps->_capacity = ps->_top = 0;
  7. }
复制代码
2. 队列

2.1 队列的概念及结构

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

队列也可以用数组和链表的结构实现,利用链表的结构实现更优一些,因为假如利用数组的结构,出队列在数组头上出数据,效率会比较低(因为数组头上出数据,需要把头后面的数据全部向前移,而链表只需要改背叛点的指向就行了)。
链表结构实现队列:

  1. typedef struct QListNode
  2. {
  3.         struct QListNode* _next;
  4.         QDataType _data;
  5. }QNode;
  6. // 队列的结构
  7. typedef struct Queue
  8. {
  9.         QNode* _front;
  10.         QNode* _rear;
  11.         int size;
  12. }Queue;
复制代码
初始化队列 

  1. void QueueInit(Queue* q)
  2. {
  3.         assert(q);
  4.         q->_front = NULL;
  5.         q->_rear = NULL;
  6.         q->size = 0;
  7. }
复制代码
 队尾入队列

  1. QNode* SLBuyNode(QDataType data)
  2. {
  3.         QNode* newNode = (QNode*)malloc(sizeof(QNode));
  4.         if (newNode == NULL)
  5.         {
  6.                 perror("malloc");
  7.                 return;
  8.         }
  9.         newNode->_next = NULL;
  10.         newNode->_data = data;
  11.         return newNode;
  12. }
  13. void QueuePush(Queue* q, QDataType data)
  14. {
  15.         QNode* newNode = SLBuyNode(data);
  16.         if (q->_front == NULL)
  17.         {
  18.                 q->_front = q->_rear = newNode;
  19.                 q->size++;
  20.         }
  21.         else
  22.         {
  23.                 q->_rear->_next = newNode;
  24.                 q->_rear = q->_rear->_next;
  25.                 q->size++;
  26.         }
  27. }
复制代码
队头出队列 

  1. void QueuePop(Queue* q)
  2. {
  3.         assert(q&&q->_front);
  4.         QNode* next = q->_front->_next;
  5.         free(q->_front);
  6.         if (next == NULL)
  7.                 q->_front = q->_rear = next;
  8.         else
  9.                 q->_front = next;
  10.         q->size--;
  11. }
复制代码
 获取队列头部元素 

  1. QDataType QueueFront(Queue* q)
  2. {
  3.         assert(q && q->_front);
  4.         return q->_front->_data;
  5. }
复制代码
 获取队列队尾元素 

  1. QDataType QueueBack(Queue* q)
  2. {
  3.         assert(q && q->_rear);
  4.         return q->_rear->_data;
  5. }
复制代码
 获取队列中有效元素个数

  1. int QueueSize(Queue* q)
  2. {
  3.         QNode* head = q->_front;
  4.         int count = 0;
  5.         while (head)
  6.         {
  7.                 head = head->_next;
  8.                 count++;
  9.         }
  10.         return count;
  11. }
复制代码
 判空

检测队列是否为空,假如为空返回非零结果,假如非空返回0 
  1. int QueueEmpty(Queue* q)
  2. {
  3.         if (q->_front == NULL)
  4.                 return 1;
  5.         else
  6.                 return 0;
  7. }
复制代码
 烧毁队列

  1. void QueueDestroy(Queue* q)
  2. {
  3.         assert(q);
  4.         while (q->_front)
  5.         {
  6.                 QNode* next = q->_front->_next;
  7.                 free(q->_front);
  8.                 q->_front = next;
  9.         }
  10.         q->_rear = NULL;
  11. }
复制代码
3. 栈和队列的面试题

3.1 队列实现栈


. - 力扣(LeetCode)

解题思路:

队列中的数据依照先进先出的原则,而栈中的数据依照后进先出的原则,以是要想队列实现栈,就需要让队列实现数据的后进先出,这时候我们就可以设置两个队列,其中一个队列入数据,当出数据的时候,就把除了最后一个数据的全部数据放到另一个队列中,然后从队列1出数据,这样就实现了后进先出。(如下图)
还有一个问题就是当入数据的时候该往哪个队列入数据呢?
假如是往空队列入数据的话,空队列就酿成了非空队列,这样就会导致两个队列都是非空队列,而无法分辨下一次该往哪个队列入数据。而往非空队列入数据则避免了这一情况,固然假如两个队列都为空,那就随便往一个队列入数据就可以了。

  1. typedef int QDataType;// 链式结构:表现队列 typedef struct QListNode
  2. {
  3.         struct QListNode* _next;
  4.         QDataType _data;
  5. }QNode;
  6. // 队列的结构
  7. typedef struct Queue
  8. {
  9.         QNode* _front;
  10.         QNode* _rear;
  11.         int size;
  12. }Queue;// 初始化队列 void QueueInit(Queue* q)
  13. {
  14.         assert(q);
  15.         q->_front = NULL;
  16.         q->_rear = NULL;
  17.         q->size = 0;
  18. }QNode* SLBuyNode(QDataType data){        QNode* newNode = (QNode*)malloc(sizeof(QNode));        if (newNode == NULL)        {                perror("malloc");                return 0;        }        newNode->_next = NULL;        newNode->_data = data;        return newNode;}// 队尾入队列 void QueuePush(Queue* q, QDataType data){        QNode* newNode = SLBuyNode(data);        if (q->_front == NULL)        {                q->_front = q->_rear = newNode;                q->size++;        }        else        {                q->_rear->_next = newNode;                q->_rear = q->_rear->_next;                q->size++;        }}// 队头出队列 void QueuePop(Queue* q)
  19. {
  20.         assert(q&&q->_front);
  21.         QNode* next = q->_front->_next;
  22.         free(q->_front);
  23.         if (next == NULL)
  24.                 q->_front = q->_rear = next;
  25.         else
  26.                 q->_front = next;
  27.         q->size--;
  28. }// 获取队列头部元素 QDataType QueueFront(Queue* q){        assert(q);    assert(q->_front);        return q->_front->_data;}// 获取队列队尾元素 QDataType QueueBack(Queue* q){        assert(q);    assert(q->_rear);        return q->_rear->_data;}// 获取队列中有效元素个数 int QueueSize(Queue* q)
  29. {
  30.         QNode* head = q->_front;
  31.         int count = 0;
  32.         while (head)
  33.         {
  34.                 head = head->_next;
  35.                 count++;
  36.         }
  37.         return count;
  38. }// 检测队列是否为空,假如为空返回非零结果,假如非空返回0 int QueueEmpty(Queue* q)
  39. {
  40.         if (q->_front == NULL)
  41.                 return 1;
  42.         else
  43.                 return 0;
  44. }// 烧毁队列 void QueueDestroy(Queue* q)
  45. {
  46.         assert(q);
  47.         while (q->_front)
  48.         {
  49.                 QNode* next = q->_front->_next;
  50.                 free(q->_front);
  51.                 q->_front = next;
  52.         }
  53.         q->_rear = NULL;
  54. }typedef struct {    Queue q1;    Queue q2;} MyStack;MyStack* myStackCreate() {    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));    QueueInit(&pst->q1);    QueueInit(&pst->q2);    return pst;}void myStackPush(MyStack* obj, int x) {    if(QueueEmpty(&obj->q1)==0)    {        QueuePush(&obj->q1, x);    }    else{        QueuePush(&obj->q2, x);    }}int myStackPop(MyStack* obj) {    Queue* Empty = &obj->q1;    Queue* nonEmpty = &obj->q2;    if(QueueEmpty(&obj->q1)==0)    {        Empty = &obj->q2;        nonEmpty = &obj->q1;    }    while(nonEmpty->size>1)    {        QueuePush(Empty, QueueFront(nonEmpty));        QueuePop(nonEmpty);    }    int top = QueueFront(nonEmpty);    QueuePop(nonEmpty);    return top;}int myStackTop(MyStack* obj) {    if(QueueEmpty(&obj->q1)==0)    {        return QueueBack(&obj->q1);    }    else        return QueueBack(&obj->q2);}bool myStackEmpty(MyStack* obj) {    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);}void myStackFree(MyStack* obj) {    QueueDestroy(&obj->q1);    QueueDestroy(&obj->q2);    free(obj);}
复制代码
3.2 用栈实现队列

标题描述:

. - 力扣(LeetCode)

解题思路: 

栈中的数据依照后进先出的原则,而队列中的数据依照先进先出的原则,以是要想用栈实现栈,就需要让栈实现数据的先进先出,如下图我们可以设置两个栈,一个栈存放数据,另一个栈出数据。具体实现是:往非空的栈插入数据,出数据的时候将栈1的数据导入栈2,然后从栈2出数据,最后将栈的数据导回栈1,因为不导回数据的话根据栈后进先出的原则,会将插入的数据倒过来,假如直接将数据插入栈2,就会导致栈插入的数据顺序被打乱。

  1. typedef int QDataType;// 链式结构:表现队列 typedef struct QListNode
  2. {
  3.         struct QListNode* _next;
  4.         QDataType _data;
  5. }QNode;
  6. // 队列的结构
  7. typedef struct Queue
  8. {
  9.         QNode* _front;
  10.         QNode* _rear;
  11.         int size;
  12. }Queue;// 初始化队列 void QueueInit(Queue* q)
  13. {
  14.         assert(q);
  15.         q->_front = NULL;
  16.         q->_rear = NULL;
  17.         q->size = 0;
  18. }QNode* SLBuyNode(QDataType data){        QNode* newNode = (QNode*)malloc(sizeof(QNode));        if (newNode == NULL)        {                perror("malloc");                return 0;        }        newNode->_next = NULL;        newNode->_data = data;        return newNode;}// 队尾入队列 void QueuePush(Queue* q, QDataType data){        QNode* newNode = SLBuyNode(data);        if (q->_front == NULL)        {                q->_front = q->_rear = newNode;                q->size++;        }        else        {                q->_rear->_next = newNode;                q->_rear = q->_rear->_next;                q->size++;        }}// 队头出队列 void QueuePop(Queue* q)
  19. {
  20.         assert(q&&q->_front);
  21.         QNode* next = q->_front->_next;
  22.         free(q->_front);
  23.         if (next == NULL)
  24.                 q->_front = q->_rear = next;
  25.         else
  26.                 q->_front = next;
  27.         q->size--;
  28. }// 获取队列头部元素 QDataType QueueFront(Queue* q){        assert(q);    assert(q->_front);        return q->_front->_data;}// 获取队列队尾元素 QDataType QueueBack(Queue* q){        assert(q);    assert(q->_rear);        return q->_rear->_data;}// 获取队列中有效元素个数 int QueueSize(Queue* q)
  29. {
  30.         QNode* head = q->_front;
  31.         int count = 0;
  32.         while (head)
  33.         {
  34.                 head = head->_next;
  35.                 count++;
  36.         }
  37.         return count;
  38. }// 检测队列是否为空,假如为空返回非零结果,假如非空返回0 int QueueEmpty(Queue* q)
  39. {
  40.         if (q->_front == NULL)
  41.                 return 1;
  42.         else
  43.                 return 0;
  44. }// 烧毁队列 void QueueDestroy(Queue* q)
  45. {
  46.         assert(q);
  47.         while (q->_front)
  48.         {
  49.                 QNode* next = q->_front->_next;
  50.                 free(q->_front);
  51.                 q->_front = next;
  52.         }
  53.         q->_rear = NULL;
  54. }typedef struct {    Queue q1;    Queue q2;} MyStack;MyStack* myStackCreate() {    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));    QueueInit(&pst->q1);    QueueInit(&pst->q2);    return pst;}void myStackPush(MyStack* obj, int x) {    if(QueueEmpty(&obj->q1)==0)    {        QueuePush(&obj->q1, x);    }    else{        QueuePush(&obj->q2, x);    }}int myStackPop(MyStack* obj) {    Queue* Empty = &obj->q1;    Queue* nonEmpty = &obj->q2;    if(QueueEmpty(&obj->q1)==0)    {        Empty = &obj->q2;        nonEmpty = &obj->q1;    }    while(nonEmpty->size>1)    {        QueuePush(Empty, QueueFront(nonEmpty));        QueuePop(nonEmpty);    }    int top = QueueFront(nonEmpty);    QueuePop(nonEmpty);    return top;}int myStackTop(MyStack* obj) {    if(QueueEmpty(&obj->q1)==0)    {        return QueueBack(&obj->q1);    }    else        return QueueBack(&obj->q2);}bool myStackEmpty(MyStack* obj) {    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);}void myStackFree(MyStack* obj) {    QueueDestroy(&obj->q1);    QueueDestroy(&obj->q2);    free(obj);}
复制代码
3.3 设计循环队列

标题描述:

. - 力扣(LeetCode)

解题思路: 

  1. typedef struct {
  2.     int*_a;
  3.     int head;
  4.     int tail;
  5.     int k;
  6. } MyCircularQueue;
  7. MyCircularQueue* myCircularQueueCreate(int k) {
  8.     MyCircularQueue* tmp = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  9.     tmp->_a = (int*)malloc(sizeof(int)*(k+1));
  10.     tmp->head=0;
  11.     tmp->tail=0;
  12.     tmp->k=k;
  13.     return tmp;
  14. }
  15. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
  16.     if((obj->tail+1)%(obj->k+1)==obj->head)
  17.         return false;
  18.     obj->_a[obj->tail] = value;
  19.     obj->tail = (obj->tail+1)%(obj->k+1);
  20.     return true;
  21. }
  22. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
  23.     if(obj->tail==obj->head)
  24.     {
  25.         return false;
  26.     }
  27.     obj->head = (obj->head+1)%(obj->k+1);
  28.     return true;
  29. }
  30. int myCircularQueueFront(MyCircularQueue* obj) {
  31.     if(obj->head==obj->tail)
  32.     {
  33.         return -1;
  34.     }
  35.     return obj->_a[obj->head];
  36. }
  37. int myCircularQueueRear(MyCircularQueue* obj) {
  38.     if(obj->head==obj->tail)
  39.     {
  40.         return -1;
  41.     }
  42.     return obj->_a[(obj->tail+obj->k)%(obj->k+1)];
  43. }
  44. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
  45.     return obj->head==obj->tail;
  46. }
  47. bool myCircularQueueIsFull(MyCircularQueue* obj) {
  48.     return (obj->tail+1)%(obj->k+1)==obj->head;
  49. }
  50. void myCircularQueueFree(MyCircularQueue* obj) {
  51.     free(obj->_a);
  52.     free(obj);
  53.     obj->_a==NULL;
  54. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

卖不甜枣

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表