【代码随想录算法训练营】【C语言版】第十天| 栈与队列的理论基础、232.用栈实现队列、225. 用队列实现栈、20. 有效的括号、1047. 删除字符串中的所有相邻重复项

栈与队列

理论基础

文章讲解:https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。

栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。

所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。

那么问题来了,STL 中栈是用什么容器实现的?

从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。

我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。

deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。

队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。

也可以指定list 为起底层实现。所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。

在这里插入图片描述
在这里插入图片描述

232.用栈实现队列

232. Implement Queue using Stacks

题目链接/文章讲解/视频讲解:https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.html

代码:

//思路:

//出队列pop就是把最早的拿出(取首元素peek同理),即如果输出栈为空(即输入栈和输出栈都为空),就把输入栈(最早的在最里面)全导入输出栈,再取出来的最外面的就是最早的;如果输出栈非空,就直接取出最外面的就好

//入队列push就是把元素放到末尾,直接放入数据栈即可

//返回队列的首元素peek,即如果输出栈非空,就直接取出最外面的顶部元素,否则取出输入栈的首元素

//判断队列为空,即如果输入栈和输出栈都为空则队列为空,否则不为空

//释放队列的内存空间,就是将输入栈和输入栈顶索引重置为-1

//注:

//return obj->in[0];//首元素不在顶部时可查询但不可直接操作

//思路:
//出队列pop就是把最早的拿出(取首元素peek同理),即如果输出栈为空(即输入栈和输出栈都为空),就把输入栈(最早的在最里面)全导入输出栈,再取出来的最外面的就是最早的;如果输出栈非空,就直接取出最外面的就好
//入队列push就是把元素放到末尾,直接放入数据栈即可
//返回队列的首元素peek,即如果输出栈非空,就直接取出最外面的顶部元素,否则取出输入栈的首元素
//判断队列为空,即如果输入栈和输出栈都为空则队列为空,否则不为空
//释放队列的内存空间,就是将输入栈和输入栈顶索引重置为-1
//注:
//return obj->in[0];//首元素不在顶部时可查询但不可直接操作
typedef struct {
    int outTop;//输出栈顶索引
    int inTop;//输入栈顶索引
    //因为题目说最多调用 100 次 push、pop、peek 和 empty,所以栈大小取100
    int out[100];//输出栈
    int in[100];//输入栈
} MyQueue;

//创建栈指针
//先分配内存空间,再初始化栈顶索引为-1,最后返回指针
MyQueue* myQueueCreate() {
    MyQueue* queue =(MyQueue*)malloc(sizeof(MyQueue));
    queue->outTop=-1;
    queue->inTop=-1;
    return queue;
}

//入队列push就是把元素放到末尾,直接放入数据栈即可
void myQueuePush(MyQueue* obj, int x) {
    obj->inTop++;// 输入栈顶索引加1
    obj->in[obj->inTop]=x;// 将元素x放入输入栈
}
//出队列pop就是把最早的拿出(取首元素peek同理),即如果输出栈为空,就把输入栈(最早的在最里面)全导入输出栈,再取出来的最外面的就是最早的;如果输出栈非空,就直接取出最外面的顶部元素就好
int myQueuePop(MyQueue* obj) {
    int temp;
    //如果输出栈为空,就把输入栈(最早的在最里面)全导入输出栈
    if(obj->outTop==-1){
        while(obj->inTop>-1){
            obj->out[++obj->outTop]=obj->in[obj->inTop--];
        }
    }
    //取出输出栈最外面的顶部元素
    temp=obj->out[obj->outTop--];
    return temp;
}

//返回队列的首元素peek,即如果输出栈非空,就直接取出最外面的顶部元素,否则取出输入栈的首元素
int myQueuePeek(MyQueue* obj) {
    if(obj->outTop>-1){
        return obj->out[obj->outTop];
    }
    return obj->in[0];//首元素不在顶部时可查询但不可直接操作
}

