【算法设计作业】week3

照例选题写思路

这里写图片描述

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值