栈和队列详解

  • 栈的表示和实现

  • 栈的概念及结构(栈就相当于一个盒子,只能从一头一个一个放进去,一个一个拿出来)

    练习题:
    1. 答案:B、C
    2. 出栈:栈的删除操作叫做出栈。出数据也在栈顶。
    3. 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
    4. 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out) 的原则。
    5. 实现:
      1. 数组实现(选择这个来实现)
          1. 这样实现的是一个静态的顺序表,缺陷是MAX一旦满了就不能再插入了,所以不建议写静态的
          2. 这样写:
      2. 链表实现
      3. 注意,栈顶是指向数组数据的后面一个
      4. 实现源码:
  • 队列的表示和实现
    1. 表示:队列就相当于一个管道,这边进去,那边出来
      1. 出栈,入两个,出两个这种,是不会改变出队顺序的
    2. 实现:还是两种方式,数组和链表。
      1. 数组:入数据好入,出数据不好出,而且增容不好增容,不太合适
      2. 链表:单链表实现即可
        • 实现代码:
             

    1. 现实世界中的队列的应用:排队
      1. 更深层次的东西:比如如果两个窗口同时空了,同时按到了按钮,同时叫到了102号,这种怎么解决呢?这就涉及到并行操作了,需要后续操作系统的知识,也就是需要加锁来解决问题,先来的人先取,他取了再下一个,这就不至于导致两个窗口抢到一个
  • 栈和队列OJ题
    1. . - 力扣(LeetCode)
      1. 思路:左括号就入栈,如果是右括号,就拿出栈顶的左括号判断二者是否匹配
      2. 实现代码:
// 支持动态增长的栈

typedef char STDataType;

typedef struct Stack

{

    STDataType* _a;

    int _top;       // 栈顶

    int _capacity;  // 容量

}Stack;

// 初始化栈

void StackInit(Stack* ps);

// 入栈

void StackPush(Stack* ps, STDataType data);

// 出栈

void StackPop(Stack* ps);

// 获取栈顶元素

STDataType StackTop(Stack* ps);

// 获取栈中有效元素个数

int StackSize(Stack* ps);

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0

bool StackEmpty(Stack* ps);

// 销毁栈

void StackDestroy(Stack* ps);


// 初始化栈

void StackInit(Stack* ps)

{

    assert(ps);

    ps->_a = NULL;

    ps->_top = ps->_capacity = 0;

}

// 入栈

void StackPush(Stack* ps, STDataType data)

{

    assert(ps);

    //空间不够先增容

    if (ps->_top == ps->_capacity)

    {

        int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;

        STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);

        if (tmp == NULL)

        {

            perror("realloc fail\n");

            exit(-1);

        }

        ps->_a = tmp;

        ps->_capacity = newcapacity;

    }

    ps->_a[ps->_top] = data;

    ++ps->_top;

}

// 出栈

void StackPop(Stack* ps)

{

    assert(ps);

    assert(ps->_top > 0);


    --ps->_top;

}

// 获取栈顶元素

STDataType StackTop(Stack* ps)

{

    assert(ps);

    assert(ps->_top > 0);


    return ps->_a[ps->_top - 1];

}

// 获取栈中有效元素个数

int StackSize(Stack* ps)

{

    assert(ps);

    return ps->_top;

}


// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0

bool StackEmpty(Stack* ps)

{

    assert(ps);

    return ps->_top == 0;

}

// 销毁栈

void StackDestroy(Stack* ps)

{

    assert(ps);

    free(ps->_a);


    ps->_a = NULL;

    ps->_top = ps->_capacity = 0;

}


bool isValid(char* s) {

    Stack st;

    StackInit(&st);

    for(int i = 0;s[i] != '\0';++i)

    {

        if(s[i] == '(' || s[i] == '{' || s[i] == '[')

        {

            StackPush(&st,s[i]);

        }

        if(s[i] == ')' || s[i] == '}' || s[i] == ']')

        {

            if(StackEmpty(&st))

            {

                StackDestroy(&st);

                return false;

            }

            char tmp = StackTop(&st);

            if((tmp == '(' && s[i] == ')') || (tmp == '{' && s[i] == '}') || (tmp == '[' && s[i] == ']'))

            {

                StackPop(&st);

            }

            else

            {

                StackDestroy(&st);

                return false;

            }

        }

    }

    bool ret = StackEmpty(&st);

    StackDestroy(&st);

    return ret;

}
      1. OJ报错如何解决?
        • 看报错的测试用例,分析代码,用大脑跟着走读代码
    1. 用队列实现栈
      1. OJ链接:
      2. 思路:
        • 我们可以把另外一个队列当作中间人:
        • 入队列和出队列
          1. 入队列:哪个队列为空,就入到哪个队列里面
          2. 出队列:哪个队列不为空,就先把队尾前的数据倒到另一个队列里面,再pop
      3. 代码
typedef int QDataType;


typedef struct QueueNode

