Leetcode刷题笔记——单调性
单调性
单调性是数学中使用的一种常见性质,通常用于描述函数,在高等数学中的定义常常为:
设函数f(x)在区间I上有定义,如果对于I上的任意两个数x1和x2,当x1<x2时,有f(x1)<f(x2)(或者f(x1)>f(x2)),则称函数f(x)在区间I上是单调递增的(或者单调递减的)。
例如如下图像就是两个单调函数。

利用单调性我们可以减少很多重复的运算。例如,对于如下函数,我们给定其定义域为[0,+∞),现在要求查找出在其定义域内所有f(x)即y大于0.5的区间。

- 如果不借助单调性,我们需要采用遍历的方法,依次遍历定义域中的所有点x,判断其f(x)是否满足条件(大于0.5)。
- 如果借助单调性,我们知道上述函数是严格单调递增的,其图像如下:

绿线表示y=0.5的图像,处理该问题,只需要找到方程0.5=(1/3)x^3的解x0,由于函数具有单调性,且单调递增,因此,所有大于x0的区间内的x其f(x)都满足大于0.5。
对于计算机语言来说,用于表示函数的常见数据结构就是数组,我们可以通过
- 原数组本身的单调性
- 构造单调性
简化许多运算。下面引入几个例子:
15. 三数之和
题目:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:
- 答案中不可以包含重复的三元组
- 3 <= nums.length <= 3000
- -10^5 <= nums[i] <= 10^5
按照最朴素的解决方法,三层循环,循环遍历整个数组,然后再对整个结果进行去重,便可以解决该问题,但是时间复杂度为O(n^3),由于过于简单,这里给出伪代码:
list = [][]int{}
for i:=0;i <= len(nums)-3;i ++ {
for j:=i+1;j <= len(nums)-2;j ++ {
for k:= j+1;k < len(nums)-1;k ++ {
if nums[i]+nums[j]+nums[k] == 0 {
list = append(list, []int{nums[i],nums[j],nums[k]})
}
}
}
}
对list去重
本题目首先要求我们去重,因为返回结果要求不重复,对于去重常见的做法:
- 使用数据结构set、map进行处理,但是会额外占用内存
- 对原始数据排序,然后按序处理跳过重复项
优化掉去重问题后,我们可以尝试对内层的两层for循环进行优化,这里就引入了一个经典的方法:构造单调性,根据单调性进行查找。
巧妙的方法
如果nums[i]确定,那么我们只需要寻找满足条件nums[j]+nums[k]=-nums[i]的j、k值,这就变成了一个二数之和的问题,暴力算法是直接进行遍历,然后查找该值,但是由于数组的有序性,我们有一种更加巧妙的方法:
- 由于当前数组的有序性,保证了数组本身是单调递增(或递减的,这里以递增为例)
- 设置指针p1、p2指向数组开头
p1=i+1和结尾p2=len(nums)-1 - pred=nums[p1]+nums[p2],target=-nums[i]
- if target < pred,由于数组递增,nums[p2-1]<num[p2],因此,p2 --
- if target > pred,由于数组递增,nums[p1+1]>num[p1],因此,p1 ++
- if target == pred,找到了目标,但为防止遗漏数据还要继续查找,此时指针向任意方向移动都没有影响,可以p1 ++或者p2 --
- 直到p1 >p2则可以停止查找(=取决于需求,如果有需求可以>=或<=)
这个模式可以应用于很多地方,实际上具有单调性的函数一般都可以通过该办法查找,例如nums[j]*nums[k]=target,查找j、k。
例如,在[-1,0,1,2,-1, 3]这个数组中,查找nums[j]+nums[k]=4的nums[j]和nums[k]的值,现对其进行排序,然后用上述方法进行处理:

