算法编程题-区间最小数乘区间和的最大值,基于数组中的数字拼接可得的小于目标值的最大数

算法编程题-区间最小数乘区间和的最大值,基于数组中的数字拼接可得的小于目标值的最大数

    • 区间最小数乘区间和的最大值
      • 原题描述
      • 思路简述
      • 代码实现
      • 复杂度分析
    • 基于数组中的数字拼接可得的小于目标值的最大数
      • 原题描述
      • 思路简述
      • 代码实现
      • 复杂度分析
    • 参考

这里分享两道字节面试常考但是不是leetcode原题的两道题目。

区间最小数乘区间和的最大值

原题描述

给定一个数组,定义一个子数组的值为:该子数组中的最小值乘以该子数组的和,现在需要找到值最大的子数组。比如,对于数组[6, 2, 1],应当输出36。

思路简述

该题虽然不是leetcode原题,但是和经典题接雨水的思路是一样的。先从暴力思路入手,最暴力最直观的思路就是遍历每一个子数组,然后在遍历这个子数组得出子数组的最小值和和,这种做法的时间复杂度达到了 O ( n 3 ) O(n^3) O(n3)。先尝试在这一个基础上进行优化,减少遍历次数。可以优化遍历方式,在遍历的同时计算当前子数组的最小值和和,这样时间复杂度就减少到了 O ( n 2 ) O(n^2) O(n2)。但是这样的时间复杂度是肯定不够的,还需要优化。
考虑枚举,换一个角度想,能不能枚举没有的最小值,也就是我们遍历数组,以当前位置作为最小值,那么当前位置的数能够作为什么样的子数组的最小值呢,显然是子数组中不会存在小于该数的数,那么就可以从当前位置往左看,第一个小于当前数的位置,以及往右看,第一个小于当前数的位置,以上两个位置夹的子数组就是以当前数为最小数的情况下,对应的最优的子数组,因为在最小数确定的情况下,子数组自然是越长越好。可是怎么去找到以上的两个位置呢?可以基于单调栈以 O ( n ) O(n) O(n)的时间复杂度预处理得到所有位置的左边第一个小于的数,右边第一个小于的数,这样整体的时间复杂度就减少到了 O ( n ) O(n) O(n)
以寻找左边最近的较小的数字的过程为例来说明单调栈的过程,首先是从左往右遍历数组,维护一个单调递增栈,为什么是递增,如下图,假设此时维护的栈不满足严格的递增,遍历到一个新的数11,其左边的第一个较小的数字是8,而10永远没有机会,因为一个数字如果比10大,也一定会大于8,而我们要找的左边最近的,相当与10会被8覆盖掉,所以我们要维护成一个单调递增栈。具体的思路就是在遍历的过程中,先把栈顶大于等于当前数字的数pop出去,然后根据栈中的情况决定左边最近的较小的数字的位置是什么,然后将当前数字也加入到栈中,这样维护的栈就是一个单调递增栈。
在这里插入图片描述

代码实现

func MaxVInterval(arr []int) int {n := len(arr)// 1. 从前往后用单调栈找出左边最近的比当前元素要小的元素的位置+1leftSmaller := make([]int, n)stack := gulc.NewStack[int]()for i := 0; i < n; i++ {for !stack.IsEmpty() && arr[stack.Top()] >= arr[i] {stack.Pop()}if stack.IsEmpty() {leftSmaller[i] = 0} else {leftSmaller[i] = stack.Top() + 1}stack.Push(i)}// 2. 从后往前遍历用单调栈找到右边最近的比当前元素要小的位置-1rightSmaller := make([]int, n)stack.Clear()for i := n - 1; i >= 0; i-- {for !stack.IsEmpty() && arr[stack.Top()] >= arr[i] {stack.Pop()}if stack.IsEmpty() {rightSmaller[i] = n - 1} else {rightSmaller[i] = stack.Top() - 1}stack.Push(i)}// 3. 利用前缀和 预处理区间和prefixSum := make([]int, n + 1)sum := 0for i := 0; i < n; i++ {sum += arr[i]prefixSum[i + 1] = sum}// 4. 推导答案ans := arr[0] * (prefixSum[rightSmaller[0] + 1] - prefixSum[leftSmaller[0]])for i := 1; i < n; i++ {ans = max(ans, arr[i] * (prefixSum[rightSmaller[i] + 1] - prefixSum[leftSmaller[i]]))}return ans
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),预处理过程,代码实现看起来有两层循环,但是两层循环里的操作只有栈的push和pop,而考虑到数组中的每一个元素最多进栈一次,出栈一次,所以实际上的循环次数是 O ( n ) O(n) O(n)级别的
  • 空间复杂度: O ( n ) O(n) O(n)

基于数组中的数字拼接可得的小于目标值的最大数

原题描述

给定一个数组,数组里的数字只包括0-9的数字,给定一个目标值target,要求出用数组里面的数字组装成最大的小于target的数字。比如,对于数组[5, 4, 9, 2]和目标值16345,应当输出9999

思路简述