{

    struct QueueNode* next;

    QDataType val;

}QNode;


typedef struct Queue

{

    QNode* phead;

    QNode* ptail;

    int size;

}Queue;


//队列初始化

void QueueInit(Queue* pq);


//队尾进数据

void QueuePush(Queue* pq, QDataType x);


//队头出数据

void QueuePop(Queue* pq);


//队列长度

int QueueSize(Queue* pq);


//获取队头数据:

QDataType QueueFront(Queue* pq);


//获取队尾数据:

QDataType QueueBack(Queue* pq);


//判断空

bool QueueEmpty(Queue* pq);


//队列初始化

void QueueInit(Queue* pq)

{

    pq->phead = NULL;

    pq->ptail = NULL;

    pq->size = 0;

}


//队尾进数据(尾插)

void QueuePush(Queue* pq, QDataType x)

{

    assert(pq);

    QNode* newnode = (QNode*)malloc(sizeof(QNode));

    if (newnode == NULL)

    {

        perror("malloc fail\n");

        exit(-1);

    }

    newnode->next = NULL;

    newnode->val = x;

    if (pq->phead == NULL)

    {

        pq->phead = newnode;

        pq->ptail = newnode;

    }

    else

    {

        pq->ptail->next = newnode;

        pq->ptail = newnode;

    }

    pq->size++;

}


//队头出数据

void QueuePop(Queue* pq)

{

    assert(pq);

    if (pq->size == 1)//一个节点

    {

        free(pq->phead);

        pq->phead = pq->ptail = NULL;

    }

    else//多个节点

    {

        QNode* next = pq->phead->next;

        free(pq->phead);

        pq->phead = next;

    }

    pq->size--;

}


//队列长度

int QueueSize(Queue* pq)

{

    return pq->size;

}


//获取队头数据:

QDataType QueueFront(Queue* pq)

{

    assert(pq);

    return pq->phead->val;

}


//获取队尾数据:

QDataType QueueBack(Queue* pq)

{

    assert(pq);

    return pq->ptail->val;

}


//判断空

bool QueueEmpty(Queue* pq)

{

    return pq->phead == NULL ? true : false;

}


void QueueDestory(Queue* pq)

{

    assert(pq);

    QNode* cur = pq->phead;

    while (cur)

    {

        QNode* next = cur->next;

        free(cur);

        cur = next;

    }

    pq->phead = pq->ptail = NULL;

    pq->size = 0;

}


typedef struct {

    Queue q1;

    Queue q2;

} MyStack;


MyStack* myStackCreate() {

    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));

    QueueInit(&obj->q1);

    QueueInit(&obj->q2);

    return obj;

}


void myStackPush(MyStack* obj, int x) {

    assert(obj);

    if(QueueEmpty(&obj->q1))

    {

        QueuePush(&obj->q2,x);

    }

    else

    {

        QueuePush(&obj->q1,x);

    }

}


int myStackPop(MyStack* obj) {

    assert(obj);

    Queue* empty = &obj->q1;//假设法,假设q1为空

    Queue* noempty = &obj->q2;

    if(!QueueEmpty(&obj->q1))

    {

        empty = &obj->q2;

        noempty = &obj->q1;

    }

    while(QueueSize(noempty) > 1)

    {

        QueuePush(empty,QueueFront(noempty));

        QueuePop(noempty);

    }

    int ret = QueueFront(noempty);

    QueuePop(noempty);

    return ret;

}


int myStackTop(MyStack* obj) {

    assert(obj);

    if(!QueueEmpty(&obj->q1))

    {

        return QueueBack(&obj->q1);

    }

    else

    {

        return QueueBack(&obj->q2);

    }

}


bool myStackEmpty(MyStack* obj) {

    assert(obj);

    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);

}


void myStackFree(MyStack* obj) {

    assert(obj);

    QueueDestory(&obj->q1);

    QueueDestory(&obj->q2);

    free(obj);

}
    1. 用栈实现队列
      1. OJ链接:
      2. 思路与分析
        • 然后你会发现,在s2这个栈里面的数据,已经符合队列的出栈顺序了,所以,我们出数据,就可以直接让s2这个栈pop,
        • 如果需要入数据,我们就push到s1这个栈里面,当s2中的数据pop完了,我们又从s1中把数据倒到s2中去
        • 由此,我们可以设计一个pushst的栈,专门用来入数据,另一个popst的栈,专门用来出数据
      3. 代码:
typedef int STDataType;


typedef struct Stack

{

    STDataType* a;

    int top;//栈顶

    int capacity;//容量

}ST;


//初始化:

void StackInit(ST* ps);


void StackDestory(ST* ps);


void StackPush(ST* ps, STDataType x);


void StackPop(ST* ps);


bool StackEmpty(ST* ps);


STDataType StackTop(ST* ps);


int StackSize(ST* ps);


//初始化:

void StackInit(ST* ps)

