【力扣Leetcode题解系列之0011—Container With Most Water 盛最多水的容器:多语言实现与拓展】

11. Container With Most Water 盛最多水的容器:多语言实现与拓展

一、题目分析

给定 n 个非负整数 a1, a2, ..., an,每个数代表坐标 (i, ai) 处的一个点。绘制 n 条垂直线,使得第 i 条线的两个端点分别为 (i, ai)(i, 0)。找到两条线,它们与 x 轴构成一个容器,使得该容器能容纳最多的水。注意容器不能倾斜,且 n 至少为 2

二、常用解法

双指针法

  1. 思路:使用两个指针,一个指向数组的开头(左指针 l),一个指向数组的末尾(右指针 r)。计算当前指针所指元素构成的容器面积,即 min(height[l], height[r]) * (r - l)。然后,将较短的指针向中间移动,因为较短边决定了容器的高度,移动较短边有可能找到更高的边,从而得到更大的面积。每次移动后更新最大面积。
  2. 优点:这种方法避免了暴力解法中对所有可能组合的计算,将时间复杂度从 O(n2)O(n^2)O(n2) 降低到 O(n)O(n)O(n)

三、多语言实现

Python实现

class Solution:
    def maxArea(self, height):
        ans = 0
        l, r = 0, len(height) - 1
        while l < r:
            ans = max(ans, min(height[l], height[r]) * (r - l))
            if height[l] < height[r]:
                l += 1
            else:
                r -= 1
        return ans

Java实现

class Solution {
    public int maxArea(int[] height) {
        int ans = 0;
        int l = 0, r = height.length - 1;
        while (l < r) {
            ans = Math.max(ans, Math.min(height[l], height[r]) * (r - l));
            if (height[l] < height[r]) {
                l++;
            } else {
                r--;
            }
        }
        return ans;
    }
}

C实现

#include <stdio.h>
#include <stdlib.h>

int maxArea(int* height, int heightSize) {
    int ans = 0;
    int l = 0, r = heightSize - 1;
    while (l < r) {
        int area = (r - l) * (height[l] < height[r]? height[l] : height[r]);
        ans = ans > area? ans : area;
        if (height[l] < height[r]) {
            l++;
        } else {
            r--;
        }
    }
    return ans;
}

C++实现

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    int maxArea(vector<int>& height) {
        int ans = 0;
        int l = 0, r = height.size() - 1;
        while (l < r) {
            ans = max(ans, min(height[l], height[r]) * (r - l));
            if (height[l] < height[r]) {
                ++l;
            } else {
                --r;
            }
        }
        return ans;
    }
};

Go实现

package main

import "fmt"

func maxArea(height []int) int {
    ans := 0
    l, r := 0, len(height)-1
    for l < r {
        area := (r - l) * min(height[l], height[r])
        if area > ans {
            ans = area
        }
        if height[l] < height[r] {
            l++
        } else {
            r--
        }
    }
    return ans
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

四、算法复杂性分析

  1. 时间复杂度:双指针法中,指针 lr 最多遍历数组一次,因此时间复杂度为 O(n)O(n)O(n),其中 n 是数组 height 的长度。
  2. 空间复杂度:所有实现中,除了输入数组外,只使用了常数级别的额外空间(如指针变量和存储最大面积的变量),所以空间复杂度为 O(1)O(1)O(1)

五、题目扩展及难度加深

扩展题目1:三维容器问题

假设现在是在三维空间中,给定 n 个长方体的底面边长数组 lengths 和高度数组 heights,每个长方体的底面边长为 lengths[i],高度为 heights[i]。要选择两个长方体,使得它们与底面构成的三维容器能容纳最多的液体。这里液体的高度由两个长方体中较矮的那个决定,底面面积由两个长方体底面边长中较短的那个与它们之间的距离决定。

扩展题目2:可变挡板高度

假设挡板的高度可以在一定范围内动态变化,例如,每次移动指针时,挡板高度可能会随机增减一个较小的值。如何在这种情况下找到最大盛水量?

难度加深题目:多约束条件下的盛水问题

增加约束条件,例如某些挡板之间存在障碍物,不能同时选择,或者选择某些挡板会有额外的成本。如何在这些复杂约束下找到能盛最多水的容器?

六、应用场合

  1. 资源分配优化:在一些资源分配场景中,如网络带宽分配,每个节点有不同的带宽上限,需要找到两个节点之间的最优分配方式,以最大化传输的数据量,类似于找到能盛最多水的容器。
  2. 图像处理:在图像边缘检测或形状识别中,可能需要找到图像中两个边界之间的最大区域,该区域的大小受边界的某些属性限制,类似于本题寻找最大面积的容器。
  3. 数据挖掘:在时间序列数据中,寻找两个时间点之间的最大某种度量值,且该度量值受这两个时间点对应数据值的限制,类似于本题找最大面积。

12. Integer to Roman 整数转罗马数字:全面解析与拓展

一、题目分析

罗马数字由七个不同的符号表示:I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和 M(1000)。通常罗马数字从左到右按从大到小的顺序书写,但存在六种减法的特殊情况:I 可放在 V(5)和 X(10)前表示 4 和 9;X 可放在 L(50)和 C(100)前表示 40 和 90;C 可放在 D(500)和 M(1000)前表示 400 和 900。给定一个在 1 到 3999 范围内的整数,将其转换为罗马数字。

二、常用解法

贪心算法解法

  1. 思路:建立一个整数与罗马数字符号的对应关系表,从最大的整数开始,依次检查输入整数中包含多少个该整数对应的罗马数字符号。若包含,则将对应的罗马数字符号添加到结果字符串中,并从输入整数中减去该整数。重复此过程,直到输入整数为 0。这种方法利用了贪心策略,每次都尽可能地使用最大的罗马数字符号来表示输入整数,从而保证了结果的正确性和唯一性。
  2. 优点:算法思路清晰,实现简单,能够高效地完成转换任务。

三、多语言实现

Python实现

class Solution:
    def intToRoman(self, num):
        values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
        symbols = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
        result = ""
        for i in range(len(values)):
            while num >= values[i]:
                result += symbols[i]
                num -= values[i]
        return result

Java实现

class Solution {
    public String intToRoman(int num) {
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < values.length; i++) {
            while (num >= values[i]) {
                result.append(symbols[i]);
                num -= values[i];
            }
        }
        return result.toString();
    }
}

