leetcode 解题报告(53-64)

41. First Missing Positive

题目描述: 给定一堆无序的整数,有正有负,找出第一个缺失的正整数,例如:

Example :

Input: [1,2,0]
Output: 3
Example 2:

Input: [3,4,-1,1]
Output: 2
Example 3:

Input: [7,8,9,11,12]
Output: 1

解题思路: 如果给的序列是连续的那必然是最小的或者最大的一个,如果不连续的话,就从1开始找咯,找到那个数没有出现过就行了。时间消耗主要是在确定某个数是不是在序列中,如果用数组妥妥的超时,那么就用哈希表来查找,这样消耗的时间就很少了。

AC代码:

# python
class Solution(object):
    def firstMissingPositive(self, nums):
        if nums == []: # 列表空直接返回 0
            return 1
        dic = {} # 建字典,内部使用的hash算法来存储键的所以可以直接拿来当做哈希表
        for i in nums:
            if i > 0: # 把每个正整数值映射到hash表中去
                dic[i] = 1
        i = 1
        while i < max(nums)+2:
            try:	# 从以开始查询键是否存在,不存在的就是答案,这时候python会抛出一个异常,把他捕获下来再返回结果就行了
                a = dic[i]
            except:
                return i
            i += 1
        return 1

53. Maximum Subarray

题目描述: 找一个数组中,区间和最大的区间。

解题思路: 题目看似简单其实还是要想一下的,用pack来保存当前状态,从第一个数开始往后遍历,如果pack已经是负的了,那么把pack里面的数全部丢掉,只保留这个新的值。否则见到一个数就往pack里面装,这样就有可能使pack的结果变下从而丢失正解,所以再用一个ans来记录pack所经历过的最大值,便是正解。

AC代码:

// c++
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        if(nums.size() == 1) return nums[0];
        long long pack = nums[0];
        long long ans  = pack; // 先放第一个值进去先
        for(int i = 1 ; i < nums.size() ; i ++)
        {
            if(nums[i] + pack < nums[i]) // 更新pack
                pack = nums[i];
            else
                pack += nums[i];
            ans = max(ans,pack); // 记录最大值
        }
        return ans;
    }
};

54. Spiral Matrix

题目描述: 旋转矩阵? 给一个矩阵,然后螺旋走位,把值输出出来。

Example 1:

Input:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
Output: [1,2,3,6,9,8,7,4,5]
Example 2:

Input:
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9,10,11,12]
]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]

解题思路: 总共就四个方向,上下左右,而且每次都是规律变化的也就是 -右-下-左-上-右-下… 先弄一个可以移动的点,然后根据当前的方向,改变坐标值,移动到下一个位置,在这期间判断一下是否会走出矩阵,或者走到之前走到过的地方,如果有,那么改变方向,如果三个方向都没办法走了,也就是走到最中心了,算法结束。

AC代码:

// c++
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector <int> res;
        int m = matrix.size();
        if (m == 0 ) return res; // 空矩阵直接返回
        int n = matrix[0].size(); 
        int i = 0,j = 0,cnt = n*m,to = 0; // 变量to记录方向
        bool vis[1000][1000]; // 标记每个点是否已经走过了
        memset(vis,false,sizeof(vis)); // 初始化vis
        res.push_back(matrix[0][0]); // 起始点先走过去
        vis[0][0] = true;	
        int four_to[4][2] = {0,1,1,0,0,-1,-1,0}; // 四个方向的坐标计算方法
        while(cnt--)
        {
            i += four_to[to][0];
            j += four_to[to][1]; // 计算坐标往前走
            cout<<i<<" "<<j<<endl;
            if(i < 0 || j < 0 || j >= n || i >= m || vis[i][j]) // 判断是否需要改变方向
            {
                i -= four_to[to][0]; // 需要改变方向,先退回去
                j -= four_to[to][1];
                to = (to+1)%4; // 改变方向
                i += four_to[to][0]; // 重新出发
                j += four_to[to][1];
                if(i < 0 || j < 0 || j >= n || i >= m || vis[i][j])   return res;
                // 如果仍然要改变方向,则说明已经走到头了
            }
            res.push_back(matrix[i][j]);
            vis[i][j] = true;
        }
        return res;
    }
};

55. Jump Game

题目描述: 给一个数组,记录从每个位置出发往后可以向后跳跃的最远距离,问,一个给定的数组,能不能从起始点跳到最后一个点。

解题思路: 刚开始想的是dfs直接从起始点往后跳就是,能跳到最后一点就结束算法,返回结果。但是如果给的是可以跳过去的话,算法会比较快结束,如果没法跳过去,那么算法的时间就是巨大的了,已经不是几次方的级别了。看了别人的题解后,恍然大悟。就拿一个值n,记录当前可以到达的最远距离,那么每到一个位置,n要么不变,要么就是array[i]+i,那么当i>n的时候就说明i的位置是无法到达的所以就可以结束算法了。否则等循环结束,说明最后一点就是可到达的。

AC代码:

