【剑指offer专项突破版】栈篇——“C“

前言

剑指offer专项突破版(力扣官网)——> 点击进入
本文所属专栏——>点击进入

  • 多插几句

由于这是C语言,用到的数据结构,需要手搓,当然也很简单,但总归是费点时间的,因此我们避免一道题造一次轮子尴尬情况,请自觉备好一份栈的代码,以便于CV

进入今天的栈篇吧!

理论知识——栈和对列
栈的实现原理就是简单的顺序表

这里为了避免冗余代码,先把栈的代码给出。

typedef struct Stack
{
    
    
    int* arr;
    int size;
    int capacity;
}Stack;
void Init(Stack* stack)
{
    
    
    stack->arr = NULL;
    stack->size = 0;
    stack->capacity = 0;
}
void Push(Stack* stack,int val)
{
    
    
    if(stack->capacity == stack->size)
    {
    
    
        int capacity = stack->capacity == 0 ? 4 : stack->capacity * 2;
        stack->arr = (int*)realloc(stack->arr,sizeof(int)*capacity);
        stack->capacity = capacity;
    }
    (stack->arr)[(stack->size)++] = val;
}
int Pop(Stack *stack)
{
    
    
    if(stack->size > 0)
    {
    
    
        return (stack->arr)[--(stack->size)];
    }
    return 0;
}
bool is_empty(Stack* stack)
{
    
    
    if(stack->size == 0)
    {
    
    
        return true;
    }
    return false;
}
void Stack_free(Stack* stack)
{
    
    
    free(stack->arr);
}
int Top(Stack* stack)
{
    
    
    if(stack->size > 0)
    {
    
    
        return (stack->arr)[stack->size-1];
    }
    else
    {
    
    
        return -1;
    }
}

一、后缀表达式

题目分析

在这里插入图片描述

思路分析

为了弄懂这道题,首先我们先要清楚逆波兰表示法,这个概念。

 1+1等于2,这都知道,那换种写法,我说1 1 + 的结果也是 2。前一种,是我们习惯的写法,后一种是计算机的写法,我们一般把1+1叫做中缀表示法,就是操作符在操作数之间。那 1 1 +叫做后缀表示法/逆波兰表示法操作数在前,操作符在后,如何从后缀表示法推到中缀表示法,是我们要解决的问题。

我们先给稍微复杂一点的表达式,5 + 1 + (5 * 5)优先级高的优先放到后面,那第一步我们转换成了,5 + 1 + 5 5 *,然后加减法从左往右,第二步就转换成了,5 1 + 5 5 * +,第三步就转换成了,5 1 5 5 * + +

 弄懂概念,我们就成功一半了,剩下的问题是如何计算后缀表达式。根据转换过程中的优先级的符号在前面,我们就可以将此表达式,转换成 5 1 (5 * 5) + +,即为5 1 25 + +,那么接着继续取出两个数,5 (1 +25)+,即为5 26 +,再进一步转换,(5+26) ,即为31,这个结果即为计算的结果 。仔细看,这个过程,再结合我们今天的专题,很容易就能想到,这是一个栈的思想。遇到数字就入栈,遇到符号就出栈,出两个元素,当然顺序不能乱(尤其除和减),出的第一个元素我们设为n1,第二个元素我们设为n2,操作符我们设为x,最终一定是n2 x n1。然后计算后我们还要把结果入栈,以便于后续的操作。oK,理解了这些,写出代码就容易多了。

代码

int str_nums(char* str)
{
    
    
    //求一下字符串的长度
    int len = strlen(str);
    //要转换成字符串需要正序遍历
    int begin = 0;
    int end = len-1;
    //先假设是正数
    int flag = 1;
    if(str[0]=='-')
    {
    
    
        flag = -flag;
        begin = 1;
    }
    int ret = 0;
    for(int i = begin; i <= end; i++)
    {
    
    
        ret = ret*10 + (str[i] - '0');
    }
    return ret*flag;
}
bool is_nums(char* str)
{
    
    
    int len = strlen(str);
    char judge = str[len-1];
    if(judge>='0'&&judge<='9')
    {
    
    
        return true;
    }
    else
    {
    
    
        return false;
    }
}
int evalRPN(char ** tokens, int tokensSize)
{
    
    
    Stack stack;
    Init(&stack);
    //将计算结果放在此变量中
    int ret = 0;
    int end = 0;
    for(int i = 0; i < tokensSize; i++)
    {
    
    
        //判断是否是数字
        char *nums = tokens[i];
        if(is_nums(nums))
        {
    
    
            //是数字就入栈
            int num = str_nums(nums);
            Push(&stack,num);
        }
        else
        {
    
    
            int n1 = Pop(&stack);
            int n2 = Pop(&stack);
            //判断加减乘除,同时出栈
            if(*nums == '+')
            {
    
    
                ret = n2 + n1;
            }
            else if(*nums == '-')
            {
    
    
                ret = n2 - n1;
            }
            else if(*nums == '*')
            {
    
    
                ret = n2 * n1;
            }
            else
            {
    
    
                ret = n2/n1;
            }
            //然后将结果再入栈
            Push(&stack,ret);
        }
    }
    //获取栈顶的元素
    ret = Top(&stack);
    //不要忘记,释放栈的空间
    Stack_free(&stack);
    return ret;
}

二、小行星碰撞

题目分析

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

思路分析

 小行星碰撞,看起来就很有意思,不过需要我们分析清楚思路,什么时候会发生碰撞什么时候会同归于尽什么时候永远不会发生碰撞。解决了这三个问题,代码就很轻松了。

 先来解决一个大前提,我们为啥会采取栈的结构进行解决此问题,我们从抽象的角度考虑,栈的当前状态都是一致的,当遇到某种变量可能会打破这种状态时,栈会通过不断地取栈顶元素就为去维持当前的状态,直到将这种变量的影响消除,从而更新或者维护状态。

 这里我们再具象化,当栈里面存的是不发生相撞的元素时,栈当前的状态是不相撞的,当遇到当前元素,可能会让栈的状态改变,从而发生相撞,我们就会用栈顶元素进行处理,直到栈的状态是不相撞的为止。

OK,来分析第一种情况什么时候会发生相撞? 当然是相向而行了,如果背向而行,只会越走越远。那什么时候会发生相向而行呢?当然是栈不为空,且栈顶的元素大于0,且当前元素为负数。那什么时候栈顶的元素不够处理这种情况呢?当前元素的相反数大于栈顶元素。此时我们就要出栈了,用下一个栈顶元素,来处理这种情况。

接着分析,第二种情况什么时候会发生同归于尽? 很明显,是在一种情况的特殊情况,当栈不为空,且栈顶的元素大于0,且当前元素为负数,且其相反数等于栈顶元素。

第三种情况什么时候永远不会发生碰撞? 当背向而行时,肯定不会碰撞,即栈的元素小于0,当前元素大于0。当同向而行时,即栈的元素和当前元素同号。当栈的元素为空时,也不会发生相撞。

OK,情况分析完了。

代码

int* asteroidCollision(int* asteroids, int asteroidsSize, int* returnSize)
{
    
    
    Stack stack;
    Init(&stack);
    for(int i = 0; i < asteroidsSize; i++)
    {
    
    
        int num = asteroids[i];
        //栈不为空时,且达成一直相撞条件的——当前元素小于0,栈元素大于0,且当前元素的绝对值较大
        while(!is_empty(&stack)&&Top(&stack) > 0 && Top(&stack) < - num)
        {
    
    
            Pop(&stack);
        }
        //前提是栈不为空,当栈的元素大于0,并且与当前元素的相反数相等
        if((!is_empty(&stack))&&(Top(&stack) > 0)&&(Top(&stack)==-num))
        {
    
    
            Pop(&stack);
        }
        //剩下的就是不满足相撞条件的,栈为空,栈的元素小于0,当前元素大于0,
        else if(is_empty(&stack)||Top(&stack) < 0 || num > 0)
        {
    
    
            Push(&stack,num);
        }
    }
    *returnSize = stack.size;
    return stack.arr;
}

三、每日温度

题目分析

在这里插入图片描述

思路分析

 如果能弄懂,栈的抽象解题思路,这道题,便会简单一些。首先要计算天数,我们存的是下标才好,栈里面的状态是目前为止气温没有升高,当遇到温度高的,那么就要计算栈顶元素的与之的天数差,直到栈的状态是目前气温没有升高为止或者栈为空,然后直到所有的元素都入过栈为止,最后栈的状态是目前为止气温没有升高,那么剩余栈的元素的天数差就是0。

