栈和队列经典oj面试题

作者:[南航科院小张
南航科院小张的博客
专栏:从c语言的入门到进阶
学习知识不只是要懂,还要会用;想要找到好的工作,这里给大家介绍一件可以斩获诸多大厂offer的利器–牛客网
点击免费注册和我一起开始刷题之路吧!!!


一、有效的括号

在这里插入图片描述

题解:这里我们可以用栈的特性:先进后出;要是遇到左括号就入栈,要是右括号就和栈顶的元素进行比较,看是否匹配,要是匹配,就把栈顶出栈,要是不匹配,直接返回false;下面有几个要注意的点:

  1. 全部都是左括号;这个情况的话就是可以出循环,但是我们要在后面判断一下栈是否为空,要是空,那么就符合题意,返回true;否则返回false;
  2. 全部是右括号;这个问题当第一个右括号的时候就会返回法false,因为不匹配;
  3. 右括号比左括号多;这个情况的话也是最后面的判断栈是否空可以解决;
    下面代码要注意的是c语言没有栈,我自己实现的一个栈,然后就是在返回true或者false之前的时候一定要销毁栈!!!
// 支持动态增长的栈
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 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);
void StackInit(Stack* ps) {
	assert(ps);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	if (ps->top == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity *2;
		STDataType* temp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (temp == NULL) {
			perror("realloc fail");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

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

STDataType StackTop(Stack* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

int StackSize(Stack* ps) {
	assert(ps);
	return ps->top;
}

int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->top ==0;
}

void StackDestroy(Stack* ps) {
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
bool isValid(char * s){
Stack st;
StackInit(&st);
while(*s){
    if(*s=='('||*s=='{'||*s=='['){
        StackPush(&st,*s);
    }
    else{
        if(StackEmpty(&st)){//为了防止栈为空,下面的StackPop接口还在用;
            StackDestroy(&st);
            return false;
        }
        char top=StackTop(&st);
        if(*s==')'&&top!='('
        ||*s=='}'&&top!='{'
        ||*s==']'&&top!='[')
        {
            StackDestroy(&st);
            return false;
        }
        else{
            StackPop(&st);
        }
    }
    ++s;
}
bool flag=StackEmpty(&st);
StackDestroy(&st);
return flag;
}

二、用队列实现栈

在这里插入图片描述

思路很简单,但是用代码实现起来还是有点挑战的哈哈;
栈的特点:先进后出;队列特点:先进先出;
用两个队列实现栈,利用队列的特点,把先进的数据导到另外一个队列里面,然后剩下一个数据,然后出这个数据(pop数据),这样就实现了栈的出数据特点,栈顶出数据;我们要控制那里的队列为空就往空队列导数据,非空队列留下一个来出数据,就这样一直在两个队列里面操作,然后入数据就往非空队列里入数据;我这里是直接写的队列哈,所以比较多代码;

// 链式结构:表示队列 
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构 
typedef struct Queue
{
	size_t sz;
	QNode* head;
	QNode* tail;
}Queue;

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

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

void QueuePush(Queue* q, QDataType data) {
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		perror("malloc fail");
		exit(-1);
	}
	else {
		newnode->data = data;
		newnode->next = NULL;
	}
	if (q->head == NULL) {
		q->head = q->tail = newnode;
		q->sz++;
	}
	else {
		q->tail->next = newnode;
		q->tail = newnode;
		q->sz++;

	}
}
void QueuePop(Queue* q) {
	assert(q);
	assert(!QueueEmpty(q));
	if (q->head->next == NULL) {
		free(q->head);
		q->head = q->tail = NULL;
		q->sz--;
	}
	else {
	QNode* cur = q->head->next;
	free(q->head);
	q->head = cur;
	q->sz--;

	}
	
}

QDataType QueueFront(Queue* q) {
	assert(q);
	assert(!QueueEmpty(q));
	return q->head->data;
}

QDataType QueueBack(Queue* q) {
	assert(q);
	assert(!QueueEmpty(q));
	return q->tail->data;
}
int QueueSize(Queue* q) {
	assert(q);
	return q->sz;
}
int QueueEmpty(Queue* q) {
	assert(q);
	return q->head == NULL && q->tail == NULL;
}
void QueueDestroy(Queue* q) {
	assert(q);
	QNode* cur = q->head;
	while (cur) {
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->head = q->tail = NULL;
}
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    if(obj==NULL){
        perror("malloc fail");
        exit(-1);
    }
    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* noEmptyQ=&obj->q2;
    if(!QueueEmpty(&obj->q1)){
        EmptyQ=&obj->q2;
        noEmptyQ=&obj->q1;
    }
    while(QueueSize(noEmptyQ)>1){
        int top=QueueFront(noEmptyQ);
        QueuePop(noEmptyQ);
        QueuePush(EmptyQ,top);
    }
    int top=QueueFront(noEmptyQ);
    QueuePop(noEmptyQ);
    return top;
}

int myStackTop(MyStack* obj) {
    Queue* EmptyQ=&obj->q1;
    Queue* noEmptyQ=&obj->q2;
    if(!QueueEmpty(&obj->q1)){
        EmptyQ=&obj->q2;
        noEmptyQ=&obj->q1;
    }
    while(QueueSize(noEmptyQ)>1){
        int top=QueueFront(noEmptyQ);
        QueuePop(noEmptyQ);
        QueuePush(EmptyQ,top);
    }
    int top=QueueFront(noEmptyQ);
    QueuePop(noEmptyQ);
    QueuePush(EmptyQ,top);
    return top;
}

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

void myStackFree(MyStack* obj) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    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);
*/

三. 用栈实现队列

在这里插入图片描述

思路很简单,但是用代码实现起来还是有点挑战的哈哈;
栈的特点:先进后出;队列特点:先进先出;
用栈来模拟队列的特点,有了上面的题解,想必我们很容易想到方法了,这题比上一题简单哦,我们搞两个栈,一个栈专门用来入数据,一个栈专门用来出数据,因为入数据完后将全部数据到入专门的出数据的栈里面的时候,出数据的栈的数据是符合队列的出数据规则的了;所以可以对两个栈分开工作;
要注意的是:出数据的栈要判断是否为空,要是空的话就导数据,然后再出;

//Stack
// 支持动态增长的栈
typedef int 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 
int StackEmpty(Stack* ps);
// 销毁栈 

void StackInit(Stack* ps) {
	assert(ps);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}
void StackPush(Stack* ps, STDataType data) {
	assert(ps);
	if (ps->top == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* temp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (temp == NULL) {
			perror("realloc fail");
			exit(-1);
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

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

STDataType StackTop(Stack* ps) {
	assert(ps);
	assert(!StackEmpty(ps));
	return ps->a[ps->top - 1];
}

int StackSize(Stack* ps) {
	assert(ps);
	return ps->top;
}

int StackEmpty(Stack* ps) {
	assert(ps);
	return ps->top == 0 ? 1 : 0;
}

void StackDestroy(Stack* ps) {
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}


typedef struct {
    Stack PopST;
    Stack PushST;
} MyQueue;


MyQueue* myQueueCreate() {
MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
if(obj==NULL){
    perror("malloc fail");
    exit(-1);
}
StackInit(&obj->PopST);
StackInit(&obj->PushST);
return obj;
}

void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PushST,x);
}
//判断PopST队列是否为空,要是空,就导数据;
void PushSTTOPopST(MyQueue* obj){
if(StackEmpty(&obj->PopST)){
    while(!StackEmpty(&obj->PushST)){
    int top=StackTop(&obj->PushST);
    StackPop(&obj->PushST);
    StackPush(&obj->PopST,top);
}
}

}
int myQueuePop(MyQueue* obj) {
PushSTTOPopST(obj);
int top=StackTop(&obj->PopST);
StackPop(&obj->PopST);
return top;
}

int myQueuePeek(MyQueue* obj) {
PushSTTOPopST(obj);
int top=StackTop(&obj->PopST);
return top;
}

bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PopST)&&StackEmpty(&obj->PushST);
}

void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->PopST);
StackDestroy(&obj->PushST);
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);
*/