# python
class Solution(object):
    def canJump(self, nums):
        ans = False
        m = 0 # 记录最远可到达距离
        for i,n in enumerate(nums):
            if i > m :
                return False
            m = max(m,i+n) # 更新 m 的最大值
        return True

56. Merge Intervals

题目描述: 给一堆无序的区间,有一些区间是有交叉的,有交叉的就把他们合并。

Example 1:

Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
Example 2:

Input: [[1,4],[4,5]]
Output: [[1,5]]
Explanation: Intervals [1,4] and [4,5] are considered overlapping.

解题思路: 既然是无序的,那就先排个序,按照每个区间的起始时间由小到大排好序,然后比较每个区间的结束时间和下一个区间的开始时间,如果结束时间大于开始时间,说明是有交叉的,那么合并这两个区间,开始时间就是前一个区间的开始时间,结束时间取两个区间结束时间的最大值。

AC代码:

// c++
class Solution {
public:
    static bool compare(vector<int> a,vector<int> b){
        return a[0] < b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        int len = intervals.size();
        vector<vector<int>> res;
        vector<int> tmp;
        int beg,en;
        sort(intervals.begin(),intervals.end(),compare); // 给区间排序
        for(int i = 0 ; i < len  ;i ++)
        {
            beg = intervals[i][0];
            en = intervals[i][1];
            while(i+1<len && intervals[i+1][0] <= en){ // 判断是否有交叉,有交叉就合并
                en = max(intervals[i+1][1],en);
                i ++;
            }
            tmp.push_back(beg);
            tmp.push_back(en);
            res.push_back(tmp);
            tmp.clear();
        }
        return res;
    }
};

57. Insert Interval

题目描述: 和上一题差不多,一堆区间,不过这次是按起始时间排好序的区间,另外再加一个新的区间,新区间可能与旧区间有交叉。

解题思路: 解题核心逻辑也和上题差不过,本来我直接复制上一题的代码,然后先把新区间push进去,然后直接用上次的代码,就过了,然后发现时间挺慢的,既然是排好序的,那么其实就可以省掉排序的时间了,这也是主要的时间消耗,只要找到新区间对应的插入位置把他插入,然后再合并区间就ok了

AC代码:

// c++
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int flag = 0;
        for(vector<vector<int>> ::iterator pos = intervals.begin(); pos !=intervals.end() ; pos ++ )
        { // 找插入位置,因为是排好序的所以挺方便
            if((*pos)[0] > newInterval[0])
            {
                intervals.insert(pos,newInterval);
                flag = 1;
                break;
            }
        }
        if(!flag) intervals.push_back(newInterval);
        int len = intervals.size();
        vector<vector<int>> res;
        vector<int> tmp;
        int beg,en,i;
        for( i = 0; i < len ;i ++)
        {
            beg = intervals[i][0];
            en = intervals[i][1];
            while(i+1 < len && intervals[i+1][0] <= en){ // 判断是否有交叉,有交叉就合并
                en = max(intervals[i+1][1],en);
                i ++;
            }
            tmp.push_back(beg);
            tmp.push_back(en);
            res.push_back(tmp);
            tmp.clear();
        }
        return res;
    }
};

58. Length of Last Word

题目描述: 一个字符串最后一个单词的长度

解题思路: 水题

AC代码:

# python
class Solution(object):
    def lengthOfLastWord(self, s):
        try:
            s = s.split()[-1]
            return len(s)
        except:
            return 0

59. Spiral Matrix II

题目描述: 再一次旋转矩阵,不过这次不太一样,上次是螺旋走位,然后输出,这次是螺旋走位,然后按顺序填值进去,给定一个n,填满一个n*n的矩阵。

Example:

Input: 3
Output:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

解题思路: 和之前那题差不多的思路,四个方向,判断什么时候改变方向,由于矩阵也是自己建的,所以还省掉了建vis数组的麻烦,先开一个n*n的矩阵全部填成0,那么不是0的地方就是走过的了。

AC代码:

// c++
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res;
        for (int i = 0 ; i < n ; i ++ ) // 初始化n*n的矩阵全部填成0
        {
            vector<int> tmp;
            res.push_back(tmp);
            for (int j = 0 ; j < n ; j ++ )
                res[i].push_back(0);
        }
        int i = 0,j = -1,cnt = 0,to = 0;
        int four_to[4][2] = {0,1,1,0,0,-1,-1,0};
        while(++cnt <= n*n)
        {
            i += four_to[to][0];
            j += four_to[to][1];
            if(i < 0 || j < 0 || j >= n || i >= n || res[i][j] != 0) // 改变方向
            {
                i -= four_to[to][0];
                j -= four_to[to][1];
                to = (to+1)%4;
                i += four_to[to][0];
                j += four_to[to][1];
                if(i < 0 || j < 0 || j >= n || i >= n || res[i][j] != 0)
                    return res;
            }
            res[i][j] = cnt; // 填值
        }
        return res;
    }
};

60. Permutation Sequence

题目描述: 又是全排列,这次要求给一组数,输出所有排列

解题思路: 上次是用递归写的,这次懒得写了,直接调库函数

AC代码:

