照例选题写思路
1.3Sum
来源:https://leetcode.com/problems/3sum/description/
题意
给出一串数,叫你找出所有的a,b,c使得a+b+c=0;要注意的是给出的数,可能重复。
思路
将输入的数排序,从头到尾对每一不同数字循环,下标为i,target = -i。并从用指针指向front = i+1,back = j + 1;
·如arr[front] + arr[back] > target, 说明太大,back–,移动到上一更小的数(因为有序,所以移动到的必然是更小的);
·如arr[front] + arr[back] < target, 说明太小,front++,移动到下一更大的数(因为有序,所以移动到的必然是更大的)。
·如arr[front] + arr[back] == target, 说明符合要求,将这个三元组加到结果集中。
重复上述判定,直到i循环结束。
这样复杂度仅为O(n^2),避免了三重循环的O(n^3)复杂度。
代码
vector<vector<int> > threeSum(vector<int> &num) {
vector<vector<int> > res;
std::sort(num.begin(), num.end());
for (int i = 0; i < num.size(); i++) {
int target = -num[i];
// positive + positive > 0, it's impossible to get a negitive by positive+positive
if (target < 0) break;
int front = i + 1;
int back = num.size() - 1;
while (front < back) {
int sum = num[front] + num[back];
// Finding answer which start from number num[i]
if (sum < target)
front++;
else if (sum > target)
back--;
else {
vector<int> triplet(3, 0);
triplet[0] = num[i];
triplet[1] = num[front];
triplet[2] = num[back];
res.push_back(triplet);
// Processing duplicates of Number 2
// Rolling the front pointer to the next different number forwards
while (front < back && num[front] == triplet[1]) front++;
// Processing duplicates of Number 3
// Rolling the back pointer to the next different number backwards
while (front < back && num[back] == triplet[2]) rear--;
}
}
// Processing duplicates of Number 1
while (i + 1 < num.size() && num[i + 1] == num[i])
i++;
}
return res;
}
2.3Sum Closest
题意
与上一题类似,不过另外给出了一个target,要求找出a+b+c = sum中最接近 target的sum。
思路
思路和上一题是一样的,不过要给多个变量记录最接近target的结果而已。
代码
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
if(nums.size() < 3) return 0;
int closest = nums[0] + nums[1] + nums[2];
std::sort(nums.begin(), nums.end());
for(int first = 0; first < nums.size()-2; first++) {
int second = first + 1;
int third = nums.size()-1;
while(second < third) {
int curSum = nums[first] + nums[second] + nums[third];
if (curSum == target) return curSum;
if(abs(target-curSum)<abs(target-closest)) {
closest = curSum;
}
if(curSum > target) {
--third;
} else {
++second;
}
}
}
return closest;
}
};
3.4Sum
题意
与上一题类似,不过要求计算4个数的和,a+b+c+d的和。
思路
类似的,双重循环套一个线性寻找。
注意两个点:1.重复数字的处理 2.对于两个循环,有些情况开头就不符合条件的,比如最小sum > targer, 最大sum < target这些情况,不用再继续,直接break或者continue。
代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> res;
if(nums.size() < 4) return vector<vector<int>>();
std::sort(nums.begin(), nums.end());
for(int i = 0; i < n - 3;i++) {
if(i > 0 && nums[i] == nums[i-1]) continue; // decrease duplicate
if(nums[i] + nums[i+1]+ nums[i+2] +nums[i+3] > target) break; // too large, search finished
if(nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target) continue; // too small
for(int j = i + 1; j < n - 2; j++ ) {
if(j > i+1 && nums[j] == nums[j-1]) continue; // decrease duplicate
if(nums[i]+nums[j]+nums[j+1]+nums[j+2] > target) break; // too large
if(nums[i] + nums[j] + nums[n-2] + nums[n-1] < target) continue; // too small
int front = j + 1, back = n-1;
while(front < back) {
int sum = nums[front] + nums[back] + nums[i] + nums[j];
if (sum < target) {
front++;
} else if(sum > target) {
back--;
} else {
vector<int> tmp = {nums[i], nums[j], nums[front], nums[back]};
cout << nums[i] << ' ' << nums[j] << ' ' << nums[front] << ' ' << nums[back] << endl;
res.push_back(tmp);
do{front++;}while(front < back && nums[front] == nums[front-1]);
do{back--;}while(front < back && nums[back] == nums[back+1]);
}
}
}
}
return res;
}
};
4. Remove Nth Node From End of List
题意
给出一个链表,要求删除倒数第n个结点
思路
1.最直接的第一个思路就是,遍历一遍,数一下一共多少个结点,就知道倒数第n个是顺数第几个,然后就可以将之删除。这个复杂度是O(2n).
2.更好的一个思路,是复杂度为O(n)。我想不出,参考的http://www.cnblogs.com/grandyang/p/4606920.html
“这道题让我们移除链表倒数第N个节点,限定n一定是有效的,即n不会大于链表中的元素总数。还有题目要求我们一次遍历解决问题,那么就得想些比较巧妙的方法了。比如我们首先要考虑的时,如何找到倒数第N个节点,由于只允许一次遍历,所以我们不能用一次完整的遍历来统计链表中元素的个数,而是遍历到对应位置就应该移除了。那么我们需要用两个指针来帮助我们解题,pre和cur指针。首先cur指针先向前走N步,如果此时cur指向空,说明N为链表的长度,则需要移除的为首元素,那么此时我们返回head->next即可,如果cur存在,我们再继续往下走,此时pre指针也跟着走,直到cur为最后一个元素时停止,此时pre指向要移除元素的前一个元素,我们再修改指针跳过需要移除的元素即可。”
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* first = head, *cur = head;
for(int i = 0; i < n; i++) first = first -> next;
if(first == NULL) {
return head -> next;
}else {
while(first -> next != NULL) {
first = first -> next;
cur = cur -> next;
}
cur -> next = cur ->next ->next;
return head;
}
}
};
5. Valid Parentheses
题意
其实就是括号匹配,给一个字符串,包含()[]{}这三种括号,判断这个括号匹不匹配。
思路
很简单,借助一个栈,左括号入栈。如果是右括号,若栈为空直接返回false。若栈不为空,和栈顶比较,若匹配则出栈,否则返回false。等字符串遍历完毕,若栈为空,则说明括号全部匹配,返回true。
代码
class Solution {
public:
bool isValid(string s) {
stack<char> stk;
for(int i = 0; i < s.size(); i++) {
if (s[i] == ')' || s[i] == '}' || s[i] == ']') {
if(stk.empty()) return false;
else {
if ((stk.top() == '[' && s[i] == ']') || (stk.top() == '{' && s[i] == '}') || (stk.top() == '(' && s[i] == ')'))
stk.pop();
else
return false;
}
} else if (s[i] == '(' || s[i] == '{' || s[i] == '[') {
stk.push(s[i]);
} else {
continue;
}
}
return stk.empty();
}
};
6.Merge Two Sorted Lists
题意
合并两个有序链表,不难。
思路
比较两条链表的最前面元素,哪个比较小,就放到新链表中。如果其中一个已经为空,直接将另一个放进就好。
代码
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* head = new ListNode(-1), *tmp;
ListNode* rear = head;
while (l1 != NULL || l2 != NULL) {
if(NULL == l1 && NULL != l2) {
tmp = l2;
l2 = l2->next;
} else if(NULL == l2 && l1 != NULL) {
tmp = l1;
l1 = l1 -> next;
} else {
if(l1->val < l2->val) {
tmp = l1;
l1 = l1-> next;
} else {
tmp = l2;
l2 = l2->next;
}
}
rear -> next = new ListNode(tmp->val);
rear = rear->next;
}
return head -> next;
}
};
我叫了,才突然想到,其实可以减少很多内存开销而不用new的。用的discussion中的代码,如下
class Solution {
public:
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
ListNode dummy(INT_MIN);
ListNode *tail = &dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
tail->next = l1;
l1 = l1->next;
} else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = l1 ? l1 : l2;
return dummy.next;
}
};
另一个递归的方法
class Solution {
public:
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
if(l1 == NULL) return l2;
if(l2 == NULL) return l1;
if(l1->val < l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
} else {
l2->next = mergeTwoLists(l2->next, l1);
return l2;
}
}
};
7.Generate Parentheses
题意
·给出一个数字n,生成所有合法的的n对括号对的组合。
例如,
For example, given n = 3, a solution set is:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
思路
1
我们发现,n=2的结果,可以基于n=1的结果推出……n=k的结果可以基于 k-1的结果推出。于是我们就可以这样做。原理就是在上一轮的“(”后插入“()”。然后去重。
class Solution {
// 注意:这一段代码思路是正确的,只是不知道哪里问题,最后会多出一个“”空字符串,而且次序和标答不同,因而过不了。
public:
vector<string> generateParenthesis(int n) {
vector<string> ans({"()"}), tmp;
if (n == 0) return tmp;
else {
for(int i = 1; i < n; i++) {
tmp.clear();
for(int ii = 0; ii < ans.size(); ii++) {
tmp.push_back(insertToString(ans[ii], 0));
int len = ans[ii].size();
for(int j = 0; j < len; j++) {
if(ans[ii][j] == '(') {
tmp.push_back(insertToString(ans[ii], j+1));
}
}
}
std::unique(tmp.begin(), tmp.end());
ans.swap(tmp);
}
return ans.reverse();
}
}
string insertToString(string& s, int pos) {
// string tmp(s, 0, pos);
tmp += "()";
for(int i = pos; i < s.size(); i++) {
tmp += s[i];
}
cout << tmp << endl;
return tmp;
}
};
输出的结果是正确的,但是次序有问题,因而过不了。
2
思路二,可以过而且很简洁。参考
http://blog.csdn.net/yutianzuijin/article/details/13161721