四. 设计循环队列

在这里插入图片描述

这道题其实难,可以用链表也可以用顺序表,不过我觉得用顺序表更简单容易实现一点;为了可以判断什么时候是满的什么时候是空的,我们有两个方法,一个是用size来计数,第二个是直接加一个节点;加了一个节点后当数据为空,就是head等于tail的时候,当(tail+1)%N等于head的时候为满数据,这个时候不能再插入数据了;我们一定要注意这是一个循环的,到边界的时候一定要有回去,这个时候我的实现是%N,返回尾元素则很巧,(tail-1+n)%n;我们要画图多观察才容易理解;要是不容易理解代码,可以画图然后看着代码来还原思路;

typedef struct {
int* a;
int head;
int tail;
int N;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
if(obj==NULL){
    perror("obj malloc fail");
    exit(-1);
}
obj->a=(int*)malloc(sizeof(int)*(k+1));//加一个空间,便于判断空和满;
if(obj->a==NULL){
    perror("a malloc fail");
    exit(-1);
}
obj->N=k+1;//作用很大,便于下标的循环恢复;
obj->head=obj->tail=0;
return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->tail==obj->head;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->tail+1)%(obj->N)==obj->head;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->tail]=value;
obj->tail++;
obj->tail%=obj->N;//防止在最大下标+1的情况,要循环进行恢复最小下标;
return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->head++;
obj->head%=obj->N;
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-1+obj->N)%(obj->N)];
}


