leetcode题解(一)

题目:213.House Robber II

题目链接

https://leetcode.com/problems/house-robber-ii/description/

题目描述

你是一个专业的强盗,计划抢劫沿街的房屋。每间房都藏有一定的现金,阻止你抢劫他们的唯一的制约因素就是相邻的房屋有保安系统连接,如果两间相邻的房屋在同一晚上被闯入,它会自动联系警方。

给定一个代表每个房屋的金额的非负整数列表,确定你可以在没有提醒警方的情况下抢劫的最高金额。

在上次盗窃完一条街道之后,窃贼又转到了一个新的地方,这样他就不会引起太多注意。这一次,这个地方的所有房屋都围成一圈。这意味着第一个房子是最后一个是紧挨着的。同时,这些房屋的安全系统与上次那条街道的安全系统保持一致。

给出一份代表每个房屋存放钱数的非负整数列表,确定你可以在不触动警报的情况下盗取的最高金额。

解题思路

这是一道动态规划问题,将房子编号为1~n,设M[i]为盗贼到第i间房子时,盗取的最高金额,设V[i]表示第i间房子的金额。  

盗贼一定会偷尽可能多的房屋,即当他偷到第n-4间房子时,他不会就此罢手,而一定会去偷第n-2、n-1间房子。  

他也不会直接从第n-4间房子直接到第n间房子偷窃,因为这就少偷了第n-2间房子。  

基于上述判断,我们可以断言,当盗贼偷窃第i间房子时,他一定是从第i-2或第i-3间房子过来的,即:  

M[i] = max(M[i-2], M[i-3]) + V[i]  

接下来考虑边界条件:  

如果房子不是围成一圈(即第1间房子和第n间房子不相邻),那么盗贼盗取的最高金额必为max(M[n-1], M[n]),且为了最大收益,盗贼第一次盗取的房子一定是第1间或第2间。  

如果房子围成一圈,我们仍可以用遍历的方式求解,当盗贼盗取了最高金额的前提下,还需要考虑以下情况:  

1、若盗贼最后盗取的房子为第n间,那么他第一次盗取的房子一定不是第1间  

2、若盗贼最后盗取的房子为第n间,那么他第一次盗取的房子可能是第3间  

3、若盗贼第一次盗取的房子是第1间,那么他最后盗取的房子可能是第n-2间  

用M[i][j]表示盗贼第一次盗取的房子为第i间时(1<=i<=3),盗取第j间房子能得到的最大收益,则盗贼盗取的最大金额必为:  

M[2][n], M[3][n], M[1][n-1], M[2][n-1], M[1][n-2]中的最大值  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    int pre(vector arr[],int j,int i){
        if(i-3<0)return arr[j][i-2];
        return max(arr[j][i-2],arr[j][i-3]);
    }
    int rob(vector& nums) {
        if(!nums.size())return 0;
        vector arr[3]={vector(nums.size(),0),vector(nums.size(),0),vector(nums.size(),0)};
        arr[0][0]=nums[0];
        if(nums.size()>1)arr[1][1]=nums[1];
        if(nums.size()>2)arr[2][2]=nums[2];
        for(int i=2;i

题目:120.Triangle

题目链接

https://leetcode.com/problems/triangle/description/

题目描述

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

[
    [2],
    [3,4],
    [6,5,7],
    [4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

说明:

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

解题思路

从上到下,从左到右遍历,每计算完一行后,都将结果保留在一个数组中。  

根据这一行的结果,计算下一行的结果,如此循环,直到找到最后一行结果的最小值。

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    int minimumTotal(vector>& triangle) {
        vector arr(triangle.size(),0),temp(triangle.size(),0);
        int ans=1000000;
        for(int i=0;i

题目:55. Jump Game

题目链接

https://leetcode.com/problems/jump-game/description/

题目描述

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

解题思路

遍历数组,设当前位置为i。  

每一次都计算从位置i能跳到的最远位置,同时记录从位置1~i能跳到的最远位置。  

如果最远位置大于或等于数组长度,说明可以跳到数组的最后一位。  

如果到了当前位置i,但1~i-1能跳到的最远位置小于i,说明无法跳到位置i,即无法跳到数组的最后一位。  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    bool canJump(vector& nums) {
        int maxn = 0;
        for(int i = 0;i=nums.size()-1)return true;
            maxn = max(maxn, i+nums[i]);
        }
        return false;
    }
};

题目:134.Gas Station

题目链接

https://leetcode.com/problems/gas-station/description/

题目描述

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明: 

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。

解题思路

遍历gas,当gas[i]>=cost[i]时,开始模拟汽车的行驶,即每一次i = (i + 1) % gas.size()

如果行驶过程中出现了汽车的油量小于当前位置的耗油量时,退出循环,继续遍历gas  

如果汽车通过行驶能够回到原来的位置,说明可以从第i个加油站出发绕环路行驶一周  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    int canCompleteCircuit(vector& gas, vector& cost) {
        for(int i=0;i=cost[j]){
                gass-=cost[j];
                j=(j+1)%gas.size();
                gass+=gas[j];
                flag++;
            }
            if(j==i){
                return i;
            }
        }
        return -1;
    }
};

题目:148. Sort List

题目链接

https://leetcode.com/problems/sort-list/description/

题目描述

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。  

解题思路

直接对链表进行快速排序即可。  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    void swap(int &a,int &b){
        int temp=a;
        a=b;
        b=temp;
    }
    ListNode* sortListPos(ListNode* begin, ListNode *mid, ListNode *end) {
        if (begin == end || begin->next == end)return NULL;
        int nval = begin->val;
        ListNode *p = begin, *q = begin->next;
        while (q != end) {
            if (q->val < nval) {
                p = p->next;
                swap(p->val, q->val);
            }
            q = q->next;
        }
        swap(p->val, begin->val);
        return p;
    }
    void qsortList(ListNode *begin,ListNode *end){
        ListNode *mid=sortListPos(begin,end);
        if(mid!=NULL){
            qsortList(begin,mid);
            qsortList(mid->next,end);
        }
    }
    ListNode* sortList(ListNode* head) {
        qsortList(head,NULL);
        return head;
    }
};

题目:215. Sort List

题目链接

https://leetcode.com/problems/kth-largest-element-in-an-array/description/

题目描述

在未排序的数组中找到第 k 个最大的元素。请注意,它是数组有序排列后的第 k 个最大元素,而不是第 k 个不同元素。

例如,
给出 [3,2,1,5,6,4] 和 k = 2,返回 5。

注意事项:

你可以假设 k 总是有效的,1 ≤ k ≤ 数组的长度。

解题思路

本来是想用优先队列做的,后来发现题目数据太水,直接用标准库的sort排序后取倒数第k项,竟然比用优先队列更快。  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    int findKthLargest(vector& nums, int k) {
        sort(nums.begin(),nums.end());
        return nums[nums.size()-k];
    }
};