了解了这个模式后,我们给出解决该问题的代码:
解题代码以及注释
import "sort"
func threeSum(nums []int) [][]int {
result := [][]int{}
sort.Ints(nums)
// 尝试固定i,然后将3数之和转化为两数之和
for i := 0; i < len(nums)-2; i++ {
// 对nums[i]进行去重
if i-1 >= 0 && nums[i-1] == nums[i] {
continue
}
sum := -nums[i]
left := i + 1
right := len(nums) - 1
// 解决两数之和问题,寻找left、right使得nums[left]+nums[right]==sum
for left < right {
temp := nums[left] + nums[right]
if temp == sum {
result = append(result, []int{nums[i], nums[left], nums[right]})
// 去重nums[left]
for left < right && nums[left] == nums[left+1] {
left++
}
// 去重nums[right]
for left < right && nums[right] == nums[right-1] {
right--
}
left++
right--
} else if temp > sum {
right--
} else {
left++
}
}
}
return result
}
823. 带因子的二叉树
题目:给出一个含有不重复整数元素的数组arr,每个整数arr[i]均大于 1。用这些整数来构建二叉树,每个整数可以使用任意次数。其中:每个非叶结点的值应等于它的两个子结点的值的乘积。满足条件的二叉树一共有多少个?答案可能很大,返回 对 10^9+7 取余 的结果。
例如:输入: arr = [2, 4, 5, 10]
输出: 7
解释: 可以得到这些二叉树: [2], [4], [5], [10], [4, 2, 2], [10, 2, 5], [10, 5, 2]
该问题是一个树相关的问题,并且对于父子结点处理过程是类似的。举例说明这件事:
对于输入arr=[18, 3, 6, 2],页结点可以为[2][3][6][18],可以把未显示的结点看做空结点,对于顶点为6的树可以为[6,2,3]或者[6,3,2],就需要借助叶节点信息。对于顶点为18的树可以为[18,3,6],[18,6,3],而组成以[6]的顶点的组合有3个。可以看到该问题是个动态规划问题。
f(18)=f(3)*f(6)
f(18)=f(6)*f(3)
f(6)=f(3)*f(2)
f(6)=f(2)*f(3)
f(3)=1
f(2)=1
状态转换方程为:
\(f(a*b)= \begin{array}{ll}
f(a)*f(b)*2+1 & a!=b,a为左子树b为右子树,和a为右子树b为左子树\\
f(a)*f(b)+1, & a==b\\
\end{array}\)
那最后的问题就是查找在index属于[0,i-1]的数组中,哪些a,b满足arr[a]*arr[b]==arr[i],我们就可以使用上面提到的巧妙的方法类比解决该问题。这里就不再赘述。
解题代码和注释
func numFactoredBinaryTrees(arr []int) int {
sort.Ints(arr)
dp := make([]int64, len(arr))
res, mod := int64(0), int64(1e9 + 7)
for i := 0; i < len(arr); i++ {
dp[i] = 1
// 查找arr[left]*arr[right]==arr[right]*arr[left]
for left, right := 0, i - 1; left <= right; left++ {
for left <= right && int64(arr[left]) * int64(arr[right]) > int64(arr[i]) {
right--
}
if left <= right && int64(arr[left]) * int64(arr[right]) == int64(arr[i]) {
if left == right {
dp[i] = (dp[i] + dp[left] * dp[right]) % mod
} else {
dp[i] = (dp[i] + dp[left] * dp[right] * 2) % mod
}
}
}
res = (res + dp[i]) % mod
}
return int(res)
}
Leetcode刷题笔记——单调性的更多相关文章
- LeetCode刷题笔记和想法(C++)
主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...
- 18.9.10 LeetCode刷题笔记
本人算法还是比较菜的,因此大部分在刷基础题,高手勿喷 选择Python进行刷题,因为坑少,所以不太想用CPP: 1.买股票的最佳时期2 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. ...
- LeetCode刷题笔记 - 12. 整数转罗马数字
学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题. 官方链 ...
- LeetCode刷题笔记 - 2022
这篇博客集中整理在LeetCode的刷题记录,方便查阅 258. 各位相加 - 力扣(LeetCode) (leetcode-cn.com) 代码 class Solution { public: i ...
- Leetcode刷题笔记(双指针)
1.何为双指针 双指针主要用来遍历数组,两个指针指向不同的元素,从而协同完成任务.我们也可以类比这个概念,推广到多个数组的多个指针. 若两个指针指向同一数组,遍历方向相同且不会相交,可以称之为滑动窗口 ...
- LeetCode刷题笔记(1-9)
LeetCode1-9 本文更多是作为一个习题笔记,没有太多讲解 1.两数之和 题目请点击链接 ↑ 最先想到暴力解法,直接双循环,但是这样复杂度为n平方 public int[] twoSum(int ...
- leetcode刷题笔记
(1)Best Time to Buy and Sell Stock Total Accepted: 10430 Total Submissions: 33800My Submissions Say ...
- leetcode刷题笔记08 字符串转整数 (atoi)
题目描述 实现 atoi,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即 ...
- LeetCode刷题笔记-回溯法-分割回文串
题目描述: 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串. 返回 s 所有可能的分割方案. 示例: 输入: "aab"输出:[ ["aa", ...
- leetcode刷题笔记231 2的幂
题目描述: 给定一个整数,写一个函数来判断它是否是2的幂. 题目分析: 判断一个整数是不是2的幂,可根据二进制来分析.2的幂如2,4,8,等有一个特点: 二进制数首位为1,其他位为0,如2为10,4为 ...
随机推荐
- 40% building 31/38 modules 7 active ...es\core-js\modules\es6.object.assign.jsBrowserslist: caniuse-lite is outdated.
一早运行项目发现不正常 虽然能运行,但是怎么看都不顺眼啊,那就照着提示先: npm update 一连串下来啥也看不懂是不是 我就直接npm install看看重新下载一下依赖结果直接给我来了个 :c ...
- 这10个Lambda表达式必须掌握,简化你的代码,提高生产力
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名 ...
- GitLib详细使用手册(windows系统)
Git是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理. 对gitlab的常见的使用有建立仓库.提交代码.更新代码.回滚代码.显示/修改日志.拉取分支.解决冲突.设置比 ...
- Python基础 - 赋值运算符
以下假设变量a为10,变量b为20: 运算符 描述 实例 = 简单的赋值运算符 c = a + b 将 a + b 的运算结果赋值为 c += 加法赋值运算符 c += a 等效于 c = c + a ...
- 一篇文章带你详细了解axios的封装
axios 封装 对请求的封装在实际项目中是十分必要的,它可以让我们统一处理 http 请求.比如做一些拦截,处理一些错误等.本篇文章将详细介绍如何封装 axios 请求,具体实现的功能如下 基本配置 ...
- CAPL 脚本基本语句
CAPL(Communication Access Programming Language)是一种用于汽车通信网络分析和仿真的脚本语言.以下是CAPL脚本的基本语句: 1.变量声明 variable ...
- SRE 的工作介绍
哈喽大家好,我是咸鱼 今天看到了一篇很不错的文章,作者是一名 SRE 工程师,在 Shopee 工作,base 新加坡 分享出来给大家看看 作者:卡瓦邦噶 原文链接:https://www.kawab ...
- LLaMA模型微调版本:斯坦福 Alpaca 详解
项目代码:https://github.com/tatsu-lab/stanford_alpaca 博客介绍:https://crfm.stanford.edu/2023/03/13/alpaca.h ...
- MySQL 中分区表
MySQL 中的分区表 InnoDB 逻辑存储结构 表空间 (Tablespace) 段 (segment) 区 (extent) 页 (page) 行 (row) InnoDB 数据页结构 分区别表 ...
- 如何使用libavfilter库给pcm音频采样数据添加音频滤镜?
一.初始化音频滤镜 初始化音频滤镜的方法基本上和初始化视频滤镜的方法相同,不懂的可以看上篇博客,这里直接给出代码: //audio_filter_core.cpp #define INPUT_SAMP ...