栈和递归——刷题oj及概念选择题

目录

1.编程oj题

1.1有效的括号

1.1.1题目

1.1.2思路

1.1.3代码

1.2用队列实现栈

1.2.1题目

1.2.2思路

1.2.3代码

1.3用栈实现队列

1.3.1题目

1.3.2思路

1.3.3代码

1.4设计循环队列

1.4.1题目

1.4.2思路

1.4.3代码

1.4.4取模

2.概念选择题

2.1考察出栈入栈

2.2考察出栈入栈判断

2.3考察循环队列满与空

2.4考察队列基本运算

2.5考察循环队列取模


1.编程oj题

1.1有效的括号

1.1.1题目

力扣

1.1.2思路:

不能数数量,数量对得上不一定是正确的

用栈来解决:

左括号入栈

右括号出栈

1.1.3代码

//先写一个栈
//
typedef char STDatatype;
typedef struct Stack
{
    STDatatype* a;
    int top;        // 栈顶
    int capacity;
}ST;
 
 
void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDatatype x);
void StackPop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);
STDatatype StackTop(ST* ps);
 
 
void StackInit(ST* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = 0; // -1
    ps->capacity = 0;
}
 
 
void StackDestroy(ST* ps)
{
    assert(ps);
    if (ps->a)
    {
        free(ps->a);
    }
    ps->a = NULL;
    ps->top = 0; 
    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 = realloc(ps->a, sizeof(STDatatype)*newcapacity);
        if (tmp == NULL)
        {
            printf("rellaoc fail\n");
            exit(-1);
        }
        ps->a = tmp;
        ps->capacity = newcapacity;
    }
 
 
    ps->a[ps->top] = x;
    ps->top++;
}
 
 
void StackPop(ST* ps)
{
    assert(ps);
    assert(!StackEmpty(ps));
 
 
    --ps->top;
}
 
 
bool StackEmpty(ST* ps)
{
    assert(ps);
 
 
    return ps->top == 0;
}
 
 
int StackSize(ST* ps)
{
    assert(ps);
 
 
    return ps->top;
}
 
 
STDatatype StackTop(ST* ps)
{
    assert(ps);
    assert(!StackEmpty(ps));
 
 
    return ps->a[ps->top - 1];
}
 
 
bool isValid(char * s)
{
    //定义一个栈
    ST st;
    //初始化
    StackInit(&st);
    bool match = true;
    
    //字符串,不到\0不终止
    while(*s)
    {
        //硬判断
        //如果字符串等于左括号中的任意一个
        if(*s == '[' || *s == '(' || *s == '{')
        {
            //出栈
            StackPush(&st, *s);
            ++s;
        }
        //右括号
        else
        {
          //判断栈是否为空
            if(StackEmpty(&st))
            {
                //小心内存泄漏,内存泄漏不会发出警告
                StackDestroy(&st);
                match = false;
                break;
            }
 
   //取出栈顶元素
            char ch = StackTop(&st);
            StackPop(&st);
            
            //不匹配的情况就false
            if((*s == ']' && ch != '[')
            || (*s == '}' && ch != '{')
            || (*s == ')' && ch != '('))
            {
                StackDestroy(&st);
                match = false;
                break;
            }
            else
            {
                ++s;
            }
        }
    }
 
 
    if(match == true)
    {
        match = StackEmpty(&st);
    }
 
 //销毁
    StackDestroy(&st);
    
    return match;
}

1.2用队列实现栈

1.2.1题目

力扣

只使用队列取操作后进先出

考察性质,不考虑效率

1.2.2思路

一个队列是空的----用来倒数据

一个队列用来存储数据

1.2.3代码

//先写一个队列
//
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>



//设置两个指针
typedef int QDataType;
typedef struct QueueNode
{
    struct QueueNode* next;
    QDataType data;
}QNode;

typedef struct Queue
{
    //int size;
    //定义变量效率高,插入++,删除--最后返回size就可以
    QNode* head;
    QNode* tail;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//入队列
void QueuePush(Queue* pq, QDataType x);
//出队头的数据
void QueuePop(Queue* pq);
//获取队列头部元素
QDataType QueueFront(Queue* pq);
//获取队列队尾元素
QDataType QueueBack(Queue* pq);
//检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);
//获取队列中有效元素个数
int QueueSize(Queue* pq);

void QueueInit(Queue* pq)
{
    assert(pq);
    pq->head = pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
    assert(pq);
    QNode* cur = pq->head;
    while (cur)
    {
        QNode* next = cur->next;
        free(cur);
        cur = next;
    }

    pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
    
    assert(pq);
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    //检查野指针
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    newnode->data = x;
    newnode->next = NULL;

    //只需尾插
    if (pq->tail == NULL)
    {
        pq->head = pq->tail = newnode;
    }
    else
    {
        pq->tail->next = newnode;
        pq->tail = newnode;
    }
}

void QueuePop(Queue* pq)
{
    assert(pq);
    //暴力检查
    assert(!QueueEmpty(pq));

    // 只有一个结点
    if (pq->head->next == NULL)
    {
        free(pq->head);
        pq->head = pq->tail = NULL;
    }
    // 含有多个结点
    else
    {
        QNode* next = pq->head->next;
        free(pq->head);
        pq->head = next;
    }
}
 
QDataType QueueFront(Queue* pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));

    return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
    assert(pq);
    assert(!QueueEmpty(pq));

    return pq->tail->data;
}