// c++
class Solution {
public:
    string getPermutation(int n, int k) {
        vector <int> res;
        for(int i = 0; i < n ; i++)
            res.push_back(i+1);
        for(int i = 0; i < k-1; i++)
            next_permutation(res.begin(),res.end());
        string s;
        for(int i = 0; i < n ; i++)
            s += res[i]+'0';
        return s;
    }
};

61. Rotate List

题目描述: 给一个链表和一个k,把从倒数第k个位置开始的表接到头上去。

Example 1:

Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:

Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL

解题思路: 思路也比较简单,弄两个指针,一个先跑k步,然后两个一起跑,等跑得快的那个到达链表末尾了,跑得慢的那个也就到了倒数第k个位置了,然后把这一段整个挪到最前面去。不过这题还是好多坑,要很小心的计算这两个坐标,还有就是k可以走到尾巴,然后再从头开始走,那么k就给的很大,这时候当k第一次到达队尾的时候,长度也就计算出来了,然后k对长度取个模,这样可以省下很多时间。

AC代码:

// c++
class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) {
        ListNode*slow = head,*fast = head,*pre;
        if(head == NULL)
            return NULL;
        if(head->next == NULL)
            return head;
        for(int i = 0 ; i <  k ; i ++)
        {
            fast = fast->next;
            if(fast == NULL)
            {
                fast = head;
                k = (k-i-1)%(i+1);
                cout<<k<<endl;
                i = -1;
            }
        }
        while(fast->next)
        {
            fast = fast->next;
            slow = slow->next;
        }
        fast->next=head;
        head = slow->next;
        slow->next=NULL;
        return head;
    }
};

62. Unique Paths

题目描述: 在一块n*m的板子上,一个机器人想从左上角走到右下角,机器人只能往右或者往下走,每次走一格,问走到右下角有多少种方法。

解题思路: 典型的dp问题,第一行只能有一种方法,第一列也只能有一种方法,那么其他地方,要么从上面走下来,要么从左边坐过来,所以就只需要这两个值加起来。那么答案就出来了。

AC代码:

// c++
class Solution {
public:
    int uniquePaths(int m, int n) {
        long long ans[101][101] = {0};
        for(int i = 0 ; i < n ; i ++) // 初始化第一行
            ans[0][i] = 1;
        for(int i = 0 ; i < m ; i ++) // 初始化第一列
            ans[i][0] = 1;
        for(int i = 1 ; i < m ; i ++)
            for(int j = 1 ; j < n ; j ++)
                ans[i][j] = ans[i-1][j] + ans[i][j-1];
        return ans[m-1][n-1];
    }
};

63. Unique Paths II

题目描述: 上一题的进阶版,还是从左上角走到右下角,但是中间有障碍物,用1来表示,如下:

Example 1:

Input:
[
  [0,0,0],
  [0,1,0],
  [0,0,0]
]
Output: 2
Explanation:
There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

解题思路: 还是dp的思想,有障碍物的地方让他们等于0就行了,然后在初始化第一行和第一列的时候,注意路上有没有障碍物,如果之前的路径有障碍物那么初始化为0。接着就从(1,1)点开始遍历,左边加上边的值,走完最后答案就求出来了。

AC代码:

# python
class Solution(object):
    def uniquePathsWithObstacles(self, obstacleGrid):
        new_grid = [[0 for x in i] for i in obstacleGrid] 初始化 背包,全填0  
        if(obstacleGrid[0][0] == 1): # 如果第一个就是障碍物直接返回0
            return 0
        new_grid[0][0] = 1
        for i in range(1,len(new_grid[0])):# 初始化第一行
            if 1 not in obstacleGrid[0][:i+1] : # 判断路径中是否有障碍物
                new_grid[0][i] = 1
        for i in range(1,len(new_grid)): # 初始化第一列
            if 1 not in [obstacleGrid[j][0] for j in range(i+1)]: # 判断路径中是否有障碍物
                new_grid[i][0] = 1
        for i in range(1,len(new_grid)): # 遍历一遍
            for j in range(1,len(new_grid[0])):
                if (obstacleGrid[i][j] != 1):
                    new_grid[i][j] = new_grid[i-1][j] + new_grid[i][j-1]
        return new_grid[len(new_grid)-1][len(new_grid[0])-1]

64. Minimum Path Sum

题目描述: 给一个矩阵,走过的路径就把值累加起来,还是从左上走到右下,问路径的值加起来最小是多少。

解题思路: 还是一个dp的问题啊,只要把前两题的方法数改成记录路径和就行了。

AC代码:

// c++
class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        for (int i = 1 ; i < grid[0].size() ; i ++) // 初始化第一行
            grid[0][i] = grid[0][i]+grid[0][i-1];
        for (int i = 1 ; i < grid.size() ; i ++)	// 初始化第一列
            grid[i][0] = grid[i][0]+grid[i-1][0];
        for (int i = 1 ; i < grid.size() ; i ++) // dp
            for (int j = 1 ; j < grid[0].size() ; j ++)
                grid[i][j] += min(grid[i-1][j],grid[i][j-1]);
        return grid[grid.size()-1][grid[0].size()-1];
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值