//判断队列为空,即如果输入栈和输出栈都为空则队列为空,否则不为空
bool myQueueEmpty(MyQueue* obj) {
    if((obj->outTop==-1)&&(obj->inTop==-1)){
        return true;
    }
    return false;
}

//释放队列的内存空间,就是将输入栈和输入栈顶索引重置为-1
void myQueueFree(MyQueue* obj) {
    obj->inTop=-1;
    obj->outTop=-1;
}

/**
 * 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);
*/
参考资料:

代码随想录算法训练营第十天| 232.用栈实现队列 、 225. 用队列实现栈https://blog.csdn.net/weixin_48369273/article/details/135703310

Typedef Struct 用法详解和用法小结https://www.cnblogs.com/lzjsky/archive/2010/11/24/1886717.html

在这里插入图片描述

225. 用队列实现栈

225. Implement Stack using Queues

题目链接/文章讲解/视频讲解:https://programmercarl.com/0225.%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.html

不熟的知识点:

rear尾指针指向的是尾元素后一位

虽然front和rear是int类型,但相当于数组的第front和rear位,可以作为指示队列开头结尾在数组中的位置指针,所以front不一定为0

虽然front和rear是int类型,但相当于数组的第front和rear位,可以作为指示队列开头结尾在数组中的位置指针,所以front不一定为0

代码:

//思路:
//用两个队列实现pop就是将队列1中除了首元素的所有元素放入队列2,再从队列2中放回队列1
//用一个队列实现pop就是将除了首元素的所有元素依次取出再从尾部加入队列

//思路:
//用两个队列实现pop就是将队列1中除了首元素的所有元素放入队列2,再从队列2中放回队列1
//用一个队列实现pop就是将除了首元素的所有元素依次取出再从尾部加入队列
#define maxSize 100//定义队列的最大容量为100,因为题目规定最多调用100 次 push、pop、top 和 empty

typedef struct {
    int front1,rear1;
    int data1[maxSize];
} MyStack;

//创建新的队列,就是先分配内存,再将首尾指针都赋值为0,返回新分配的队列指针
MyStack* myStackCreate() {
    MyStack* temp=(MyStack*)malloc(sizeof(MyStack));
    temp->front1=0;
    temp->rear1=0;
    return temp;
}

//向栈中压入一个元素push,就是队列尾部加入元素移动尾指针即可
void myStackPush(MyStack* obj, int x) {
    if(obj->front1==(obj->rear1+1)%maxSize)  return;//如果front1等于(rear1+1)%MAX_SIZE,说明循环队列已满,退出函数
    obj->data1[obj->rear1]=x;
    obj->rear1=(obj->rear1+1)%maxSize;
}

//从栈中弹出一个元素pop,就是将除了首元素的所有元素依次取出再从尾部加入队列
int myStackPop(MyStack* obj) {
    int temp=obj->rear1;//temp记录原尾指针位置
    while((obj->front1+1)%maxSize!=temp){//如果相等,即首指针移动到原来尾指针的前一位(即原来的尾元素位置),即把除了尾元素的所有元素都遍历一遍
        obj->data1[obj->rear1]=obj->data1[obj->front1];//将首元素赋值给尾元素后一位
        obj->rear1=(obj->rear1+1)%maxSize;//将尾指针后移
        obj->front1=(obj->front1+1)%maxSize;//将首指针后移继续遍历元素
    }
    int temp1=obj->data1[obj->front1];//temp1记录首指针位置,原来的尾元素位置
    obj->front1=(obj->front1+1)%maxSize;//将首指针后移,即从队列中将尾元素弹出
    return temp1;//返回原来尾元素位置
}

//返回栈顶元素的值,就是先pop取出栈顶元素,再push把栈顶元素压回去
int myStackTop(MyStack* obj) {
    int temp=myStackPop(obj);
    myStackPush(obj,temp);
    return temp;
}

//判断栈是否为空,即判断队列首尾指针是否相同
bool myStackEmpty(MyStack* obj) {
    if(obj->front1==obj->rear1) return true;
    return false;
}