void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
obj->a=NULL;
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);
*/

总结

在这里插入图片描述

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: 南京信息工程大学oj系统题库是为了帮助学生提高编程能力、加强算法训练而开设的,题库中包含了各种类型的题目,涵盖了计算机科学与技术、软件工程、网络工程、物联网等专业的知识点。题库中的题目难度有高有低,从初级到高级、从简单到复杂都有涵盖,可以满足不同层次、不同需求的学生。 南京信息工程大学oj系统题库的题目内容丰富多样,可以涉及到各种算法、数据结构、编程语言等方面的知识。学生可以通过解答这些题目,了解和掌握不同算法和数据结构的应用场景,提高编程能力和实践动手能力。 南京信息工程大学oj系统题库的使用非常方便,学生只需登录系统,在题库中选择自己感兴趣或者需要练习的题目,然后根据题目的要求,编写相应的代码进行提交。系统会自动对代码进行评测,给出相应的反馈和评分,帮助学生了解自己的编程能力和解题能力。 同时,南京信息工程大学oj系统题库也可以作为教师授课和学生自学的辅助工具。老师可以根据课程内容,选择合适的题目进行布置作业或者考试,提升学生的学习兴趣和积极性。对于学生自学的话,可以通过解答题库中的题目,巩固和加深对于课程知识的理解和掌握。 总之,南京信息工程大学oj系统题库是一个重要的学习资源,可以帮助学生巩固和提高编程能力,为他们在计算机科学与技术以及相关领域的学习和就业打下坚实的基础。 ### 回答2: 南京信息工程大学oj系统题库是该校为了方便学生进行编程实践和提高编程能力而特别开设的一个在线题库。该题库的目的是帮助学生巩固课堂所学的理论知识,实践编程技巧,并培养学生解决实际问题的能力。 南京信息工程大学oj系统题库中包含了各种难度级别的编程题目,涵盖了计算机科学与技术、通信工程、软件工程等多个专业领域的知识点。学生可以通过选定特定的题目,根据自身编程能力的提升情况逐渐挑战难度更大的题目。 在南京信息工程大学oj系统题库中,学生可以提交自己的代码,系统会自动对代码进行评测并返回结果。这样可以有效地帮助学生查找代码中的错误,并及时进行修改和改进。除此之外,学生还可以查看别人的解题思路和代码实现,借鉴他人的经验,丰富自己的编程思维。 南京信息工程大学oj系统题库还提供了在线编程环境,学生无需安装额外的编译器或者IDE,只需通过浏览器就可以完成编程任务。这样方便学生的学习和实践,并提高了学生在编程中的效率和体验。 总之,南京信息工程大学oj系统题库是一个宝贵的资源,能够提供给学生进行编程实践和提高编程能力的机会。通过挑战不同难度的题目,学生可以不断提升自己的编程水平,更好地适应未来的学习和工作需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值