题目:23. Merge k Sorted Lists

题目链接

https://leetcode.com/problems/merge-k-sorted-lists/description/

题目描述

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
    1->4->5,
    1->3->4,
    2->6
]
输出: 1->1->2->3->4->4->5->6  

解题思路

一开始是将合并2个排序链表的思路扩展到k个,即每一次循环都在k个链表里找到结点p,结点p有最小值。

然后让p指向下一个结点,再次循环,直到k个链表对应的结点都指向NULL为止。

后来改成用一次循环将所有链表与第一个链表合并,最后返回第一个链表。  

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();
class Solution {
public:
    ListNode* mergeList(ListNode *pre, ListNode *after){
        ListNode *p=pre,*q=after;
        if(p==NULL)return q;
        if(q==NULL)return p;
        ListNode *res=new ListNode(0);
        ListNode *r=res;
        while(p!=NULL&&q!=NULL){
            if(p->val>q->val){
                r->next=q;
                q=q->next;
            }
            else{
                r->next=p;
                p=p->next;
            }
            r=r->next;
        }
        if(p!=NULL)r->next=p;
        if(q!=NULL)r->next=q;
        return res->next;
    }
    ListNode* mergeKLists(vector& lists) {
        if(lists.size()<1)return NULL;
        if(lists.size()<2)return lists[0];
        for(int i=1;i

题目:4. Median of Two Sorted Arrays

题目链接

https://leetcode.com/problems/median-of-two-sorted-arrays/description/

题目描述

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。

请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。  

解题思路

一开始是将两个数组直接合并,得到新的数组后,返回它的中位数。  

由于leetcode的数据比较水,这样O(m+n)的方法也能过。  

后来看了题解,发现可以用二分的方法将集合划分  

假设A为第一个数组,B为第二个数组,只需要将A和B划分成两个集合。  

A[1] A[2] ... A[i-1] | A[i] A[i+1] ... A[m]
B[1] B[2] ... B[j-1] | B[j] B[j+1] ... B[n]

两个集合满足A[i-1]

代码

int x = [](){
    std::ios::sync_with_stdio(false);
    cin.tie(nullptr);
    return 0;
}();

class Solution {
public:
    double findMedianSortedArrays(vector& nums1, vector& nums2) {
        if(nums1.size()>nums2.size())return findMedianSortedArrays(nums2,nums1);
        int sizea=nums1.size(),sizeb=nums2.size();
        if(sizea==0)return sizeb%2?nums2[sizeb/2]:double(nums2[sizeb/2]+nums2[sizeb/2-1])/2;
        int imin=0,imax=nums1.size(),midsize=(sizea+sizeb+1)/2,i=0,j=0,left=0,right=0;
        while(imin<=imax){
            i=(imin+imax)/2;
            j=midsize-i;
            if(i>0&&nums1[i-1]>nums2[j])imax=i;
            else if(inums1[i])imin=i+1;
            else{
                left=(i==0)?nums2[j-1]:(j==0)?nums1[i-1]:max(nums1[i-1],nums2[j-1]);
                right=(i==sizea)?nums2[j]:(j==sizeb)?nums1[i]:min(nums1[i],nums2[j]);
                return (sizea+sizeb)%2?left:double(left+right)/2;
            }
        }
        return -1;
    }
};

你可能感兴趣的:(leetcode)