第一道
题目名称:11. Container With Most Water
题目难度:Medium
题目描述: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 and n is at least 2.
题目分析:
题目的意思是给一组数字,每个数字代表一个点(i, ai),这个点与点(i, 0)构成一条直线。找出任意两条这样的直线和X轴三线组成的一个“容器“的最大容量。
本题博主前前后后尝试了四次方法,最后终于比较完美的AC了。在这里记录下每一次所用的方法以及其思路:
一开始理解错了题意,以为两条边只能是相邻的两条,这么一想还觉得题目特别简单,因为相邻的话底是一样大的(长度为1)这样子只需要根据木桶效应找出两个边中比较短的那一条即可。所以只需遍历一次即可,复杂度为O(n)。但是很明显,错误的理解题意的结果是Wrong Answer。
正确理解题意之后想直接使用暴力求解,遍历所有二元组的情况,这样肯定会找到最大值。这种做法肯定没错,但是因为O(n^2)的复杂度,直接Runtime Limit Error了。
第三次尝试时的想法是最大解的情况只可能是将数组排序之后相邻两个数之间围成的区域。但是这个方法没有考虑到底长的情况,所以自然也是错的。
第四次的思路是:使用两个变量,一个表示底的开始,一个表示底的终点。一开始底的起点是数组第一位,终点是数组最后一位。这样去计算两者之间围成的区域,如果比当前记录的最大区域大,则将之记为最大区域值。之后,将两边中较短的一边向中间移动一个单位,再次计算区域大小,重复刚才的后续过程。这里的难点是为什么移动的是两边中较小的一条呢?因为这条是这个区域增加的“短板“。移动较短的一边,也许会遇到一条更大的边,这样的话这种增加有可能抵消了底边的变短,甚至可能使得区域面积变得更大。相反的,如果移动的是较大的一边,最好的情况是遇到一条比较短边还要长的边,但是因为“木桶效应“,整个区域大小还是取决于较短的边,所以移动较长的一边是不可能得到更好的结果的。
最后AC的代码如下,时间复杂度为O(n):
class Solution {
public:
int maxArea(vector<int>& height) {
int max = 0, begin = 0, end = height.size() - 1, h;
while (begin != end) {
h = height[begin] < height[end] ? height[begin] : height[end];
int temp = h * (end - begin);
if (temp > max)
max = temp;
if (height[begin] < height[end]) {
begin++;
} else {
end--;
}
}
return max;
}
};
第二道
题目名称:2. Add Two Numbers
题目难度:Medium
题目描述:You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
题目分析:
一开始最直接的想法就是将每个链表代表的数字提取出来,两者相加之后再存在一个新的链表中。
但是这样作的话会有溢出问题。因为两个加数是用链表表示的而不是用int类型表示的,所以这两个数都有可能超过2的32次方这个表示范围。
合理的算法是每次取出一位,两者相加之后取个位数,用来构造结果链表的新节点。最后AC的代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *t1 = l1, *t2 = l2;
ListNode *result = new ListNode(0);
ListNode *current = result;
int sum = 0;
while (t1 != NULL || t2 != NULL) {
sum /= 10;
if (t1 != NULL) {
sum += t1->val;
t1 = t1->next;
}
if (t2 != NULL) {
sum += t2->val;
t2 = t2->next;
}
ListNode* newNode = new ListNode(sum % 10);
current->next = newNode;
current = newNode;
}
if (sum / 10 == 1)
current->next = new ListNode(1);
return result->next;
}
};