LeetCode——Container With Most Water

本文探讨了一个经典的算法问题:如何在给定的高度数组中找到能够容纳最多水的两个垂直线所形成的容器。通过三种不同的算法实现,包括最简单的暴力枚举、基于排序的方法以及最优的双指针策略,来逐步优化解决方案。

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

Given n non-negative integers a1a2, ..., an, where each represents a point at coordinate (iai). n vertical lines are drawn such that the two endpoints of line i is at (iai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.

Note: You may not slant the container.

» Solve this problem


1.最简单的算法:

枚举所有可能的情况,求出最大面积。

时间复杂度O(n2)


2.排序:

在方法1中,我们其实做了很多不必要的工作。

例如:枚举了(2,8),(2,7),(2,5),(2,4),(2,3),因为2要低于3,4,5,7,8,因此这五组的高度都是由2的高度决定。

即,area(2,8)=height(2)*(8-2)>area(2,7)=height(2)*(7-2)> ...>area(2,3)=height(2)*(3-2)。

所以,我们只用考虑离2最远的8即可。

算法的基本思想就是对所有的高度由低到高排序,然后依次取出,每次找寻离该点最远的一根。

for (int i = 0; i < sortedHeight.size(); i++) {
    max = max{sortedHeight[i] * (i - left), sortedHeight[i] * (right - i), max}
}
维护left和right(left指的是现在剩余的线中最左边的,right指的是现在剩余的线中最右边的):

取出i之后,需要更新left和right。

可以使用一个标记数组,记录哪些线是已经取出的。如果left已经取出则不断向左移,同法维护right。

for (int i = 0; i < sortedHeight.size() - 1; i++) {
    max = max{sortedHeight[i] * (i - left), sortedHeight[i] * (right - i), max};
    used[sortedHeight[i].在原来的height数组中的下标] = true;
    while (used[left]) {
        left++;
    }
    while (used[right]) {
        right--;
    }
}

时间复杂度为O(nlogn)。

class Component {
public:
    int height;
    int no;
    
    Component() {}
    Component(int h, int n): height(h), no(n) {}
};

bool comp(const Component &c1, const Component &c2) {
    return c1.height < c2.height;
}

// nlogn
class Solution {
public:
    int maxArea(vector<int> &height) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int count = height.size();
        Component components[count];
        for (int i = 0; i < count; i++) {
            components[i] = Component(height[i], i);
        }
        sort(components, components + count, comp);
        
        bool hash[count];
        memset(hash, 0, sizeof(bool) * count);
        
        int right= count - 1, left= 0, max = 0;
        int temp1 = 0, temp2 = 0;
        for (int i = 0; i < count - 1; i++) {
            temp1 = (right - components[i].no) * components[i].height;
            temp2 = (components[i].no - left) * components[i].height;
            max = temp1 > max ? temp1 : max;
            max = temp2 > max ? temp2 : max;
            
            hash[components[i].no] = true;
            while (hash[right]) {
                right--;
            }
            while (hash[left]) {
                left++;
            }
        }
                
        return max;
    }
};


3.最快速的算法:


再次看看这个图。

我们看看(1,8)和(1,7):

由于height[1]<height[8],所以area(1,8)=height[1]*(8-1)。而height[7]>height[8]>height[1],所以area(1,7)=height[1]*(7-1)<area(1,8)。

假设,height[7]<=height[8],area(1,7)和area(1,8)的关系会是什么。

1.height[1]<height[7]<=height[8]:area(1,7)=height[1]*(7-1)<area(1,8);

2.height[7]<=height[1]<height[8]:area(1,7)=height[7]*(7-1)<=height[1]*(7-1)<area(1,8)。

所以,无论如何area(1,8)>area(1,7)。

同理可知,area(1,8)>area(1,7)>area(1,6)>area(1,5)>...>area(1,2)。

因此,我们只需要计算area(1,8)。

下一步,我们需要考虑的是area(2,8),因为height[2]<height[8],所以area(2,8)>area(2,7)>area(2,6)>...>area(2,3)。

当我们计算area(4,8)时,因为height[4]>height[8],类似地,可以推出area(4,8)>area(5,8)>area(6,8)>area(7,8)。

之后,我们计算的将是(5,7)和(6,7)。


算法的雏形:

while (head < tail) {
    if (height[head] < height[tail]) {
        max = max{height[head] * (tail - head), max};
        head++;
    }
    else {
        max = max{height[tail] * (tail - head), max};
        tail--;
    }
}


代码:

class Solution{
public:
    int maxArea(vector<int> &height) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int head = 0, tail = height.size() - 1;
        int max = 0, temp = 0;
        while (head < tail) {
            temp = tail - head;
            if (height[head] < height[tail]) {
                temp *= height[head];
                head++;
            }
            else {
                temp *= height[tail];
                tail--;
            }
            if (temp > max) {
                max = temp;
            }
        }
        return max;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值