代码

int* dailyTemperatures(int* temperatures, int temperaturesSize, int* returnSize)
{
    
    
    Stack stack;
    Init(&stack);
    int *arr = (int*)malloc(sizeof(int)*temperaturesSize);
    *returnSize = temperaturesSize;
    memset(arr,0,sizeof(int)*temperaturesSize);
    for(int i = 0; i < temperaturesSize; i++)
    {
    
    
        //当栈不为空,并且当前的温度大于栈顶的温度。
        //我们就计算栈顶元素的天气差,然后将栈顶元素出栈。
        while(!is_empty(&stack)&&temperatures[Top(&stack)]\
        < temperatures[i])
        {
    
    
            arr[Top(&stack)] = i - Top(&stack);
            Pop(&stack); 
        }
        //其余情况我们入栈即可
        Push(&stack,i);
    }
    Stack_free(&stack);
    return arr;
}

四、直方图最大矩形面积

题目分析

在这里插入图片描述

思路分析

 首先我们要理解如何求这个问题,假如将每一个矩形看做求取最大矩形的高,如何求可能的最大的面积,这个问题很简单,那就是确定比这个矩形矮的两边最近界限,以上面的5为例,比之低的就是 1 和 2,因此我们可以确定宽度,即为两界限下标差减1。那如何问题就转换为了如何求这个界限的问题,这就利用到了栈,用栈(存的元素下标)的状态表示当前不存在两边都小的情况,即栈的元素递增,当遇到比栈顶小或等于的元素,我们就可以计算,最小的宽度从而计算面积,将栈顶元素不断出栈,直到栈的状态满足递增或者栈为空为止。最后可能栈还会有元素,其符合递增顺序,且是整个数组中较小的元素,因此我们依次求宽度乘栈顶元素的高度即可,宽度即为元素个数减栈顶元素的下一个元素再减去1,最后返回求得的最大面积即可。

代码

int largestRectangleArea(int* heights, int heightsSize)
{
    
    
    Stack stack;
    Init(&stack);
    Push(&stack,0);
    int max = 0;
    for(int i = 1; i < heightsSize;i++)
    {
    
    
        int top = Top(&stack);
        //处理栈不为空,且当前元素小于等于栈顶的元素
        while(!is_empty(&stack)&&heights[i] <= heights[top])
        {
    
    
            Pop(&stack);
            int wide = i-Top(&stack)-1;
            int area = heights[top]*wide;
            if(max<area)
            {
    
    
                max = area;
            }
            top = Top(&stack);
        }
        Push(&stack,i);
    }
    //最后处理栈中递增的元素
    int top = Top(&stack);
    while(!is_empty(&stack))
    {
    
    
        Pop(&stack);
        int wide = (heightsSize-1)-Top(&stack);
        int area = heights[top]*wide;
        if(max < area)
        {
    
    
            max = area;
        }
        top = Top(&stack);
    }
    //不要忘记释放空间
    Stack_free(&stack);
    return max;
}

五、矩阵中最大的矩形

题目分析

在这里插入图片描述

思路分析

将二维矩阵转换成多个一维矩阵,不断地迭代,转为上一题的思路进行求解。

代码

int maximalRectangle(char** matrix, int matrixSize)
{
    
    
    if(matrixSize == 0)
    {
    
    
        return 0;
    }
    char *str = matrix[0];
    int len = strlen(str);
    int *arr = (int*)malloc(sizeof(int)*len);
    memset(arr,0,sizeof(int)*len);
    int max = 0;
    for(int i = 0; i < matrixSize; i++)
    {
    
    
        char *str = matrix[i];
        for(int j = 0; j < len; j++)
        {
    
    
            if(str[j] == '0')
            {
    
    
                arr[j] = 0;
            }
            else
            {
    
    
                arr[j]++;
            }
        }
        int ret = largestRectangleArea(arr,len);//上一题
        max = max > ret ? max : ret;
    }
    return max;
}

总结

 今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见

猜你喜欢

转载自blog.csdn.net/Shun_Hua/article/details/131253226