一、前言
标签:动态规划、栈运用、双指针。
问题来源LeetCode 5 难度:困难。
问题链接:https://leetcode-cn.com/problems/longest-palindromic-substring/
二、题目
三、思路
三种解题方法。
方法一:动态规划
- 找到数组中从下标 i 到最左端最高的条形块高度 left_max。
- 找到数组中从下标 i 到最右端最高的条形块高度 right_max。
- 扫描数组 height 并更新答案:
- 累加 min(max_left[i],max_right[i])−height[i] 到 ansans 上
复杂度分析:时间复杂度O(n), 空间复杂度O(n)
方法二:栈的应用
使用栈来存储条形块的索引下标。
遍历数组:
当栈非空且 height[current] > height[st.top()]。意味着栈中元素可以被弹出。弹出栈顶元素 top。计算当前元素和栈顶元素的距离,准备进行填充操作。distance = current−st.top() − 1,找出界定高度,bounded_height = min(height[current], height[st.top()]) − height[top],往答案中累加积水量ans += distance×bounded_height
将当前索引下标入栈。
将 current 移动到下个位置。
时间复杂度:O(n), 空间复杂度:O(n)
方法三:双指针
和栈用有些类似,两头分别一个指针,向中间遍历。
四、编码实现
//==========================================================================
/*
* @file : 042_Trap.h
* @label : 动态规划、栈运用、双指针
* @blogs : https://blog.csdn.net/nie2314550441/article/details/107498351
* @author : niebingyu
* @date : 2020/07/20
* @title : 42.接雨水
* @purpose : 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
*
* 示例1:
* 输入: [0,1,0,2,1,0,1,3,2,1,2,1]
* 输出: 6
*
* 来源:力扣(LeetCode)
* 难度:困难
* 链接:https://leetcode-cn.com/problems/trapping-rain-water/
*/
//==========================================================================
#pragma once
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <assert.h>
using namespace std;
#define NAMESPACE_TRAP namespace NAME_TRAP {
#define NAMESPACE_TRAPEND }
NAMESPACE_TRAP
/*
* 算法
* 找到数组中从下标 i 到最左端最高的条形块高度 left_max。
* 找到数组中从下标 i 到最右端最高的条形块高度 right_max。
* 扫描数组 height 并更新答案:
* 累加 min(max_left[i],max_right[i])−height[i] 到 ansans 上
*/
// 方法一,动态规划
// 时间复杂度:O(n), 空间复杂度:O(n)
class Solution_1
{
public:
int trap(vector<int>& height)
{
if (height.empty())
return 0;
int ans = 0;
int size = height.size();
vector<int> left_max(size), right_max(size);
left_max[0] = height[0];
for (int i = 1; i < size; i++)
left_max[i] = max(height[i], left_max[i - 1]);
right_max[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--)
right_max[i] = max(height[i], right_max[i + 1]);
for (int i = 1; i < size - 1; i++)
ans += min(left_max[i], right_max[i]) - height[i];
return ans;
}
};
/*
* 算法
* 使用栈来存储条形块的索引下标。
* 遍历数组:
* 当栈非空且 height[current] > height[st.top()]
* 意味着栈中元素可以被弹出。弹出栈顶元素 top。
* 计算当前元素和栈顶元素的距离,准备进行填充操作
* distance = current−st.top()−1
* 找出界定高度
* bounded_height = min(height[current], height[st.top()])−height[top]
* 往答案中累加积水量ans += distance×bounded_height
* 将当前索引下标入栈
* 将 current 移动到下个位置
*/
// 方法二,栈的应用
// 时间复杂度:O(n), 空间复杂度:O(n)
class Solution_2
{
public:
int trap(vector<int>& height)
{
int ans = 0, current = 0;
stack<int> st;
while (current < height.size())
{
while (!st.empty() && height[current] > height[st.top()])
{
int top = st.top();
st.pop();
if (st.empty())
break;
int distance = current - st.top() - 1;
int bounded_height = min(height[current], height[st.top()]) - height[top];
ans += distance * bounded_height;
}
st.push(current++);
}
return ans;
}
};
// 方法三,使用双指针
// 时间复杂度:O(n), 空间复杂度:O(1)
class Solution_3
{
public:
int trap(vector<int>& height)
{
int left = 0, right = height.size() - 1;
int ans = 0;
int left_max = 0, right_max = 0;
while (left < right)
{
if (height[left] < height[right])
{
height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
++left;
}
else
{
height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
--right;
}
}
return ans;
}
};
以下为测试代码//
// 测试 用例 START
void test(const char* testName, vector<int>& height, int expect)
{
Solution_2 s;
int result = s.trap(height);
if (result == expect)
cout << testName << ", solution passed." << endl;
else
cout << testName << ", solution failed. " << endl;
}
// 测试用例
void Test1()
{
vector<int> height = { 5,0,1,2 };
int expect = 1;
test("Test1()", height, expect);
}
void Test2()
{
vector<int> height = { 0,1,0,2,1,0,1,3,2,1,2,1 };
int expect = 6;
test("Test2()", height, expect);
}
NAMESPACE_TRAPEND
// 测试 用例 END
//
void Trap_Test()
{
cout << "------ start 42.接雨水 ------" << endl;
NAME_TRAP::Test1();
NAME_TRAP::Test2();
cout << "------ end 42.接雨水 --------" << endl;
}
执行结果: