码上掘金——主题四解析
当青训营遇上码上掘金。
在拿到码上掘金主题之后,我内心突然一喜,这主题4不是我们练习了很多次的接雨水问题吗。双指针问题
双指针问题是指可以使用双指针方法来解决的问题,这类问题有很多。顾名思义,双指针方法就是用两个速度不同或者方向不同的指针来进行访问数据,解决问题。
双指针可以分为
- 左右指针
- 快慢指针
左右指针
首先介绍一下左右指针,通常,左右指针是从数组的最左端和最右端开始逐渐向中间靠近,然后遍历完所有数据。经典的快速排序就是这样的例子。
while(low<high)
{
//以keyvalue为界,将待排序数组分为两部分
while(low<high && a[high]>=keyValue){--high;}
swap(&a[low],&a[high]); //大于keyvalue部分
while(low<high && a[low]<=keyValue){++low;}
swap(&a[low],&a[high]); //小于keyvalue部分
}
快慢指针
快慢指针其实是两个步长不同的指针,一个步长大,一个步长小,因此一个移动的快,一个移动的慢。也称为龟兔赛跑算法。其经典例子是寻找单链表的中点。
if(head==nullptr) return nullptr;
node *fast=head;
node *slow=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
接雨水问题解法
基本思路
我们得到的是一组高低不同的柱子,每个位置能接到的水的最高位值由其左边最高的柱子hi和右边最高的柱子hj中的最小值决定,即min(hi,hj),那么我们就可以很简单的算出该位置能存储的水量为最高水位减去地面高度了。因此问题的关键是怎么计算出每个位置的左右位置柱子最高值。
暴力解法
暴力解法的时间复杂度为O(N^2),其思路就是对于每一个位置,遍历左边所有柱子和右边所有柱子得到左右最高值。 代码如下:
for(int i = 1; i < num - 1;i++){
int leftMax = 0,rightMax = 0;
for(int j = 0; j <= i; j++){
leftMax = max(leftMax,height[j]);
}
for(int j = i; j < num; j++){
rightMax = max(rightMax,height[j]);
}
res += min(leftMax,rightMax) - height[i];
}
动态规划
时间复杂度为O(N),先通过最左和最右的柱子高度,通过动规方程解出每个位置对应的最左最右最大柱子高度,再计算每个位置的储水量。代码如下:
dp0[0] = height[0];
dp1[num-1] = height[num-1];
for(int i = 1; i < num; i++){
dp0[i] = max(height[i],dp0[i-1]);
}
for(int i = num-2; i>=0; i--){
dp1[i] = max(height[i],dp1[i+1]);
}
int res = 0;
for(int i = 1; i < num; i++){
res += min(dp0[i],dp1[i])-height[i];
}
双指针解法
双指针就是从左至右设置两个指针分别表示左边最高和右边最高,每次比较两个最高值,较小的那个向对方移动并计算位置对应储水量。代码如下:
while(left <= right){
if(leftMax <= rightMax){
leftMax = max(leftMax,height[left]);
res += leftMax - height[left++];
}else{
rightMax = max(rightMax,height[right]);
res += rightMax - height[right--];
}
}
学习
在查找资料的过程中,我还看到了一种方法,称为单调栈法,根据递减顺序入栈,如果要入栈的元素将要破坏单调性,我们就将栈内元素出栈,并记录储水量,进而维护单调栈,这样当没有元素需要入栈时,我们依次出栈以清空栈,最终得到结果。