这题比较容易想到解法,但难在任何优雅简洁地实现。笔者的思路就是根据目标值来逐位构造,从目标值的最高位开始,在数组里找到最大的小于等于最高位的数字,如果找到的是一个小的数,那么剩余的位数可以全部采用数组中的最大数,因为已经保证了一定比目标值要小,如果找到的是相等,那么需要在下一位找类似的分析,如果没找到,则需要回退到前一位,前一位一定是在数组中出现的,此时可以尝试去数组中比该数稍小的一个数,如果能取到,剩余数字全部去最大数即可,如果没找到,需要再回退到上一位。回退的终点是不在数组中了,此时可以取目标值位数-1的位数,每一位用最大值填充。如果目标值所有位的数字都出现在数组中,则也需要往前回退。回退需要使用回溯来实现,实现代码比较复杂。

代码实现

func MaxNumber(nums []int, target int) int {sort.Slice(nums, func(i, j int) bool {return nums[i] < nums[j]})ans := -1// 从最高位开始numbers := getNumbers(target)n := len(numbers)var dfs func(i, num int, flag bool) dfs = func(i, num int, flag bool) {if ans != -1 {return}if i == -1 {   // 回退的终点ans = 0for j := 0; j < n - 1; j++ {ans = ans * 10 + nums[len(nums) - 1]}return}pos := getMaxSmaller(nums, numbers[i])  if pos == -1 {   // 需要回退dfs(i - 1, num / 10, true)}if flag {   // 发生了回退if pos == 0 {dfs(i - 1, num / 10, true)} else {pos--}}if ans != -1 {return}num = num * 10 + nums[pos]i++if nums[pos] < numbers[i - 1] {   // 这个保证了一定小于,所以后续数字全部取最大数字即可for i < n {num = num * 10 + nums[len(nums) - 1]i++}ans = numreturn} else if i == n {   // 特殊情况,说明target的每一位都出现在了数组中, 需要回退dfs(i - 1, num / 10, true)}dfs(i, num, flag)}dfs(0, 0, false)return ans
}// getNumbers 返回num的各位数字
func getNumbers(num int) []int {if num == 0 {return []int{0}}numbers := make([]int, 12)k := 11for num > 0 {numbers[k] = num % 10k--num /= 10}return numbers[k + 1:]
}// getMaxSmaller 返回有序数组nums中小于等于target的最大整数
func getMaxSmaller(nums []int, target int) int {low := 0high := len(nums) - 1for low < high {mid := (low + high + 1) / 2if nums[mid] > target {high = mid - 1} else {low = mid}}if nums[low] > target {return -1}return low
}

复杂度分析

  • 时间复杂度: O ( 1 ) O(1) O(1),可以认为是常量级别,因为数组是由0-9的数字组成的,长度一般不超过10,然后逐位构造,考虑到整数最多也只有64位,也是有限的
  • 空间复杂度: O ( 1 ) O(1) O(1)

参考

  • 求区间最小数乘区间和的最大值

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

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

相关文章

华为Ensp模拟器配置RIP路由协议

目录 RIP路由详解&#xff1a;另一种视角解读 1. RIP简介&#xff1a;轻松理解基础概念 2. RIP的核心机制&#xff1a;距离向量的魅力 3. RIP的实用与局限 RIP配置实验 实验图 ​编辑 PC的ip配置 RIP配置步骤 测试 结语&#xff1a;RIP的今天与明天 RIP路由详解&…

数字化那点事:一文读懂物联网

一、物联网是什么&#xff1f; 物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;是指通过网络将各种物理设备连接起来&#xff0c;使它们可以互相通信并进行数据交换的技术系统。通过在物理对象中嵌入传感器、处理器、通信模块等硬件&#xff0c;IoT将“…

GoFly框架使用vue flow流程图组件说明

Vue Flow组件库是个高度可定制化的流程图组件&#xff0c;可用于工作流设计、流程图及图表编辑器、系统架构展示。可以根据自己的需求&#xff0c;设计独特的节点和边&#xff0c;实现个性化的流程图展示。这不仅增强了应用的视觉效果&#xff0c;也使得用户交互更为直观和流畅…

VS2022-创建智能酒店门锁DLL动态链接库——develop hotel smart locker dynamic

一、自主生产酒店智能门锁 1. 定制化能力&#xff1a;自主生产的品牌能够根据酒店的特定需求进行定制&#xff0c;例如特殊的外观设计、功能模块的选择等&#xff0c;更好地满足酒店的个性化要求。 2. 成本控制&#xff1a;自主生产可以更有效地控制成本&#xff0c;从原材料…

免费开源的Koodo Reader:轻松管理电子书并实现远程访问

文章目录 前言1. Koodo Reader 功能特点1.1 开源免费1.2 支持众多格式1.3 多平台兼容1.4 多端数据备份同步1.5 多功能阅读体验1.6 界面简洁直观 2. Koodo Reader安装流程2.1 安装Git2.2 安装Node.js2.3 下载koodo reader 3. 安装Cpolar内网穿透3.1 配置公网地址3.2 配置固定公网…

进程池的子进程的清理工作问题