bool QueueEmpty(Queue* pq)
{
    assert(pq);
    return pq->head == NULL;
}

int QueueSize(Queue* pq)
{
    assert(pq);
    QNode* cur = pq->head;
    int size = 0;
    while (cur)
    {
        ++size;
        cur = cur->next;
    }

    return size;
}

//

//把匿名结构体直接typedef成MyStack,不然匿名结构体无法进行定义变量
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

//初始化
MyStack* myStackCreate() {
    //避免野指针
    MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
    //光有结构体obj不行,还得把obj里面的结构初始化了
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);

    return obj;

}

//入数据
void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }

}

//倒数据
int myStackPop(MyStack* obj) {
//假设一个空,一个非空
    Queue* emptyQ = &obj->q1;
    Queue* nonEmptyQ = &obj->q2;
        //如果假设错了,就修正
    if(!QueueEmpty(&obj->q1))
    {
        emptyQ = &obj->q2;
        nonEmptyQ = &obj->q1;
    }

//非空栈里面的队头的数据往空栈里面倒,直到非空只有一个数据
    while(QueueSize(nonEmptyQ) > 1)
    {
        QueuePush(emptyQ, QueueFront(nonEmptyQ));
        QueuePop(nonEmptyQ);
    }
   //移除并返回栈顶元素
    int top = QueueFront(nonEmptyQ);
    QueuePop(nonEmptyQ);

    return top;

}

int myStackTop(MyStack* obj) {
    //谁不为空就取谁队尾的数据
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else{
        return QueueBack(&obj->q2);
    }

}

//判空
bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);

}

void myStackFree(MyStack* obj) {
    //依次malloc的也要依次释放
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q1);

free(obj);

}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

1.3用栈实现队列

1.3.1题目

力扣

用栈去实现先进先出

1.3.2思路

出数据之后,不需要倒回来

因为栈倒一遍顺序就颠倒了

需要两个栈,一个出栈一个入栈

1.3.3代码

//先写一个栈 


typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;

void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDataType x);
void StackPop(ST* ps);
STDataType StackTop(ST* ps);
bool StackEmpty(ST* ps);
int StackSize(ST* ps);


void StackInit(ST* ps)
{
    assert(ps);
    ps->a = NULL;
    ps->top = 0;
    ps->capacity = 0;
}

void StackDestroy(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)
        {
            printf("realloc fail\n");
            exit(-1);
        }

        ps->a = tmp;
        ps->capacity = newCapacity;
    }

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

void StackPop(ST* ps)
{
    assert(ps);
    assert(!StackEmpty(ps));
    ps->top--;
}

STDataType StackTop(ST* ps)
{
    assert(ps);
    assert(!StackEmpty(ps));

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

bool StackEmpty(ST* ps)
{
    assert(ps);

    return ps->top == 0;
}

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) {
  StackPush(&obj->pushst, x);

}


 //从队列的开头移除并返回元素
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->popst))
{
    //如果pop栈为空,就把push栈的数据倒过来
    //popst有数据的话是直接出
    while(!StackEmpty(&obj->pushst))
    {
        StackPush(&obj->popst,StackTop(&obj->pushst));
        StackPop(&obj->pushst);
        
    }
}


//返回popst栈顶的数据
int front = StackTop(&obj->popst);
StackPop(&obj->popst);
return front;

}



//返回队列开头的元素
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->popst))
{
     //如果pop栈为空,就把push栈的数据倒过来
    while(!StackEmpty(&obj->pushst))
    {
        StackPush(&obj->popst,StackTop(&obj->pushst));
        StackPop(&obj->pushst);
        
    }
}
//直接返回popst栈顶数据即可
return StackTop(&obj->popst);
}


//判空
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->popst) && StackEmpty(&obj->pushst);
}

void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->pushst);
StackDestroy(&obj->popst);

free(obj);
}

/**
 * Your MyQueue struct will be instantiated and called as such:
 * MyQueue* obj = myQueueCreate();
 * myQueuePush(obj, x);
 
 * int param_2 = myQueuePop(obj);
 
 * int param_3 = myQueuePeek(obj);
 
 * bool param_4 = myQueueEmpty(obj);
 
 * myQueueFree(obj);
*/

1.4设计循环队列

1.4.1题目

力扣

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。

环形队列可以使用数组实现,也可以使用循环链表实现

1.4.2思路

给一个front和tail

