Leetcode刷题笔记——单调性

这篇具有很好参考价值的文章主要介绍了Leetcode刷题笔记——单调性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单调性

单调性是数学中使用的一种常见性质,通常用于描述函数,在高等数学中的定义常常为:

设函数f(x)在区间I上有定义,如果对于I上的任意两个数x1和x2,当x1<x2时,有f(x1)<f(x2)(或者f(x1)>f(x2)),则称函数f(x)在区间I上是单调递增的(或者单调递减的)。

例如如下图像就是两个单调函数。

Leetcode刷题笔记——单调性

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

Leetcode刷题笔记——单调性

  • 如果不借助单调性,我们需要采用遍历的方法,依次遍历定义域中的所有点x,判断其f(x)是否满足条件(大于0.5)。
  • 如果借助单调性,我们知道上述函数是严格单调递增的,其图像如下:
    Leetcode刷题笔记——单调性
    绿线表示y=0.5的图像,处理该问题,只需要找到方程0.5=(1/3)x^3的解x0,由于函数具有单调性,且单调递增,因此,所有大于x0的区间内的x其f(x)都满足大于0.5。

对于计算机语言来说,用于表示函数的常见数据结构就是数组,我们可以通过

  1. 原数组本身的单调性
  2. 构造单调性

简化许多运算。下面引入几个例子:

15. 三数之和
823. 带因子的二叉树

15. 三数之和

题目:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意

  1. 答案中不可以包含重复的三元组
  2. 3 <= nums.length <= 3000
  3. -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去重

本题目首先要求我们去重,因为返回结果要求不重复,对于去重常见的做法:

  1. 使用数据结构set、map进行处理,但是会额外占用内存
  2. 对原始数据排序,然后按序处理跳过重复项

优化掉去重问题后,我们可以尝试对内层的两层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]=4nums[j]和nums[k]的值,现对其进行排序,然后用上述方法进行处理:

Leetcode刷题笔记——单调性

了解了这个模式后,我们给出解决该问题的代码:

解题代码以及注释

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],我们就可以使用上面提到的巧妙的方法类比解决该问题。这里就不再赘述。文章来源地址https://www.toymoban.com/news/detail-679273.html

解题代码和注释

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刷题笔记——单调性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Leetcode刷题笔记--Hot31-40

    目录 1--颜色分类(75) 2--最小覆盖子串(76) 3--子集(78) 4--单词搜索(79) 5--柱状图中最大的矩形(84) 6--最大矩形(85) 7--二叉树的中序遍历(94) 8--不同的二叉搜索数(96) 9--验证二叉搜索树(96) 10--对称二叉树(101) 主要思路:         快排 主要思路:  主要

    2024年02月10日
    浏览(22)
  • 【刷题笔记8.8】LeetCode题目:两数之和

    给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是, 数组中同一个元素在答案里不能重复出现 。 你可以按任意顺序返回答案。 解法1:使用HashMap对数

    2024年02月13日
    浏览(32)
  • 【刷题笔记8.10】LeetCode题目:有效括号

    给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左括号 首先,解决此题,我们要明确使用 栈

    2024年02月13日
    浏览(23)
  • 力扣算法刷题Day59|单调栈

    力扣题目:# 503.下一个更大元素II  刷题时长:参考题解后2min 解题方法:单调栈 复杂度分析 时间O(n) 空间O(n) 问题总结 如何解决环的问题 本题收获 循环数组解决方案 思路一:将两个nums数组拼接在一起,使用单调栈计算出每一个元素的下一个最大值,最后再把结果集即res

    2024年02月13日
    浏览(46)
  • Leetcode刷题笔记题解(C++):203. 移除链表元素

    思路:不同的情况出现了,就是第一个节点要是为等于val的节点,可以新建一个节点,并next指向head,这样就可以遍历新的链表来删除节点

    2024年02月22日
    浏览(30)
  • Github疯传!谷歌师兄的LeetCode刷题笔记开源了!

    有小伙伴私聊我说刚开始刷LeetCode的时候,感到很吃力,刷题效率很低。我以前刷题的时候也遇到这个问题,直到后来看到这个谷歌师兄总结的刷题笔记,发现LeetCode刷题都是套路呀,掌握这些套路之后,就变得非常简单了! 这份笔记是作者在找工作的时候,刷了几百道的L

    2024年02月06日
    浏览(17)
  • 蓝桥杯刷题016——最大子矩阵(尺取法+单调队列)

    题目来源:最大子矩阵 - 蓝桥云课 (lanqiao.cn) 问题描述 小明有一个大小为 N×M 的矩阵, 可以理解为一个 N 行 M 列的二维数组。 我们定义一个矩阵 m 的 稳定度 f(m)  为 f(m)=max(m)−min(m) , 其中 max(m) 表示矩阵 m 中的最大值, min(m) 表示矩阵 m 中的最小值。 现在小明想要从

    2023年04月16日
    浏览(27)
  • LeetCode刷题笔记【30】:动态规划专题-2(不同路径、不同路径 II)

    参考前文 参考文章: LeetCode刷题笔记【29】:动态规划专题-1(斐波那契数、爬楼梯、使用最小花费爬楼梯) LeetCode链接:https://leetcode.cn/problems/unique-paths/description/ 动态规划 : 创建m×n的数组, 对应这个地图, 数组 val 表示 有几种方法可以走到这一格 最开始, 第一行和第一列v

    2024年02月09日
    浏览(43)
  • Leetcode刷题笔记题解(C++):83. 删除排序链表中的重复元素

    思路:链表相关的问题建议就是画图去解决,虽然理解起来很容易,但就是写代码写不出来有时候,依次去遍历第二节点如果与前一个节点相等则跳过,不相等则遍历第三个节点

    2024年02月22日
    浏览(33)
  • LeetCode刷题笔记【23】:贪心算法专题-1(分发饼干、摆动序列、最大子序和)

    贪心的本质是选择每一阶段的局部最优,从而达到全局最优。 例如,有一堆钞票,你可以拿走十张,如果想达到最大的金额,你要怎么拿? 指定每次拿最大的,最终结果就是拿走最大数额的钱。 每次拿最大的就是局部最优,最后拿走最大数额的钱就是推出全局最优。 感觉像

    2024年02月09日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包