首先进程池看看代码怎么写的 https://gitee.com/ljh0617/linux_test/blob/master/11-17/3.pipe_use/ProcessPool.cc 我们对子进程分配到的管道读文件描述符进行了重定向&#xff0c;让他改为从0读&#xff0c;这和清理工作无关&#xff0c;只是这么设计让子进程不再有键盘输入…

Java 多线程详细介绍

Java 多线程详细介绍 线程是多线程的支柱。我们生活在一个现实世界中&#xff0c;这个世界本身就被大量应用程序包围着。随着技术的进步&#xff0c;除非我们有效地引入多任务处理的概念&#xff0c;否则我们无法达到同时运行它们所需的速度。这是通过线程的概念实现的。 Java…

二叉树+树的OJ题讲解

求第K层节点个数 思路:走到K1就不走了,一次传回得到的值 #include<stdio.h> #include<stdlib.h> //树的定义 typedef int BTDataType; typedef struct BinaryTreeNode {BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right; }BTNode;//手…

Android kotlin之配置kapt编译器插件

配置项目目录下的gradle/libs.versions.toml文件&#xff0c;添加kapt配置项&#xff1a; 在模块目录下build.gradle.kt中增加 plugins {alias(libs.plugins.android.application)alias(libs.plugins.jetbrains.kotlin.android)// 增加该行alias(libs.plugins.jetbrains.kotl…

类和对象——拷贝构造函数,赋值运算符重载(C++)

1.拷⻉构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此构造函数也叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 // 拷贝构造函数//d2(d1) Date(const Date& d) {_year d._yea…

STM32G4的数模转换器(DAC)功能介绍

目录 概述 1 DAC介绍 1.1 功能 1.2 主要特征 1.3 DAC特性总结 ​2 DAC模块框架结构 3 DAC数据格式 3.1 单DAC通道 3.2 双通道数据格式 3.3 有符号、无符号数据 4 DAC数据转换 ​5 DAC输出电压 概述 本文主要介绍STM32G4的数模转换器&#xff08;DAC&#xff09;功能&a…

Pointnet++改进68:添加FFCM |融合傅里叶卷积

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理论介绍 …

Linux:解决远程X无法连通问题,X-Server开启TCP连接

一、问题分析 提前申明&#xff1a; 本次实验使用REHL 8 进行操作&#xff01; 客户机 A 为X-Client &#xff0c;即远程X的客户端。 服务机 B 为X-Server&#xff0c;即远程X的服务端。 问题的所有操作均在已经配置好Xorg的前提下进行的&#xff0c;不知道不配置会有什么影响&…

零基础Java第十九期:认识String(一)

目录 一、String的重要性 二、String的常用方法 2.1. 字符串构造 2.2. String对象的比较 2.3. 字符串查找 2.4. 转化 2.4. 字符串替换 2.5. 字符串拆分 2.6. 字符串截取 一、String的重要性 在C语言中已经涉及到字符串了&#xff0c;但是在C语言中要表示字符串只能…

HarmonyOS4+NEXT星河版入门与项目实战--------ArkTs语言与TypeScript语法

文章目录 1、ArkTs语言1、ArkTs 特点2、ArkTs与Javascript关系 2、TypeScript 语法 1、ArkTs语言 在html的开发中&#xff0c;实现一个页面元素&#xff0c;比如Button&#xff0c;往往包含了以下三种要素&#xff1a;JS、HTML、CSS。JS处理逻辑与响应、HTML 用来声明标签生成…

使用yak编写yakit漏洞检测插件

前言 在使用yakit进行编写yaml插件的时候遇到了yaml无法处理的情况&#xff0c;我不知道是不是yaml无法处理或者说是yakit和yaml的兼容还不够&#xff0c;面对变量的处理还是有些难受&#xff0c;于是花了点时间看了官网的yak语法的手册和其他人写的yak插件尝试使用yak语言来完…

信也科技和云杉网络的AI可观测性实践分享

1. 信也科技 2、云杉网络 2.1 中国移动

Blossom:开源私有部署的markdown笔记软件

在信息化、数字化时代&#xff0c;我们每个人的生活和工作都离不开笔记和知识管理。从简单的待办事项&#xff0c;到复杂的项目计划&#xff0c;再到存储大量个人知识的工具&#xff0c;如何选择一个高效、便捷且符合个人需求的笔记软件&#xff0c;成了许多人的难题。最近在逛…

Spring:DI依赖注入的方式

Spring为我们提供了两种注入方式&#xff0c;分别是: setter注入 简单类型引用类型 构造器注入 简单类型引用类型 setter注入 在bean中定义引用类型属性&#xff0c;并提供可访问的set方法配置中使用property标签ref属性注入引用类型对象 (1)项目中添加BookDao、BookDaoIm…

逆向攻防世界CTF系列37-crackme

逆向攻防世界CTF系列37-crackme 参考https://blog.csdn.net/xiao__1bai/article/details/120230397 nspack的壳&#xff0c;查了一下好像是北斗的一个壳 没找到什么脱壳软件&#xff0c;只能手动脱壳了 手动脱壳的最终要的是ESP定律 ESP定律的原理就是“堆栈平衡”原理 涉及…