1.前序
又有很久没有更新文章了,这次带你们手撕几道基础题;真的就和康纳吃饭一样简单!!!
如果还不会队列和栈的可以去看看之前写的博客; 栈的实现 队列概念以及实现 <- 快速传送
目录
1.前序
2.括号匹配问题,有效的括号 <- 快速传送
3.用队列实现栈 <- 快速传送
3.1 核心思路
3.2代码部分,上面是思路
4.用栈实现队列 <- 快速跳转
4.1这里用c语言实现的所以要复制一个栈
5.带环队列 <- 快速跳转
2.括号匹配问题,有效的括号 <- 快速传送
- 核心思路:
- 一定是左括号入栈,右括号在外面
- 入完数据尝试匹配,不相等 可以,如果相等可以走到最后返回true
- 左括号有数据;右括号没数据;此时说明返回false
- 右括号有数据;左括号没有;右括号还有没匹配的,此时*s有数据进了循环,if判断栈为NULL此时为false
- 根据上图分析,有4种情况
- 第一步,建立循环有数据就往下读取,左括号入栈,右括号不如栈,进入的数据拿去比对
- 先处理正常情况,没有进入到if判断(不匹配的情况),此时就可以证明时匹配上了,因为没有返回false
- 注意:不匹配的情况;当满足左括号和右括号对应的时候,此时就可以不会进if
bool isValid(char* s) {
ST str;
STInit(&str);
while(*s)//有数据就继续读
{
if(*s == '(' || *s == '{' || *s == '[')//是左括号就入栈
{
STPush(&str,*s);
}
else//尝试和右括号匹配
{
//为 0 时返回 true,然后执行if,返回false,匹配失败
if(STEmpty(&str))//经过前面的匹配后,此时栈为NULL了,但是*s 还有数据
{
STDestroy(&str);
return false;
}
char top = STTop(&str);//不管取没取到,先Pop,如果匹配成功就正常往下走
STPop(&str);
//不匹配的情况,不匹配直接返回
if(top == '(' && *s != ')' ||
top == '[' && *s != ']' ||
top == '{' && *s != '}')
{
//销毁空间,并返回
STDestroy(&str);
return false;
}
}
s++;//找下一个元素
}
//返回值为false说明有栈内有数据,但是前面while已经结束,没有字符可以匹配了
//意味着左括号比右括号的多 ,只要不匹配就返回false
bool ret = STEmpty(&str);
STDestroy(&str);
return ret;//正常返回
}
3.用队列实现栈 <- 快速传送
- 传参部分要注意,这个MyStack结构体里装着两个队列
- 这里要传&(que->q1)或者&que->q1,因为这是两个结构体,改变结构体需要结构体指针
- Create部分,首先需要给MyStack(匿名结构体)开辟空间,这里他让我们初始化两个队列,并且完成初始化,这部分就直接调用函数即可。
3.1 核心思路
核心思路:插入数据时始终插入到有数据的队列去;始终要空出一个队列,用来删除
- Push,实现栈的后进后出,第一次开始数据随便放,后面插入数据要放到有数据后
- QueueEmpty,这个函数为NULL返回true(表示没有数据),反之返回false,说明此时有数据;
- 注意:!取反,将结果取反,此代码中有数据返回false取反后得到true
- Pop,这里利用假设法;先假设q1有数据,假设失败那就是q2,这里的if判断同样用判空来判断是否有数据
- 这里while里面就是挪动数据了 ,此时你需要取到头元素,挪动size - 1个数据,留一个在que1里面pop掉,就模拟了栈的出栈
- QueuePop(NoEmpty);//只是被导到Empty,原空间还有数据,所以要Pop掉已经导过去的数据
- 这里根据题目要求,要保存数据,然后要pop掉这块空间,最后返回该值
- Top,取栈的top元素,也就是队列的尾元素,当然这个尾元素也是需要看,数据在哪一边,同样的配方,判NULL
- Empty栈的判NULL,竟然是由两个队列组成自然也就需要,两个都为NULL才行,&& 也是运算符;同时为NULL,返回true
- 这里的free,要了解结构体,开始初始化是什么,你就怎么销毁;
- 这里销毁时候先后顺序的,不能直接销毁obj,而是要先销毁obj里面两个的q1和q2才行
3.2代码部分,上面是思路
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* que = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&(que->q1));//通过结构体指针访问到q1,用初始化函数直接初始化
QueueInit(&(que->q2));
return que;
}
void myStackPush(MyStack* obj, int x) {//obj 是装着q1 和 q2两个结构体的
//分为两种情况,那个后面有数据有往哪里放,都没有随便放一个
if(!QueueEmpty(&(obj->q1)))//如果q1这个队列有数据,返回false,! 取反后true,执行if
{
QueuePush(&(obj->q1),x);
}
//obj->q2有数据,或者都没有
else
{
QueuePush(&(obj->q2),x);
}
}
int myStackPop(MyStack* obj) {
//假设法,因为当一方成立是,双方代码一样
Queue* Empty = &(obj->q1);//假设p1没数据
Queue* NoEmpty = &(obj->q2);//有数据
if(!QueueEmpty(&(obj->q1)))//非NULL,有数据;假设失败
{
Empty = &(obj->q2);
NoEmpty = &(obj->q1);
}
while(QueueSize(NoEmpty) > 1)//有数据的一方需要导到无数据的一方,个数size - 1
{
QueuePush(Empty,QueueFront(NoEmpty));//取到有数据的队头,导到无数据的一方
QueuePop(NoEmpty);//只是被导到Empty,原空间还有数据,所以要Pop掉已经导过去的数据
}
QuDataType ret = QueueFront(NoEmpty);
QueuePop(NoEmpty);
return ret;
}
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&(obj->q1)))//此时q1有数据,直接取到队列的队尾
{
return QueueBack(&(obj->q1));
}
else
{
return QueueBack(&(obj->q2));
}
}
bool myStackEmpty(MyStack* obj) {
//只有这两个队列同时为NULL,才说明没数据了;满足两个判空都为NULL,才会返回true,&&也相当于是运算符
return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}
void myStackFree(MyStack* obj) {
QueueDestroy(&(obj->q1));//首先要free掉q1和q2 这两个队列,然后才是obj
QueueDestroy(&(obj->q2));
free(obj);
}
4.用栈实现队列 <- 快速跳转
- 此题创建两个栈来实现,stpush只入数据,相当于入队;stpop只出数据,相当于出队
- Create,首先是初始化MyQueue 里面放上两个栈,栈也初始化;
- Push;push数据那么调用栈的push函数即可;注意:STPush只用来放数据
- Peek,返回开头元素;把数据都导到stpop这样刚好压栈的元素就在栈顶;
- 要让这里的stpop没有数据才能导入进去;只要stpop有数据就不要进
- 因为要始终保持队列的先进先出,假如stpush的数据导过来不就乱了
- 如果没进if说明有数据,那就不要导到stpop了
- 这里用动图说明原因;只有stpop数据出完才进数据
- Pop,删除队列开头的数据并返回元素;这里为什么调用一下myQueuePeek函数呢?
- 因为我们拿数据始终在stpop拿;那么stpop没数据自然要导!!!
- 有了数据直接取头元素,删除返回即可
- Empty;判断是为NULL,因为这个是模拟是队列的功能;所以两个都要为NULL
- Free;怎么申请的空间,怎么倒着销毁;和队列实现栈的销毁逻辑一样
4.1这里用c语言实现的所以要复制一个栈
typedef struct {
ST stpush;//入队
ST stpop;//出队
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&(obj->stpush));
STInit(&(obj->stpop));
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
STPush(&(obj->stpush),x);//stpush只入数据
}
int myQueuePeek(MyQueue* obj) {
if(STEmpty(&(obj->stpop)))//pop这边栈没数据就要导入数据
{
while(!STEmpty(&(obj->stpush)))//push有数据就到导
{
int top = STTop(&(obj->stpush));
STPush(&(obj->stpop),top);
STPop(&(obj->stpush));//数据挪到那边就要删除了
}
}
return STTop(&(obj->stpop));
}
int myQueuePop(MyQueue* obj) {
int front = myQueuePeek(obj);//stpop这边有数据才能删,如果不用调整刚好也拿到了
STPop(&(obj->stpop));
return front;
}
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&(obj->stpush)) && STEmpty(&(obj->stpop));//两个都为NULL才为NULL
}
void myQueueFree(MyQueue* obj) {
STDestroy(&(obj->stpush));
STDestroy(&(obj->stpop));
free(obj);
}
5.带环队列 <- 快速跳转
- Create,这个部分就是初始化了,需要开辟两个空间,一个是MyCircularQueue结构体的另外一个就是arr 数组的
- IsEmpty,判NULL
- 多开一块空间的意义是,防止head ==tail时,不知道是满还是NULL;
- IsFull,判满
- EnQueue,向循环队列插入一个元素;在开始之前肯定要判断是否为满,满了就不能插入了
- DeQueue,从循环队列中删除一个元素;在开始之前要判NULL,不然你删个屁
- Front;开始之前肯定要判NULL;head位置的数据没啥问题直接取到就行,特殊情况在tail,因为指向下一个
- Rear,取尾的数据;普通情况正常取;特殊情况就是有效数据正好是最后一个,当tail指向下一个位置(也就是下标0);
typedef struct {
int* arr;
int head;
int tail;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* Cqueue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
if(Cqueue == NULL)
{
perror("malloc Cqueuq");
return 0;
}
//多开辟一个空间防止假溢出,这里不需要临时变量,也和数据丢不丢是没关系
Cqueue->arr = (int*)malloc(sizeof(int) * (k + 1));
Cqueue->head = Cqueue->tail = 0;//刚开始都指向下标0
Cqueue->k = k;
return Cqueue;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->head == obj->tail;//判断当前存储的位置是否为NULL
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return ((obj->tail + 1) % (obj->k + 1)) == obj->head;//满了返回true
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))//判满
return false;
obj->arr[obj->tail] = value;
obj->tail++;
obj->tail %= (obj->k + 1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))//判NULL
return false;
obj->head++;
obj->head %= (obj->k + 1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->arr[obj->head];//取当前数据
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->arr[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
free(obj);
}
- free,空间的销毁就是,怎么申请怎么销毁;假如先申请的obj后申请的obj->arr;那么就反着销毁
总结;
- 最近改变了一下学习方式;上完课要及时写作业,而且要独立完成效果最好
- 反正都这么菜了,多进步一点也算好的;总之不能止步不前