题目
Given n non-negative integers a1, a2, …, an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) 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.
思路
实在是想不到好的方法,参考了下教程,发现双指针法,真的是简单又明了。
思路是这样的:
- 现在有两个指针,一个指数组头,一个指数组尾。
- 一个max变量,用来记录最大的面积
- 判断两个指针所指的列围起来的面积,与max求最大值
- 判断两个指针所指的列高低,指向低的那个指针向另一个指针移动
- 两个指针相遇则退出
这个思路的关键点就是第四条,为何要这么做?
这就好比木桶原理,木桶能装多少水是取决于矮的那块板子的。这个短板就相当于这道题中低的那一列,只要这块短板不被替换,那么另一列再怎么移动,围起来的面积也不会比现在大,因为两个指针是相向运动,它们之间的距离是越来越短的。这就是为什么要判断两列谁短,短的那列要移动。
代码
int maxArea(int* height, int heightSize) {
int start = 0, max = 0, end = heightSize - 1;
while(start < end){
int left = height[start];
int right = height[end];
int area = (end - start) * (left > right ? right : left);
max = max > area ? max : area;
if(left >= right){
end--;
}
else{
start++;
}
}
return max;
}
6ms解决,C还是快啊!
总结
以后遇到这种题不能单纯以解决为目标,解决是很简单,O(n2)的方法,大家都会写。还是得以巧妙的方法解决有意思。多学多看吧!
下面的为之前的内容,可不看,暴力破解用例过的
一开始的代码
nt maxArea(int* height, int heightSize) {
int max = 0;
if(heightSize == 0 || heightSize == 1){
return 0;
}
for(int i = 0; i < heightSize; i++){
for(int j = i + 1; j < heightSize; j++){
int row = j - i;
int col = height[j] < height[i] ? height[j] : height[i];
int area = row * col;
max = max > area ? max : area;
}
}
return max;
}
复杂度为O(N2)超时了
2.0版本
还是超时,不过不是之前的超时用例了
int maxArea(int* height, int heightSize) {
int max = 0;
if(heightSize == 0 || heightSize == 1){
return 0;
}
for(int i = 0; i < heightSize; i++){
if(i <= heightSize / 2){//距离右端远
int j = heightSize - 1;
while(j > i){//从右开始,找到高度大于i的第一个也是最远的j
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], j -i);
max = max > (height[i] * (j - i)) ? max : height[i] * (j - i);
break;
}
else{
j--;
}
}
if(j - i < i){//右端的距离小于左端最远的距离,则左端可能存在大于目前面积的列,检查左端
int k = 0;
while(k < i && i - k > j - i){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], i-k);
max = max > (height[i] * (i - k)) ? max : height[i] * (i - k);
break;
}
else{
k++;
}
}
}
}
else{//距离左端远,同上
int j = 0;
while(j < i){
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], i-j);
max = max > (height[i] * (i - j)) ? max : height[i] * (i - j);
break;
}
else{
j++;
}
}
if(i - j < heightSize - i - 1){
int k = heightSize - 1;
while(k > i && k - i > i - j){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], k -i);
max = max > (height[i] * (k - i)) ? max : height[i] * (k - i);
break;
}
else{
k--;
}
}
}
}
}
return max;
}
3.0版
想到在上个版本上更进一步的简便方法,循环时多加个判断条件,可以少判断几次
int maxArea(int* height, int heightSize) {
int max = 0;
if(heightSize == 0 || heightSize == 1){
return 0;
}
for(int i = 0; i < heightSize; i++){
if(height[i] == 0){
continue;
}
if(i <= (heightSize - 1) / 2){//距离右端远
int j = heightSize - 1;
while(j > i && ((j - i) > max / height[i])){//从右开始,找到高度大于i的第一个也是最远的j
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], j -i);
max = max > (height[i] * (j - i)) ? max : height[i] * (j - i);
break;
}
else{
j--;
}
}
if(j - i < i){//右端的距离小于左端最远的距离,则左端可能存在大于目前面积的列,检查左端
int k = 0;
while(k < i && i - k > j - i){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], i-k);
max = max > (height[i] * (i - k)) ? max : height[i] * (i - k);
break;
}
else{
k++;
}
}
}
}
else{//距离左端远,同上
int j = 0;
while(j < i && ((i - j) > max / height[i])){
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], i-j);
max = max > (height[i] * (i - j)) ? max : height[i] * (i - j);
break;
}
else{
j++;
}
}
if(i - j < heightSize - i - 1){
int k = heightSize - 1;
while(k > i && k - i > i - j){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], k -i);
max = max > (height[i] * (k - i)) ? max : height[i] * (k - i);
break;
}
else{
k--;
}
}
}
}
}
return max;
}
还是未通过,当测试用例为倒序时,遍历的次数还是会较多,因此改下算法,先算下用例大概为升序还是降序,若为升序则从左向右开始遍历,若为降序,则从右开始向左遍历。
改完了,还是未过…只好根据测试用例,采用暴力破解了
int maxArea(int* height, int heightSize) {
int max = 0;
if(heightSize == 0 || heightSize == 1){
return 0;
}
int asc = 1;
for(int i = 1; i < heightSize; i++){
if(height[i] < height[i - 1]){
asc = 0;
break;
}
}
if(asc == 1){//升序数列
for(int i = 0; i < heightSize; i++){
max = max > (heightSize - i - 1) * height[i] ? max : (heightSize - i - 1) * height[i];
}
return max;
}
int desc = 1;
for(int i = 0; i < heightSize; i++){
if(i == heightSize - 1){
if(height[i] > height[i - 1]){
desc = 0;
break;
}
}
else{
if(height[i] < height[i + 1]){
desc = 0;
break;
}
}
}
if(desc == 1){
for(int i = heightSize - 1; i >= 0; i--){
max = max > i * height[i] ? max : i * height[i];
}
return max;
}
if(asc >= 0){
for(int i = 0; i < heightSize; i++){
if(height[i] == 0){
continue;
}
if(i <= (heightSize - 1) / 2){//距离右端远
int j = heightSize - 1;
while(j > i && ((j - i) > max / height[i])){//从右开始,找到高度大于i的第一个也是最远的j
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], j -i);
max = max > (height[i] * (j - i)) ? max : height[i] * (j - i);
break;
}
else{
j--;
}
}
if(j - i < i){//右端的距离小于左端最远的距离,则左端可能存在大于目前面积的列,检查左端
int k = 0;
while(k < i && i - k > j - i){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], i-k);
max = max > (height[i] * (i - k)) ? max : height[i] * (i - k);
break;
}
else{
k++;
}
}
}
}
else{//距离左端远,同上
int j = 0;
while(j < i && ((i - j) > max / height[i])){
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], i-j);
max = max > (height[i] * (i - j)) ? max : height[i] * (i - j);
break;
}
else{
j++;
}
}
if(i - j < heightSize - i - 1){
int k = heightSize - 1;
while(k > i && k - i > i - j){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], k -i);
max = max > (height[i] * (k - i)) ? max : height[i] * (k - i);
break;
}
else{
k--;
}
}
}
}
}
}
else{printf("new algo");
for(int i = heightSize - 1; i >= 0 ; i--){
if(height[i] == 0){
continue;
}
if(i >= (heightSize - 1) / 2){
int j = 0;
while(j < i && (i - j) > max / height[i]){
if(height[j] >= height[i]){
max = max > (height[i] * (i - j)) ? max : height[i] * (i - j);
break;
}
else{
j++;
}
}
if(i - j < heightSize - i - 1){
int k = heightSize - 1;
while(k > i && k - i > i - j){
if(height[k] >= height[i]){
max = max > (height[i] * (k - i)) ? max : height[i] * (k - i);
break;
}
else{
k--;
}
}
}
}
else{
int j = heightSize - 1;
while(j > i && ((j - i) > max / height[i])){//从右开始,找到高度大于i的第一个也是最远的j
if(height[j] >= height[i]){
//printf("%d * %d | ", height[i], j -i);
max = max > (height[i] * (j - i)) ? max : height[i] * (j - i);
break;
}
else{
j--;
}
}
if(j - i < i){//右端的距离小于左端最远的距离,则左端可能存在大于目前面积的列,检查左端
int k = 0;
while(k < i && i - k > j - i){
if(height[k] >= height[i]){
//printf("%d * %d | ", height[i], i-k);
max = max > (height[i] * (i - k)) ? max : height[i] * (i - k);
break;
}
else{
k++;
}
}
}
}
}
}
return max;
}
观察测试用例,发现超时的用例要么是升序,要么是降序,因此,在最开始判断用例是升序还是降序,然后相应的遍历一次解决…
嗯,这是个投机取巧的方法,纯粹只是为了通过题目而已..
不过根据原来的算法,其实也才300ms左右,也算过了吧~哈哈
代码中我觉的有大量冗余的地方,但是懒得改了,就放在那里了