11.Container With Most Water
思路1:暴力搜索,每两个line计算一次area,选择最大的那个。但是超时。
int maxArea(vector<int>& height) {
int size = height.size();
if (size < 2) return 0;
int water = 0;
int minLine = 0;
int maxWater = 0;
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
minLine = min(height[i],height[j]);
water = (j - i) * minLine;
if (water > maxWater)
maxWater = water;
}
}
return maxWater;
}
思路2:既然暴力搜索不可以,那就想其他方法。我们知道每两个line之间,area大小取决于line之间的长度和其中小的那个line,就是木桶原理。所以当确定一个line,而且认为它在两个line中属于短的那个,所以我们计算area就是这个line的长度乘距离。所以首先选取第一个start和最后一个end比较,计算area,然后比较start和end哪个更短,说明和短的那条line做组合的line中,最大值就是start和end,然后哪边更短,在那边移动一步,重复上边的比较。可以看这个动图
每次从短的那条line向中间移动比较容易理解,但是有点难以理解的就是为什么是左右两条边比较,还有可能是中间一条line ,和经过的边进行比较。其实可以这么理解,如果已经比较完的边,说明这条边已经作为那个短板计算过了,我们记录的最长的宽度 * 短边,这时候我们就可以把它删除掉,然后再剩下的边中进行计算比较。
int maxArea(vector<int>& height) {
int size = height.size();
if (size < 2) return 0;
int minLine = 0;
int maxWater = 0;
int water = 0;
int i = 0,j = size - 1;
while(i < j) {
minLine = min(height[i],height[j]);
water = (j - i) * minLine;
if (water > maxWater)
maxWater = water;
if (minLine == height[i])
i++;
else
j--;
}
return maxWater;
}
14 Longest Common Prefix 最长共同前缀
思路1:每两个一起比较,vector vec,先比较vec[0]和vec[1]的公共前缀,然后结果再和vec[2]比较,以此类推。
string longestCommonPrefix(vector<string>& strs) {
if (strs.empty()) return "";//注意这个空的情况
string temp = strs[0];
for (int i = 0; i < strs.size(); i++) {
temp = commonPrefixOfTwo(temp,strs[i]);
if (temp == "")
return "";
}
return temp;
}
string commonPrefixOfTwo(string& s1,string& s2) {
if (s1.empty() || s2.empty()) return "";
string commonPre = "";
for (int i = 0; i < s1.size(); i++) {
if (s1[i] != s2[i] || s2.size() <= i)
return commonPre;
commonPre.push_back(s1[i]);
}
return commonPre;
}
思路2:纵向对比。我们定义两个变量i和j,其中i是遍历搜索字符串中的字符,j是遍历字符串集中的每个字符串。这里将单词上下排好,则相当于一个各行长度有可能不相等的二维数组,我们遍历顺序和一般的横向逐行遍历不同,而是采用纵向逐列遍历,在遍历的过程中,如果某一行没有了,说明其为最短的单词,因为共同前缀的长度不能长于最短单词,所以此时返回已经找出的共同前缀。我们每次取出第一个字符串的某一个位置的单词,然后遍历其他所有字符串的对应位置看是否相等,如果有不满足的直接返回res,如果都相同,则将当前字符存入结果,继续检查下一个位置的字符.
//纵向比较
string longestCommonPrefix(vector<string>& strs) {
if (strs.empty()) return "";
string temp = "";
string flag = strs[0];
for (int i = 0; i < flag.size(); i++) {
char ch = flag[i];
for (int j = 0; j < strs.size(); j++) {
if (strs[j][i] != ch || strs[j].size() <= i)
return temp;
}
temp.push_back(ch);
}
return temp;
}
15 3Sum
思路:因为a+b+c = 0,我们先把vector进行排序,然后从小到大进行遍历,把a 确定后,然后a后面的数进行加和判断是否等于-a,加和计算是取头和尾两个指针遍历计算。如果相等,那么就可以把结果放入vector,同时可以继续向中间移动指针,这时候要循环判断向中间靠近的数是否和之前的数相等。
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
int size = nums.size();
int oldKey = INT_MIN,newKey;
for (int k= 0; k< size - 1; k++) {
newKey = -nums[k];
if (newKey == oldKey) {
continue;
}
int i = k + 1;
int j = size - 1;
while(i < j) {
if (nums[i] + nums[j] < newKey)
i++;
else if (nums[i] + nums[j] > newKey)
j--;
else{
vector<int> temp;
temp.push_back(nums[k]);
temp.push_back(nums[i]);
temp.push_back(nums[j]);
res.push_back(temp);
while (nums[i+1] == nums[i] && i + 1 < size) i++;
while (nums[j-1] == nums[j] && j - 1 >= 0) j--;
i++;
j--;
}
}
oldKey = newKey;
}
return res;
}
16 3Sum Closest
思路:和15题思路相同
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int size = nums.size();
int closetSum = INT_MAX;
int closeTarget;
for (int k = 0; k < size - 2; k++) {
int i = k + 1;
int j = size - 1;
int key = target - nums[k];
int diff;
while (i < j) {
if (nums[i] + nums[j] == key)
return target;
else if (nums[i] + nums[j] < key) {
diff = key - (nums[i] + nums[j]);
if (diff < closetSum) {
closetSum = diff;
closeTarget = target - closetSum;
}
i++;
}
else {
diff = (nums[i] + nums[j]) - key;
if (diff < closetSum) {
closetSum = diff;
closeTarget = target + closetSum;
}
j--;
}
}
}
return closeTarget;
}
17 Letter Combinations of a Phone Number
public:
vector<string> letterCombinations(string digits) {
vector<string> res;
vector<string> nums = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
if (digits.size() == 0) {
return res;
}
res.push_back("");
for (char ch : digits) {
int index = ch - '0';
string numStr = nums[index];
vector<string> temp;
for (int i = 0; i < numStr.size(); i++) {
for (int j = 0; j < res.size(); j++) {
temp.push_back(res[j] + numStr[i]);
}
}
res = temp;
}
return res;
}
18.4Sum
思路:和之前的2sum,3sum思路相同,只是多加了一层循环,核心思想就是先排序,然后两头指针,进行相加比较。为了防止有重复项,用了set。
vector<vector<int>> fourSum(vector<int>& nums, int target) {
set<vector<int>> res;
sort(nums.begin(),nums.end());
int size = nums.size();
for (int i = 0; i < size - 3; i++) {
for (int j = i + 1; j < size - 2; j++) {
int left = j + 1;
int right = size - 1;
int key = target - nums[i] - nums[j];
int temp;
while (left < right) {
temp = nums[left] + nums[right];
if (temp < key) {
left++;
}
else if (temp > key) {
right--;
}
else {
vector<int> vec = {nums[i],nums[j],nums[left],nums[right]};
res.insert(vec);
left++;
right--;
}
}
}
}
return vector<vector<int>>(res.begin(),res.end());
}
19 Remove Nth Node From End of List
思路:既然要求一次遍历,就要在找到尾指针的时候,确定删除点的位置,所以选择双指针。首先cur指针先向前走N步,如果此时cur指向空,说明N为链表的长度,则需要移除的为首元素,那么此时我们返回head->next即可,如果cur存在,我们再继续往下走,此时pre指针也跟着走,直到cur为最后一个元素时停止,此时pre指向要移除元素的前一个元素,我们再修改指针跳过需要移除的元素即可。
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* pre = head;
ListNode* cur = head;
if (!head) return NULL;
int i = 0;
while (i < n) {
cur = cur->next;
i++;
}
if (!cur) return head->next;
while (cur->next) {
pre = pre->next;
cur = cur->next;
}
pre->next = pre->next->next;
return head;
}
20.Valid Parentheses
思路:这个就是入栈出栈。当遇到一个右括号时,判断栈顶元素是不是和它配对,我们查askii表,看到
括号之间差值是1或者2,所以判断差是不是小于3,就可以判断是不是配对。
bool isValid(string s) {
stack<char> brackets;
for (char ch : s) {
if (ch == '(' || ch == '{' || ch == '[') {
brackets.push(ch);
}
else {
if (brackets.empty()) return false;
char temp = brackets.top();;
if (abs(ch - temp) < 3)
brackets.pop();
else
return false;
}
}
return brackets.empty();
}