C实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* intToRoman(int num) {
    int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    char* symbols[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
    char* result = (char*)malloc(16 * sizeof(char));
    result[0] = '\0';
    for (int i = 0; i < 13; i++) {
        while (num >= values[i]) {
            strcat(result, symbols[i]);
            num -= values[i];
        }
    }
    return result;
}

C++实现

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Solution {
public:
    string intToRoman(int num) {
        vector<int> values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        vector<string> symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        string result = "";
        for (int i = 0; i < values.size(); i++) {
            while (num >= values[i]) {
                result += symbols[i];
                num -= values[i];
            }
        }
        return result;
    }
};

Go实现

package main

import (
    "fmt"
)

func intToRoman(num int) string {
    values := []int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
    symbols := []string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}
    result := ""
    for i := 0; i < len(values); i++ {
        for num >= values[i] {
            result += symbols[i]
            num -= values[i]
        }
    }
    return result
}

四、算法复杂性分析

  1. 时间复杂度:算法中有两层循环,外层循环遍历整数与罗马数字符号对应关系表,共 13 次(因为有 13 种不同的罗马数字表示),内层循环在每次外层循环中最多执行 num / values[i] 次。由于 num 的最大值为 3999,而每次内层循环至少会使 num 减少一个正数,所以总的时间复杂度为 O(1)O(1)O(1)。因为无论输入 num 是多少,执行的操作次数都是有限且固定的,不随输入规模的变化而变化。
  2. 空间复杂度:所有实现中,除了输入的整数 num 外,额外使用的空间主要是用于存储对应关系表(valuessymbols)以及结果字符串。存储对应关系表的空间是固定的,不随输入 num 的变化而变化,结果字符串的长度最大为 15(例如 3999 对应的罗马数字 “MMMCMXCIX”),所以空间复杂度为 O(1)O(1)O(1)

五、题目扩展及难度加深

扩展题目1:支持更大范围的整数转换

假设需要支持将 1 到 3999999 的整数转换为罗马数字。这需要扩展现有的对应关系表,增加新的罗马数字表示,如 M 可以重复表示更大的数(如 MMM 表示 3000,MMMM 表示 4000 等),或者引入新的符号来表示更大的数量级。同时,算法需要考虑如何高效地处理这种扩展后的转换逻辑。

扩展题目2:罗马数字与整数的双向转换

不仅要实现整数到罗马数字的转换,还要实现罗马数字到整数的转换。这需要编写一个反向的转换函数,通过解析罗马数字字符串,根据罗马数字的规则计算出对应的整数值。实现过程中需要处理好减法规则(如 IV 表示 4,IX 表示 9 等)以及字符顺序的正确性。

难度加深题目:带条件的罗马数字转换

增加一些特殊条件,例如,某些罗马数字组合在特定场景下有不同的表示方式,或者某些整数不能用常规的罗马数字表示方法,需要使用特殊的表示形式。这要求在转换过程中不仅要遵循基本的转换规则,还要根据这些特殊条件进行额外的处理,大大增加了算法的复杂性。

六、应用场合

  1. 历史研究与文化传承:在历史、考古等领域,罗马数字常用于表示年份、历史事件发生的顺序等。通过将整数转换为罗马数字,可以更好地理解和研究古代文献、历史记录等,有助于文化传承和学术研究。
  2. 排版与编号:在书籍排版、文档编号等方面,罗马数字有时会用于表示章节、附录等的编号,以增加文档的专业性和独特性。将整数转换为罗马数字可满足这种排版和编号的需求。
  3. 密码学与编码:在一些简单的密码学应用或特定的编码系统中,罗马数字可以作为一种特殊的编码方式。整数到罗马数字的转换可作为编码过程的一部分,为数据提供一定程度的保密性和独特性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值