{

    assert(ps);

    ps->a = NULL;

    ps->capacity = ps->top = 0;

}


void StackDestory(ST* ps)

{

    assert(ps);

    free(ps->a);

    ps->a = NULL;

    ps->top = ps->capacity = 0;

}


void StackPush(ST* ps, STDataType x)

{

    assert(ps);

    //空间不够先扩容:

    if (ps->top == ps->capacity)

    {

        int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

        STDataType* tmp = (STDataType*)realloc(ps->a,sizeof(STDataType) * newcapacity);

        if (tmp == NULL)

        {

            perror("malloc failed.\n");

            return;

        }

        ps->a = tmp;

        ps->capacity = newcapacity;

    }

    ps->a[ps->top] = x;

    ps->top++;

}


void StackPop(ST* ps)

{

    assert(ps);

    assert(ps->top > 0);

    ps->top--;

}


bool StackEmpty(ST* ps)

{

    assert(ps);

    return ps->top == 0;

}


STDataType StackTop(ST* ps)

{

    assert(ps);

    assert(ps->top > 0);

    return ps->a[ps->top - 1];

}


int StackSize(ST* ps)

{

    assert(ps);

    return ps->top;

}


typedef struct {

    ST pushST;

    ST popST;

} MyQueue;


MyQueue* myQueueCreate() {

    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));

    StackInit(&obj->pushST);

    StackInit(&obj->popST);

    return obj;

}


void myQueuePush(MyQueue* obj, int x) {

    assert(obj);

    StackPush(&obj->pushST,x);

}


int myQueuePeek(MyQueue* obj) {

    assert(obj);

    if(StackEmpty(&obj->popST))

    {

        //倒数据:

        while(!StackEmpty(&obj->pushST))

        {

            StackPush(&obj->popST,StackTop(&obj->pushST));

            StackPop(&obj->pushST);

        }

        return StackTop(&obj->popST);

    }

    else

    {

        return StackTop(&obj->popST);

    }

}


int myQueuePop(MyQueue* obj) {

    assert(obj);

    int ret = myQueuePeek(obj);//函数复用

    StackPop(&obj->popST);

    return ret;

}


bool myQueueEmpty(MyQueue* obj) {

    return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);

}


void myQueueFree(MyQueue* obj) {

    assert(obj);

    StackDestory(&obj->pushST);

    StackDestory(&obj->popST);

    free(obj);

}
    1. 设计循环队列
      1. OJ地址:
      2. 什么是循环队列?
      3. 实现思路:设计一个head和tail,让tail永远指向尾数据的下一个
      4. 问题1:怎么解决假溢出问题?
      5. 问题2:回绕问题

通过上面的图,你应该已经发现了tail+1越界了,所以这时候我们需要回绕,有两种解决方案,一种是用条件语句判断,一种是取模操作

让tail + 1 % (k + 1)就可以了,或者:tail == k + 1 ? 0 : tail;

解决完这两个问题,我们就可以上手写代码了:

实现代码:

typedef struct {

    int *a;

    int head;

    int tail;

    int k;

} MyCircularQueue;


bool myCircularQueueIsEmpty(MyCircularQueue* obj) {

    return obj->head == obj->tail;

}


bool myCircularQueueIsFull(MyCircularQueue* obj) {

    return (obj->tail + 1) % (obj->k + 1) == obj->head;//防止tail+1出现溢出问题,我们这里采用模运算进行回绕

}


MyCircularQueue* myCircularQueueCreate(int k) {

    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));


    //多开一个空间解决假溢出问题:

    obj->a = (int*)malloc(sizeof(int) * (k + 1));

    obj->head = obj-> tail = 0;

    obj->k = k;

    return obj;

}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {

    if(myCircularQueueIsFull(obj))

        return false;

    obj->a[obj->tail] = value;

    obj->tail++;

    obj->tail %= (obj->k + 1);//解决回绕问题

    return true;//别忘了返回值

}


bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除就是让head往后走一个,不需要free

    if(myCircularQueueIsEmpty(obj))

        return false;

    obj->head++;

    obj->head %= (obj->k + 1);//head有可能在数组的最后一个,也需要解决回绕问题

    return true;

}


int myCircularQueueFront(MyCircularQueue* obj) {

    if(myCircularQueueIsEmpty(obj))

        return -1;

    return obj->a[obj->head];

}


int myCircularQueueRear(MyCircularQueue* obj) {

    if(myCircularQueueIsEmpty(obj))

        return -1;

    return obj->a[(obj->tail + obj->k) % (obj->k + 1)];//这里应该是obj->tail - 1 + (obj->k + 1),防止出现,tail - 1后出现-1的情况,但是因为我么们加上了k + 1,所以需要一个取模运算给它缩回去,整理一下就可以得到这个式子了;

}


void myCircularQueueFree(MyCircularQueue* obj) {

    free(obj->a);//malloc了两个,从小大大逐层释放

    obj->head = obj->tail = obj->k = 0;

    free(obj);

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值