//释放栈,就是让队列首指针移动到尾指针处
void myStackFree(MyStack* obj) {
    obj->front1=obj->rear1;
}

/**
 * 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);
*/
参考资料:

【数据结构】手写循环顺序队列【纯c语言版】

https://blog.csdn.net/weixin_43113381/article/details/121363822

使用C语言实现队列https://blog.csdn.net/Keep_Trying_Go/article/details/126289222

如果front1等于(rear1+1)%MAX_SIZE,说明循环队列已满,退出函数
在这里插入图片描述

代码随想录算法训练营第十天| 232.用栈实现队列 、 225. 用队列实现栈https://blog.csdn.net/weixin_48369273/article/details/135703310

20. 有效的括号

20. Valid Parentheses

题目链接/文章讲解/视频讲解:https://programmercarl.com/0020.%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7.html

知识点:链栈

在这里插入图片描述

使用C语言实现链栈(带头结点和不带头结点)

https://blog.csdn.net/Keep_Trying_Go/article/details/126284714

解决报错:

解决报错1(error: conflicting types for ‘stackEmpty’; have ‘_Bool(struct stackNode *)’ [solution.c]):因为定义ElemType stackPop函数在定义bool stackEmpty函数之前,而ElemType stackPop函数中用了bool stackEmpty函数,调换两函数顺序即可解决。

解决报错2(Line 13: Char 5: error: unknown type name ‘stackNode’ [solution.c]):用typedef struct stackNode和struct stackNode* next;

如下,

//定义栈节点结构体,包含数据data,指向下一个节点的指针next

typedef struct stackNode{

ElemType data;

struct stackNode* next;

}stackNode,*linkStack;//定义栈节点类型和指向栈节点的指针类型

在这里插入图片描述

代码:

//思路(用链表实现的栈):
//如果是左符号就将对应的右符号压入栈中;如果是其他字符,对比时栈为空或符号不匹配返回0
//最后对比遍历完了栈不为空,返回0,否则返回1

//思路(用链表实现的栈):
//如果是左符号就将对应的右符号压入栈中;如果是其他字符,对比时栈为空或符号不匹配返回0
//最后对比遍历完了栈不为空,返回0,否则返回1


//定义元素类型为int
#define ElemType int

//定义栈节点结构体,包含数据data,指向下一个节点的指针next
typedef struct stackNode{
    ElemType data;
    struct stackNode* next;
}stackNode,*linkStack;//定义栈节点类型和指向栈节点的指针类型

//初始化栈,就是分配内存和将栈节点置空,再返回初始化过的栈
linkStack stackIni(){
    stackNode* temp=(stackNode*)malloc(sizeof(stackNode));
    temp->next=NULL;
    return temp;
}

//入栈操作,就是为新节点分配内存、存入数据与位置next,最后插入到栈顶
void stackPush(linkStack stack,ElemType data){
    stackNode* temp=(stackNode*)malloc(sizeof(stackNode));
    temp->data=data;
    temp->next=stack->next;
    stack->next=temp;
}

//判断栈是否为空,如果栈顶指针为空(即没有下一个元素),表示栈为空
bool stackEmpty(linkStack stack){
    return stack->next==NULL;
}

//出栈操作,就是如果栈为空返回-1,如果栈不为空就取出顶部元素,并用下一个结点替代原栈顶指针,最后释放节点内存返回栈顶数据
ElemType stackPop(linkStack stack){
    if(stackEmpty(stack)) return -1;
    stackNode* temp=stack->next;//指向栈顶结点
    stack->next=temp->next;//栈顶指针指向下一个节点,替代原栈顶指针
    int tempData=temp->data;//保存栈顶数据
    free(temp);//释放栈顶节点内存
    return tempData;//返回栈顶数据
}

