【算法设计作业】week3

来源:互联网 发布:linux vi 批量删除 编辑:程序博客网 时间:2024/06/07 04:52

照例选题写思路

这里写图片描述

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

原创粉丝点击