Leetcode第11题(盛最多水的容器)解释为什么不会错过最优解。

本文探讨了在给定的n个非负整数数组中,如何使用双指针技术找到能够容纳最多水的两条线。通过对比暴力搜索和双指针搜索,阐述了双指针算法的时间复杂度优势,以及为何该方法不会错过最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (iai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (iai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

 

 分析

如果使用暴力搜索,

  • 时间复杂度:O(n^2),计算所有种高度组合的面积。
  • 空间复杂度:O(1),使用恒定的额外空间。

使用双指针搜索:

我们在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 maxarea 来持续存储到目前为止所获得的最大面积。 在每一步中,我们会找出指针所指向的两条线段形成的区域,更新 maxarea,并将指向较短线段的指针向较长线段那端移动一步。

代码如下:

class Solution:
	def maxArea(self, height: List[int]) -> int:
		i=0
		j=len(height)-1
		res=0 # 使用双指针
		while i<j:
			h=min(height[i],height[j])
			res=max(res,(j-i)*h)
			if height[i]>height[j]:
				j=j-1
			else:
				i=i+1
		return res

那么,为什么这种双指针搜索不会错过最优解呢?

我们从最开始,i=0,j=n-1开始分析,res表示容器的体积,一共两种情况:

(1)第一根高度比最后一根高度要矮,这个时候要把当前的容积存下来,并把左指针右移一位 ,这个时候之后就不用考虑第一根柱子了,为什么呢?

        因为固定左侧指针为第一根柱子不动时,对于第一根柱子来说,另一根柱子选择最后一根柱子已经是使容器体积最大,也即res最大的方案了。所以我们把这个容积res=(n-1)*height[0]存下来之后,就不需要考虑第一根柱子了,不像暴力搜索中,两层循环。这又是为什么呢?我们也分两种情况考虑。

        (a)如果选取的右侧柱子比第一根高,那么不论右侧柱子是哪一根,容器的高度h肯定取较短的那一侧高度,也就是第一根柱子高度height[0],接下来只需要看容器的宽度,也就是(j-i)的大小就好了,显然n-1是最大的宽度了,所以此时右指针是n-1是最优选择;(b)如果选取的右侧柱子比第一根矮,那么h<height[0]还小,而且容器的宽度比n-1也小,所以容积肯定也比(n-1)*height[0]小了。

        所以第一种情况下,最大的容积res=(n-1)*height[0],把这个值保存下来,想要看还有没有比这个res还大的情况,第一根柱子就不用考虑了,故把i加一,从第二根柱子开始重复这个比较过程;

(2)第一根高度比最后一根高度要高:那么跟上面这种情况类似,我们从后往前看,从最后一根柱子的视角来看,另一根柱子选择第一根柱子是使得res最大的选择,接下来把res保存下来,把j减一,就可以不用考虑最后一根柱子了。

从而,可以实现时间复杂度为O(n)的算法了,即只要一次遍历即可。

解析这个函数:int snd_soc_register_card(struct snd_soc_card *card) 2717 { 2718 int i, ret; 2719 struct snd_soc_pcm_runtime *rtd; 2720 2721 if (!card->name || !card->dev) 2722 return -EINVAL; 2723 2724 for (i = 0; i < card->num_links; i++) { 2725 struct snd_soc_dai_link *link = &card->dai_link[i]; 2726 2727 ret = soc_init_dai_link(card, link); 2728 if (ret) { 2729 dev_err(card->dev, "ASoC: failed to init link %s\n", 2730 link->name); 2731 return ret; 2732 } 2733 } 2734 2735 dev_set_drvdata(card->dev, card); 2736 2737 snd_soc_initialize_card_lists(card); 2738 2739 INIT_LIST_HEAD(&card->dai_link_list); 2740 card->num_dai_links = 0; 2741 2742 INIT_LIST_HEAD(&card->rtd_list); 2743 card->num_rtd = 0; 2744 2745 INIT_LIST_HEAD(&card->dapm_dirty); 2746 INIT_LIST_HEAD(&card->dobj_list); 2747 card->instantiated = 0; 2748 mutex_init(&card->mutex); 2749 mutex_init(&card->dapm_mutex); 2750 mutex_init(&card->dapm_power_mutex); 2751 2752 ret = snd_soc_instantiate_card(card); 2753 if (ret != 0) 2754 return ret; 2755 2756 /* deactivate pins to sleep state */ 2757 list_for_each_entry(rtd, &card->rtd_list, list) { 2758 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 2759 int j; 2760 2761 for (j = 0; j < rtd->num_codecs; j++) { 2762 struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; 2763 if (!codec_dai->active) 2764 pinctrl_pm_select_sleep_state(codec_dai->dev); 2765 } 2766 2767 if (!cpu_dai->active) 2768 pinctrl_pm_select_sleep_state(cpu_dai->dev); 2769 } 2770 2771 return ret; 2772 } 2773 EXPORT_SYMBOL_GPL(snd_soc_register_card);
最新发布
07-17
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值