//判断括号序列是否有效
//如果是左符号就将对应的右符号压入栈中;如果是其他字符,对比时栈为空或符号不匹配返回0
//最后对比遍历完了栈不为空,返回0,否则返回1
bool isValid(char* s) {
    linkStack stack=stackIni();
    ElemType data;
    for(int i=0;i<strlen(s);i++){
        switch(s[i]){
            case'(':stackPush(stack,')');break;
            case'{':stackPush(stack,'}');break;
            case'[':stackPush(stack,']');break;
            default:if((stackEmpty(stack))||(stackPop(stack)!=s[i])) return 0;
        }
    }
    if(stackEmpty(stack))return 1;
    return 0;//否则返回0
}
卡哥给出的c语言版(数组栈):
//辅助函数:判断栈顶元素与输入的括号是否为一对。若不是,则返回False
int notMatch(char par, char* stack, int stackTop) {
    switch(par) {
        case ']':
            return stack[stackTop - 1] != '[';
        case ')':
            return stack[stackTop - 1] != '(';
        case '}':
            return stack[stackTop - 1] != '{';
    }
    return 0;
}

bool isValid(char * s){
    int strLen = strlen(s);
    //开辟栈空间
    char stack[5000];
    int stackTop = 0;

    //遍历字符串
    int i;
    for(i = 0; i < strLen; i++) {
        //取出当前下标所对应字符
        char tempChar = s[i];
        //若当前字符为左括号,则入栈
        if(tempChar == '(' || tempChar == '[' || tempChar == '{')
            stack[stackTop++] = tempChar;
        //若当前字符为右括号,且栈中无元素或右括号与栈顶元素不符,返回False
        else if(stackTop == 0 || notMatch(tempChar, stack, stackTop))
            return 0;
        //当前字符与栈顶元素为一对括号,将栈顶元素出栈
        else
            stackTop--;
    }
    //若栈中有元素,返回False。若没有元素(stackTop为0),返回True
    return !stackTop;
}
参考资料:

代码随想录算法训练营第十一天| 20. 有效的括号 、1047. 删除字符串中的所有相邻重复项 、 150. 逆波兰表达式求值https://blog.csdn.net/weixin_48369273/article/details/135717260

1047. 删除字符串中的所有相邻重复项

1047. Remove All Adjacent Duplicates In String

题目链接/文章讲解/视频讲解:https://programmercarl.com/1047.%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%E7%9B%B8%E9%82%BB%E9%87%8D%E5%A4%8D%E9%A1%B9.html

而且在企业项目开发中,尽量不要使用递归!在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)

代码:

//思路:

//每拿到一个字母就去和栈顶元素对比,栈为空或和栈顶元素不相等则入栈,相等则出栈

//最后在栈顶指针位置添加字符串结束符后输出字符串数组即可(如果当作栈弹出全部元素还需要反转,所以最后直接当数组处理)

//知识点:

//数组栈数据结构包含栈顶索引和数组,初始化就是分配内存空间再初始化栈顶索引为-1

//思路:
//每拿到一个字母就去和栈顶元素对比,栈为空或和栈顶元素不相等则入栈,相等则出栈
//最后在栈顶指针位置添加字符串结束符后输出字符串数组即可(如果当作栈弹出全部元素还需要反转,所以最后直接当数组处理)
//知识点:
//数组栈数据结构包含栈顶索引和数组,初始化就是分配内存空间再初始化栈顶索引为-1
char* removeDuplicates(char* s) {
    //分配内存(数组栈长度为字符串长度+1,因为要多出一个位置存放字符串结束符)
    char* stack=(char*)malloc(sizeof(char)*(strlen(s)+1));
    //初始化栈顶索引为-1
    int stackTop=-1;
    //遍历字符串,栈为空或和栈顶元素不相等则入栈,相等则出栈
    for(int i=0;i<strlen(s);i++){
        if((stackTop==-1)||stack[stackTop]!=s[i]) stack[++stackTop]=s[i];
        else stackTop--;
    }
    //在栈顶指针位置添加字符串结束符
    stack[++stackTop]='\0';
    return stack;
}
参考资料:

代码随想录算法训练营第十一天| 20. 有效的括号 、1047. 删除字符串中的所有相邻重复项 、 150. 逆波兰表达式求值https://blog.csdn.net/weixin_48369273/article/details/135717260

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值