删除的时候是head往下走,结点并不会释放,循环利用

满了之后走一个进一个

但无法区分空和满

解决:

  1. 增加一个size(flag)记录数据个数,同时用来记录满和空
  2. 多开一个空间,不存储数据(较实用)

tail->next == head表示已经满了

再换一个角度

可以考虑使用数组的方式实现会更加方便

假设存储4个数据,开5个空间

pop的时候head往前走

tail->next = head 就满了

找尾容易,但需要处理边界

此题使用数组比链表容易

obj->tail代表数组下标

1.4.3代码

//
typedef struct {
    int* a;//数组
    int k;//队列长度
    int head;//头
    int tail;//尾

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    //先malloc一个结构体
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//malloc结构体里的数组
obj->a = malloc(sizeof(int)*(k+1));
//初始化
obj->head = obj->tail = 0;
obj->k = k;

return obj;
}


//列表为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
//相等的时候一定为空
return obj->head == obj->tail;
}


//列表满了
bool myCircularQueueIsFull(MyCircularQueue* obj) {
int next = obj->tail+1;
if(next == obj->k+1)
next = 0;

return next == obj->head;
}


//入队列
//向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //满了(无法插入)
if(myCircularQueueIsFull(obj))
  return false;

  obj->a[obj->tail] = value;
  obj->tail++;
  
//处理边界,特殊情况
  if(obj->tail == obj->k+1)
  obj->tail = 0;

  return true;
}


//从循环队列中删除一个元素。如果成功删除则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //列表为空,不能再删了
    if(myCircularQueueIsEmpty(obj))
  return false;

  ++obj->head;

//处理边界
  if(obj->head == obj->k+1)
  obj->head = 0;

  return true;

}

//从队首获取元素。如果队列为空,返回 -1 。
int myCircularQueueFront(MyCircularQueue* obj) {
       if(myCircularQueueIsEmpty(obj))
  return -1;

//直接取队头的数据
  return obj->a[obj->head];

}


//获取队尾元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
  return -1;
  
  //处理边界,使其回绕边界
  int prev = obj->tail-1;
  if(obj->tail == 0)
  prev = obj->k;

  return obj->a[prev];
}




void myCircularQueueFree(MyCircularQueue* obj) {
    //先释放小的,再释放大的
    //如果先释放obj,那么a指向的空间会内存泄漏
free(obj->a);
free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

1.4.4取模

可以用取模的思想,但全部用取模可能会出错

需要控制边界

这里写两个取模的例子:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
  return false;

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

  //if(obj->tail == obj->k+1)
  //obj->tail = 0;

//5%5 = 0 就绕回第一个了
obj->tail %= (k+1);

  return true;
}

int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
  return -1;
 // int prev = obj->tail-1;
 // if(obj->tail == 0)
 // prev = obj->k;
 
 //先加再取模
int prev = obj->tail-1+(k+1);
prev% = (k+1);

  return obj->a[prev];
  
  //也可以直接return
  //return  obj->a[(obj->tail+k)%(k+1)];
}

2.概念选择题

2.1考察出栈入栈

一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出

栈的顺序是( )。

A 12345ABCDE

B EDCBA54321

C ABCDE12345

D 54321EDCBA

按照栈的性质,直接按顺序反过来即可

2.2考察出栈入栈判断

若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

A 1,4,3,2

B 2,3,4,1

C 3,1,4,2

D 3,4,2,1

直接看答案来进行推断。

A.先入1再出1,依次入234,再出即变为432

B.先入12再出2,再入3,出3,入4出4,最后出1

C.先入123,出3,接下来只能出2,不能直接出1,必须按顺序,所以C不可行

D.先入123,出3,入4出4,再出21

2.3考察循环队列满与空

循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作

后, front=rear=99 ,则循环队列中的元素个数为( )

A 1

B 2

C 99

D 0或者100

按队列的性质:当front=rear时,队列为空(front追到rear)或者为满(rear追到front)

由于入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,故队空和队满时,头尾指针均相等,而且题目说存储空间及初始状态都是100,所以要么满(100),要么空(0)。

2.4考察队列基本运算

以下( )不是队列的基本运算?

A 从队尾插入一个新元素

B 从队列中删除第i个元素

C 判断一个队列是否为空

D 读取队头元素的值

队列是只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表

队头删除,队尾插入

2.5考察循环队列取模

现有一循环队列,其队头指针为front队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设队头不存放数据)

A (rear - front + N) % N + 1

B (rear - front + N) % N

C ear - front) % (N + 1)

D (rear - front + N) % (N - 1)

 1.rear在后,front在前,两个相减是正值

但也可以用rear+N-front之后去%N因为此时就可以把N消了

2.rear在前,front在后,两个相减是负值

相当于rear也跑在front之后但因为是循环队列,所以绕回来了,需要再加一个N

猜你喜欢

转载自blog.csdn.net/Ll_R_lL/article/details/124952165
今日推荐