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

news2024/11/17 3:51:36

文章目录

  • 前言
  • 一、后缀表达式
    • 题目分析
    • 思路分析
    • 代码
  • 二、小行星碰撞
    • 题目分析
    • 思路分析
    • 代码
  • 三、每日温度
    • 题目分析
    • 思路分析
    • 代码
  • 四、直方图最大矩形面积
    • 题目分析
    • 思路分析
    • 代码
  • 五、矩阵中最大的矩形
    • 题目分析
    • 思路分析
    • 代码
  • 总结

前言

剑指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)
{
    return (stack->arr)[stack->size-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;
}

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/655161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IBM不藏私:深刻解析量子计算机的突破和机遇

​ 巴伐利亚科学部长Markus Blume在莱布尼茨超级计算中心与Dieter Kranzlmlle&#xff08;左&#xff09;一起观看量子计算机的部分构件。&#xff08;图片来源&#xff1a;网络&#xff09; 关于量子计算机的研究已进行了数十年&#xff0c;目前还尚未生产一台能够掀起计算革命…

Vue全家桶(一):Vue基础+Vue-Cli+Vue组件化+过渡动画

目录 1.Vue概述1.1 认识Vue1.2 Vue的两核心1.3 Vue的初体验1.4 Vue的生命周期 2. Vue-CLI (Command Line Interface)3. Vue基本使用3.1 传统开发模式对比3.2 Vue.js引入3.3 Vue.js 案例分析3.3.1 实例参数el、data、methods的写法 4. Vue模板语法4.1 插值语法 {{xxx}}4.2 指令语…

vue3+ts:shims-vue.d.ts

一、本文引子 uniapp&#xff08;3.8.4.20230531&#xff09; vue3 ts vite 项目 在搭建这个base项目的时候出现红素波浪线如图&#xff0c;代码运行正常&#xff0c;但是看起来很难受&#xff0c;于是各种查找&#xff0c;能找到的资料很少&#xff0c;可能和我提问不够准…

【备战秋招】每日一题:4月23日美团春招第一题:题面+题目思路 + C++/python/js/Go/java带注释

为了更好的阅读体检&#xff0c;为了更好的阅读体检&#xff0c;&#xff0c;可以查看我的算法学习博客第一题-申请奖学金 在线评测链接:P1245 题目内容 塔子哥是一个热爱学习的大学生&#xff0c;他的梦想是成为一名优秀的算法竞赛高手。为了实现自己的梦想&#xff0c;他需…

代理ip匿名原理及那些行业需要代理ip

互联网的高速发展&#xff0c;连带了代理ip也受到了更多人的使用&#xff0c;不同的行业都存在使用代理ip的情况&#xff0c;同时代理ip也以为匿名程度分成了高匿、普匿、透明代理&#xff0c;那么代理ip匿名的原理是什么呢&#xff1f;又有哪些行业需要代理ip呢&#xff1f;下…

flume环境配置-传输Hadoop日志(namenode或datanode日志)

解压文件 修改文件名 配置环境变量 执行flume-ng version 将flume-env.sh.template改名为flume-env.sh&#xff0c; 并修改其配置 启动Flume传输Hadoop日志 启动flume 解压文件 tar -zxvf apache-flume-1.9.0-bin.tar.gz -C /opt 修改文件名 mv apache-flume-1.9.0-b…

全面安全防护,加速企业创新发展——亚马逊云科技re:Inforce全球大会

亚马逊云科技re:Inforce 2023全球大会于当地时间2023年6月13日在美国加州安纳海姆拉开帷幕。在大会上&#xff0c;亚马逊云科技宣布推出十多项安全新服务及功能&#xff0c;下面就来一览本次大会的风采。 “Security is our top priority.” “安全是我们的首要优先级”&#…

Java 面向对象 | 详细知识图谱式讲解

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; ☢Java入门 基础知识&#xff1a;了解 Java 基本语法、面向对象编程&#xff08;OOP&#xff09;概念、流程控制语句、数据类型、方法等基础知识。可以通过 Java 编程…

Nodejs七、身份认证

零、文章目录 Nodejs七、身份认证 1、Web 开发模式 &#xff08;1&#xff09;目前主流的 Web 开发模式 基于服务端渲染的传统 Web 开发模式基于前后端分离的新型 Web 开发模式 &#xff08;2&#xff09;服务端渲染的 Web 开发模式 服务器发送给客户端的 HTML 页面&…

HBase Shell操作HBase进行预分区

本文将介绍如何使用HBase Shell操作HBase进行预分区。预分区是指在创建表的时候&#xff0c;指定表的初始分区点&#xff0c;从而使表的数据能够均匀地分布在多个RegionServer上&#xff0c;提高读写性能和负载均衡。本文将使用HBase Shell命令&#xff0c;创建不同的预分区表&…

Allure在自动化测试中的应用

目录 前言&#xff1a; 01Allure的简介及使用 1、应用场景 02Allure与Pytest结合 1、添加测试步骤 2、添加主要功能模块描述 3、添加严重等级 03Allure集成Jenkins 1、Jenkins介绍和安装 2、Jenkins安装allure插件 前言&#xff1a; Allure是一种流行的测试报告框架…

Floating UI 使用经验分享 - Dialog

上文&#xff1a;Floating UI 使用经验分享 - Popover 在本文中&#xff0c;我将分享如何使用 Floating UI 来创建另一种常见的浮动 UI 组件——Dialog&#xff08;对话框&#xff09;。Dialog 是一个浮动元素&#xff0c;显示需要立即关注的信息&#xff0c;他会出现在页面内…

5G NR基于码本的上行传输

上行传输受基站DCI调度&#xff0c;UE收到DCI信息后&#xff0c;根据PMI信息选择相应的码本。 在3GPP TS 38.211 6.3.1.5节中&#xff0c;定义了不同天线端口数和不同传输层数情况下的可选码本。下面截取了单层2天线端口码本和双层两天线端口码本。 gNB在什么情况下为UE选择什…

Python--序列

Python--序列 <font colorblue>一、定义<font colorblue>二、索引<font colorblue>1.从左往右的索引&#xff1a;索引值从0开始递增<font colorblue>2.从右往左的索引&#xff1a;从-1开始递减 <font colorblue>三、切片<font colorblue>四…

Unity3d_Cut\Clipping sphere\CSG(boolean)(裁剪模型重合部分)总结

1、https://liu-if-else.github.io/stencil-buffers-uses-in-unity3d/ 下载&#xff1a;https://github.com/liu-if-else/UnityStencilBufferUses 2、手动切割 Unity 模型切割工具,CSG,任意图案,任意切割_unity csg_唐沢的博客-CSDN博客 3、 Shader Unity Shader学习&#x…

【从删库到跑路】详细讲解MySQL的函数和约束作用

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;函数⭐字符串函数&#x1f388;字符串拼接函数&…

Python基础知识 数据容器

id() 函数是python 内置函数 返回 id() 函数返回对象的唯一标识符&#xff0c;标识符是一个整数。 a, b, c 20, 30 , 40 print(a,b,c) ## a20 b 30 c40 ## 跟ES6系列中的析构函数原理一样Python中 字符串不能通过 &#xff0c;把 数字等非字符串&#xff0c;进行拼接…

Redis从入门到精通【高阶篇】之底层数据结构链表包(listpacks)详解

文章目录 0.前言2. listpacks&#xff08;紧凑列表&#xff09;2. 源码解析3. 总结 0.前言 上个篇章回顾&#xff0c;我们上个章节我们学习了《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》 本文将Redis底层数据结构 listpacks&#xff08;链表包&#…

SynchronousQueue的基本介绍

SynchronousQueue介绍 SynchronousQueue作为阻塞队列&#xff0c;区别于其他的阻塞队列。因为他不存储元素&#xff0c;但是存储消费者或者生产者。要是SynchronousQueue队列中存储了一个生产者&#xff0c;再来一个生产者想存放到队列中&#xff0c;要是你使用的是put方法&…

chatgpt赋能python:Python自动编号教程:如何给数据添加自动编号

Python自动编号教程&#xff1a;如何给数据添加自动编号 在进行数据处理和整理过程中&#xff0c;有时候需要为数据添加自动编号才能更好地进行分析和展示。而使用Python编程语言可以快速而准确地实现自动编号的功能。在本篇教程中&#xff0c;我们将介绍如何使用Python处理数…